commons-shared-web-ui 0.0.37 → 0.0.39
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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { NgModule, Injectable, inject, Component, Input, HostBinding, SecurityContext, Pipe, ViewChild, EventEmitter, Output, forwardRef,
|
|
2
|
+
import { NgModule, Injectable, inject, Component, Input, HostBinding, SecurityContext, Pipe, HostListener, ViewChild, EventEmitter, Output, forwardRef, Directive, signal, computed, ChangeDetectionStrategy, InjectionToken, ChangeDetectorRef, LOCALE_ID, ViewChildren } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
4
|
import { CommonModule, formatDate } from '@angular/common';
|
|
5
5
|
import * as i1$3 from '@angular/forms';
|
|
@@ -646,6 +646,8 @@ class FormFieldComponent {
|
|
|
646
646
|
value;
|
|
647
647
|
isVisible = true;
|
|
648
648
|
showPassword = false; // password show/hide toggle
|
|
649
|
+
// ── MULTIPLE dropdown state ───────────────────────────────────────────────
|
|
650
|
+
isMultiDropdownOpen = false;
|
|
649
651
|
isDragOver = false; // file upload drag-over state
|
|
650
652
|
fileUploadError = ''; // per-field file validation error
|
|
651
653
|
multiSaveError = ''; // error for multisave validation
|
|
@@ -752,6 +754,22 @@ class FormFieldComponent {
|
|
|
752
754
|
const translatedSectionLabel = sectionLabel ? (this.controller.labels?.[sectionLabel] || sectionLabel) : '';
|
|
753
755
|
return '+ Add a ' + translatedSectionLabel;
|
|
754
756
|
}
|
|
757
|
+
/** Getter for Select placeholder label */
|
|
758
|
+
get selectPlaceholderLabel() {
|
|
759
|
+
return this.controller.labels?.['SELECT_PLACEHOLDER'] || 'Select';
|
|
760
|
+
}
|
|
761
|
+
/** Getter for No options available label */
|
|
762
|
+
get noOptionsAvailableLabel() {
|
|
763
|
+
return this.controller.labels?.['NO_OPTIONS_AVAILABLE'] || 'No options available';
|
|
764
|
+
}
|
|
765
|
+
/** Getter for expand less icon name */
|
|
766
|
+
get expandLessLabel() {
|
|
767
|
+
return this.controller.labels?.['EXPAND_LESS'] || 'expand_less';
|
|
768
|
+
}
|
|
769
|
+
/** Getter for expand more icon name */
|
|
770
|
+
get expandMoreLabel() {
|
|
771
|
+
return this.controller.labels?.['EXPAND_MORE'] || 'expand_more';
|
|
772
|
+
}
|
|
755
773
|
initGroupField() {
|
|
756
774
|
if (this.config.sectionConfig?.allowMulti) {
|
|
757
775
|
this.groupFormArray = this.fb.array([]);
|
|
@@ -1150,9 +1168,18 @@ class FormFieldComponent {
|
|
|
1150
1168
|
const staticQueryParams = acConfig?.queryParams || {};
|
|
1151
1169
|
const customHeaders = acConfig?.headers;
|
|
1152
1170
|
const observables = urls.map(url => {
|
|
1153
|
-
|
|
1171
|
+
let fullUrl = url;
|
|
1154
1172
|
// Merge static queryParams and dynamicParams
|
|
1155
1173
|
const allQueryParams = { ...staticQueryParams, ...dynamicParams };
|
|
1174
|
+
// Interpolate path variables (e.g., {program}) from allQueryParams
|
|
1175
|
+
Object.keys(allQueryParams).forEach(key => {
|
|
1176
|
+
const placeholder = `{${key}}`;
|
|
1177
|
+
if (fullUrl.includes(placeholder)) {
|
|
1178
|
+
fullUrl = fullUrl.replace(placeholder, String(allQueryParams[key]));
|
|
1179
|
+
// Remove it so it doesn't get appended as a query param
|
|
1180
|
+
delete allQueryParams[key];
|
|
1181
|
+
}
|
|
1182
|
+
});
|
|
1156
1183
|
let httpParams = new HttpParams();
|
|
1157
1184
|
Object.entries(allQueryParams).forEach(([key, value]) => {
|
|
1158
1185
|
if (value !== null && value !== undefined) {
|
|
@@ -1345,6 +1372,22 @@ class FormFieldComponent {
|
|
|
1345
1372
|
}
|
|
1346
1373
|
return null;
|
|
1347
1374
|
}
|
|
1375
|
+
// ── MULTIPLE dropdown helpers ─────────────────────────────────────────────
|
|
1376
|
+
toggleMultiDropdown(event) {
|
|
1377
|
+
event.stopPropagation();
|
|
1378
|
+
this.isMultiDropdownOpen = !this.isMultiDropdownOpen;
|
|
1379
|
+
}
|
|
1380
|
+
onDocumentClick() {
|
|
1381
|
+
if (this.isMultiDropdownOpen) {
|
|
1382
|
+
this.isMultiDropdownOpen = false;
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
onEscapeKey() {
|
|
1386
|
+
this.isMultiDropdownOpen = false;
|
|
1387
|
+
}
|
|
1388
|
+
get multiSelectedCount() {
|
|
1389
|
+
return Array.isArray(this.value) ? this.value.length : 0;
|
|
1390
|
+
}
|
|
1348
1391
|
// ── Type guards ──────────────────────────────────────────────────────────
|
|
1349
1392
|
get isTextField() { return this.config.type === 'TEXT_INPUT'; }
|
|
1350
1393
|
get isNumberField() { return this.config.type === 'NUMBER_INPUT'; }
|
|
@@ -2341,11 +2384,11 @@ class FormFieldComponent {
|
|
|
2341
2384
|
}
|
|
2342
2385
|
}
|
|
2343
2386
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldComponent, deps: [{ token: i1$3.FormBuilder }, { token: ExpressionService }, { token: i3.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
|
|
2344
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FormFieldComponent, isStandalone: false, selector: "lib-form-field", inputs: { config: "config", controller: "controller", formGroup: "formGroup", allowMulti: "allowMulti" }, viewQueries: [{ propertyName: "mediaDeviceInput", first: true, predicate: ["mediaDeviceInput"], descendants: true }, { propertyName: "libraryModalRef", first: true, predicate: ["libraryModal"], descendants: true }], ngImport: i0, template: "<div class=\"form-field mb-16px\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\r\n\r\n <!-- \u2550\u2550 ROW Layout \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRow\" class=\"form-row grid-row\">\r\n <ng-container *ngFor=\"let child of config.children\">\r\n <div class=\"row-field\" [style.gridColumn]=\"'span ' + getChildColSpan(child)\" *ngIf=\"child.isEnabled !== false\">\r\n <lib-form-field [config]=\"child\" [controller]=\"controller\" [formGroup]=\"formGroup\" [allowMulti]=\"allowMulti\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- \u2550\u2550 GROUP \u2014 allowMulti (repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig?.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\"\r\n [class.multi-save-active]=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n\r\n <!-- Multi-Save: header row with label + top-right Add button -->\r\n <div class=\"multi-save-header d-flex justify-content-between align-items-center mb-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig?.label\">{{ config.sectionConfig!.label }}</h3>\r\n <lib-button [variant]=\"'outline'\" [icon]=\"{type: 'material', value: 'add'}\" (click)=\"addGroupInstance()\"\r\n class=\"btn-add-multi\">\r\n {{ addMultiLabel }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- Standard heading (non-multiSave) -->\r\n <h3 class=\"group-label\"\r\n *ngIf=\"config.sectionConfig?.label && !config.sectionConfig?.multiSaveConfig?.active\">{{\r\n config.sectionConfig!.label }}</h3>\r\n\r\n <!-- \u2500\u2500 Standard (non-multiSave) repeater: accordion instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <ng-container *ngIf=\"!config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-accordion-instance\"\r\n [class.is-expanded]=\"isGroupExpanded(i)\">\r\n\r\n <!-- Accordion header -->\r\n <div class=\"group-accordion-header\" (click)=\"toggleGroupAccordion(i)\"\r\n role=\"button\" [attr.aria-expanded]=\"isGroupExpanded(i)\">\r\n <div class=\"accordion-header-left d-flex align-items-center gap-10\">\r\n <span class=\"instance-badge\">{{ i + 1 }}</span>\r\n <span class=\"instance-title\">{{ config.sectionConfig!.label }} #{{ i + 1 }}</span>\r\n </div>\r\n <div class=\"accordion-header-right d-flex align-items-center gap-6\">\r\n <button type=\"button\" class=\"accordion-remove-btn\"\r\n *ngIf=\"instanceList.length > 1\"\r\n (click)=\"$event.stopPropagation(); removeGroupInstance(i)\"\r\n aria-label=\"Remove\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n <mat-icon class=\"accordion-chevron\">\r\n {{ isGroupExpanded(i) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <!-- Accordion body (always mounted so form controls survive collapse) -->\r\n <div class=\"group-accordion-body\" [hidden]=\"!isGroupExpanded(i)\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\">\r\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"true\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Full-width dashed Add button -->\r\n <button type=\"button\" class=\"btn-add-group\" (click)=\"addGroupInstance()\">\r\n <mat-icon>add</mat-icon> {{ addLabel }} {{ config.sectionConfig!.label }}\r\n </button>\r\n </ng-container>\r\n\r\n <!-- \u2500\u2500 MultiSave: card instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <ng-container *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-instance position-relative mb-16 overflow-hidden\"\r\n [class.is-editing]=\"instance.isEditing\"\r\n [class.is-card]=\"!instance.isEditing\">\r\n\r\n <!-- Edit / new form view -->\r\n <div [hidden]=\"!instance.isEditing\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\">\r\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"true\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Save / Cancel -->\r\n <div class=\"group-footer d-flex justify-content-end align-items-center gap-16 p-0-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"multiSaveError\">{{ multiSaveError }}</span>\r\n <div class=\"footer-actions d-flex gap-12\">\r\n <lib-button [variant]=\"'outline'\" (click)=\"cancelGroupInstance(i)\">Cancel</lib-button>\r\n <lib-button [variant]=\"'primary'\" (click)=\"saveGroupInstance(i)\">Save</lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Card view (saved state) -->\r\n <ng-container *ngIf=\"!instance.isEditing\">\r\n <div class=\"card-view d-flex justify-content-between align-items-center p-18px-24px\"\r\n [class.is-expanded]=\"instance.isExpanded\">\r\n <div class=\"card-content flex-1 d-flex flex-column gap-4 overflow-hidden\">\r\n <span class=\"card-title font-weight-600 overflow-hidden fs-1rem c-111827\">{{\r\n instance.fg.get(config.sectionConfig!.multiSaveConfig!.summaryField || '')?.value\r\n || '\u2014' }}</span>\r\n </div>\r\n <div class=\"card-actions d-flex align-items-center gap-16 ml-20\">\r\n <mat-icon class=\"icon-delete\" (click)=\"removeGroupInstance(i, true)\">delete_outline</mat-icon>\r\n <mat-icon class=\"icon-edit\" (click)=\"editGroupInstance(i)\">edit_outline</mat-icon>\r\n <mat-icon class=\"icon-expand\" (click)=\"toggleExpandGroupInstance(i)\">\r\n {{ instance.isExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- \u2550\u2550 GROUP \u2014 single (non-repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig && !config.sectionConfig.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig.label\">{{ config.sectionConfig.label }}</h3>\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\" *ngIf=\"field.isEnabled !== false\">\r\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"groupFormGroup\" [allowMulti]=\"false\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n\r\n <!-- \u2550\u2550 Text Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isTextField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <textarea *ngIf=\"config.subType === 'LONG'\" class=\"field-input textarea\" [placeholder]=\"config.placeholder || ''\"\r\n [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\" rows=\"4\">\r\n </textarea>\r\n\r\n <!-- Password input with show/hide toggle -->\r\n <div *ngIf=\"config.subType === 'PASSWORD'\" class=\"password-wrapper position-relative d-flex align-items-center\">\r\n <input [type]=\"showPassword ? 'text' : 'password'\" class=\"field-input password-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <button type=\"button\"\r\n class=\"password-toggle position-absolute cursor-pointer d-flex align-items-center justify-content-center b-none border-none c-6B7280 p-0-25rem\"\r\n (click)=\"showPassword = !showPassword\" tabindex=\"-1\"\r\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\r\n <mat-icon class=\"eye-icon\">{{ showPassword ? 'visibility' : 'visibility_off' }}</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input *ngIf=\"config.subType !== 'LONG' && config.subType !== 'PASSWORD'\"\r\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\" class=\"field-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\"\r\n [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Number Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isNumberField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input type=\"number\" class=\"field-input\" [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\"\r\n [min]=\"config.numberConfig?.min ?? null\" [max]=\"config.numberConfig?.max ?? null\"\r\n [step]=\"config.numberConfig?.step || 1\" [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Date Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isDateField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input matInput [matDatepicker]=\"datePicker\" class=\"field-input date-input has-icon-right\"\r\n [formControlName]=\"config.name!\" [min]=\"config.dateConfig?.minDate\" [max]=\"config.dateConfig?.maxDate\"\r\n [class.is-invalid]=\"errorMessage\" [placeholder]=\"config.placeholder || ''\" [readonly]=\"config.readonly\"\r\n (click)=\"!config.readonly && datePicker.open()\">\r\n <div class=\"date-icon-wrapper position-absolute d-flex align-items-center justify-content-center\"\r\n *ngIf=\"!config.readonly\">\r\n <mat-datepicker-toggle matSuffix [for]=\"datePicker\"></mat-datepicker-toggle>\r\n </div>\r\n <mat-datepicker #datePicker></mat-datepicker>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Time Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isTimeField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input type=\"time\" class=\"field-input time-input\" [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\" [readonly]=\"!!config.readonly\">\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Autocomplete \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isAutocomplete\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Hidden real control (stores the code value) -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <div class=\"autocomplete-wrapper position-relative d-flex align-items-center w-100\"\r\n [class.is-invalid]=\"errorMessage\" [class.readonly]=\"config.readonly\">\r\n <!-- Search icon -->\r\n <mat-icon class=\"ac-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n\r\n <input class=\"field-input ac-input\" [formControl]=\"autocompleteInputCtrl\" [matAutocomplete]=\"auto\"\r\n [placeholder]=\"config.placeholder || 'Search\u2026'\" [readonly]=\"!!config.readonly\" [class.is-invalid]=\"errorMessage\"\r\n (blur)=\"onAutocompleteClear()\" autocomplete=\"off\">\r\n\r\n <!-- Clear button -->\r\n <button type=\"button\"\r\n class=\"ac-clear-btn position-absolute d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none c-9CA3AF p-0-2rem\"\r\n *ngIf=\"autocompleteInputCtrl.value && !config.readonly\"\r\n (click)=\"autocompleteInputCtrl.setValue(''); updateValue(null)\" tabindex=\"-1\" aria-label=\"Clear\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n\r\n <mat-autocomplete #auto=\"matAutocomplete\" [panelWidth]=\"'auto'\">\r\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.label\"\r\n (onSelectionChange)=\"onAutocompleteSelected(option)\">\r\n <span class=\"ac-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Dynamic display fields (image / email / phone / text) -->\r\n <div class=\"ac-display-fields d-flex flex-wrap gap-6 mt-2\" *ngIf=\"option['displayMeta']?.length\">\r\n <ng-container *ngFor=\"let field of option['displayMeta']\">\r\n\r\n <!-- Image avatar -->\r\n <span *ngIf=\"field.type === 'image' && field.value\" class=\"ac-df-item ac-df-image\">\r\n <img [src]=\"field.value\" [alt]=\"field.label || 'image'\" class=\"ac-df-avatar\">\r\n </span>\r\n\r\n <!-- Email -->\r\n <span *ngIf=\"field.type === 'email' && field.value\" class=\"ac-df-item ac-df-chip\">\r\n <mat-icon class=\"ac-df-icon\">mail_outline</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Phone -->\r\n <span *ngIf=\"field.type === 'phone' && field.value\" class=\"ac-df-item ac-df-chip\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\">phone</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Custom / Icon-based / Generic Text -->\r\n <span *ngIf=\"field.type !== 'image' && field.type !== 'email' && field.type !== 'phone' && field.value\" \r\n class=\"ac-df-item\" [class.ac-df-chip]=\"!!field.icon\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\" *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n </ng-container>\r\n </div>\r\n </mat-option>\r\n <mat-option *ngIf=\"filteredOptions.length === 0\" disabled class=\"ac-no-results fs-0-8125rem c-6B7280\">\r\n No results found\r\n </mat-option>\r\n </mat-autocomplete>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Dropdown \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isDropdown\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <select *ngIf=\"config.subType === 'SINGLE'\" class=\"field-input\" [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\">\r\n <option [ngValue]=\"null\" disabled selected>{{ config.placeholder || 'Select' }}</option>\r\n <option *ngFor=\"let option of config.optionConfig?.optionList\" [value]=\"option.code\">\r\n {{ option.label }}\r\n </option>\r\n </select>\r\n\r\n <select *ngIf=\"config.subType === 'MULTIPLE'\" class=\"field-input\" multiple [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\">\r\n <option *ngFor=\"let option of config.optionConfig?.optionList\" [value]=\"option.code\">\r\n {{ option.label }}\r\n </option>\r\n </select>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Radio \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRadio\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"radio-group\" [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\" class=\"radio-label\"\r\n [class.card-item]=\"config.subType === 'CARD'\"\r\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"radio\" [formControlName]=\"config.name!\" [value]=\"option.code\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Checkbox \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label && config.subType === 'LIST'\"\r\n class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\r\n <label class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span>{{ config.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <div *ngIf=\"config.subType === 'LIST' || config.subType === 'CARD'\" class=\"checkbox-group d-flex flex-column gap-8\"\r\n [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\r\n class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\"\r\n [class.card-item]=\"config.subType === 'CARD'\" [class.selected]=\"isChecked(option.code)\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Chip \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isChip\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"chip-group d-flex flex-wrap gap-8\" [class.is-invalid]=\"errorMessage\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\r\n class=\"chip-label cursor-pointer p-6px-14px b-ffffff c-374151 b-1px-solid-D1D5DB br-20px fs-0-875rem\"\r\n [class.selected]=\"isChecked(option.code)\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\" hidden>\r\n <span>{{ option.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Switch \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isSwitch\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label class=\"switch-container d-flex justify-content-between align-items-center cursor-pointer\">\r\n <span class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label }}</span>\r\n <div class=\"switch position-relative\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span class=\"slider position-absolute cursor-pointer\"></span>\r\n </div>\r\n </label>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Rich Text \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRichText\" class=\"field-wrapper component-rich-text d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rich-text-container\" [class.is-invalid]=\"errorMessage\">\r\n <quill-editor [formControlName]=\"config.name!\" class=\"rich-text-editor d-block w-100\"\r\n [placeholder]=\"config.richTextConfig?.placeholder || config.placeholder || ''\"\r\n [styles]=\"{height: config.richTextConfig?.height || '200px'}\">\r\n </quill-editor>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Rating \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRating\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rating-group d-flex gap-4\" [class.is-invalid]=\"errorMessage\">\r\n <span *ngFor=\"let star of getStarArray()\" class=\"star d-inline-flex align-items-center cursor-pointer\"\r\n [class.filled]=\"isStarFilled(star)\" [class.half]=\"isStarHalf(star)\" (click)=\"onRatingChange(star, $event)\">\r\n <mat-icon>{{ isStarFilled(star) || isStarHalf(star) ? 'star' : 'star_border' }}</mat-icon>\r\n </span>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Generated Field (read-only) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isGenerated\" class=\"field-wrapper d-flex flex-column gap-6\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label\r\n }}</label>\r\n <div class=\"generated-value fs-0-875rem p-0-625rem-0-875rem b-F3F4F6 b-1px-solid-E5E7EB br-8px c-6B7280\">{{ value ||\r\n '-' }}</div>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint\">{{ config.hint }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 File Upload \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isFileUpload\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Drop Zone -->\r\n <div\r\n class=\"upload-drop-zone d-flex flex-column align-items-center justify-content-center gap-8 cursor-pointer text-center p-32px-24px us-none\"\r\n [class.drag-over]=\"isDragOver\" [class.has-files]=\"value?.length\" [class.is-invalid]=\"errorMessage\"\r\n (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onFileDrop($event)\"\r\n (click)=\"fileInput.click()\">\r\n\r\n <!-- Icon with accent-colour pill background -->\r\n <div class=\"upload-icon-wrap mb-4\">\r\n <div class=\"dropzone-icon-pill d-flex align-items-center justify-content-center\">\r\n <mat-icon class=\"upload-cloud-icon\">cloud_upload</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <p class=\"upload-main-text font-weight-600 m-0 fs-0-9rem\">Drag & drop files here</p>\r\n <p class=\"upload-sub-text m-0 fs-0-8rem c-64748B\">or <span class=\"upload-link\">Browse files</span></p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"config.attachmentConfig?.acceptLabel\">\r\n Supported: <span class=\"upload-formats font-weight-500\">{{ config.attachmentConfig!.acceptLabel }}</span>\r\n </p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"!config.attachmentConfig?.acceptLabel && config.hint\">\r\n {{ config.hint }}\r\n </p>\r\n <span class=\"upload-size-badge fs-0-72rem\" *ngIf=\"config.attachmentConfig?.maxSizeMB\">\r\n Max {{ config.attachmentConfig!.maxSizeMB }}MB\r\n </span>\r\n\r\n <!-- Hidden native file input -->\r\n <input #fileInput type=\"file\" hidden [attr.multiple]=\"config.attachmentConfig?.multiple ? true : null\"\r\n [attr.accept]=\"config.attachmentConfig?.accept || null\" (change)=\"onFileSelected($event)\">\r\n </div>\r\n\r\n <!-- Uploaded file list -->\r\n <div class=\"uploaded-list d-flex flex-column gap-8 mt-10\" *ngIf=\"value?.length\">\r\n <div *ngFor=\"let f of value; let i = index\"\r\n class=\"uploaded-item d-flex align-items-center gap-10 p-10px-14px br-8px\"\r\n [class.uploading]=\"f.isUploading\">\r\n\r\n <!-- Uploading spinner -->\r\n <ng-container *ngIf=\"f.isUploading; else fileReady\">\r\n <div class=\"upload-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size uploading-label fs-0-72rem\">Uploading...</span>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Normal state once upload is done -->\r\n <ng-template #fileReady>\r\n <mat-icon class=\"file-type-icon\">{{ getFileIcon(f.type) }}</mat-icon>\r\n <img *ngIf=\"f.type?.startsWith('image') && f.dataUrl\" [src]=\"f.dataUrl\" class=\"file-thumb rounded-4\"\r\n alt=\"preview\">\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size fs-0-72rem\">{{ formatFileSize(f.size) }}</span>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Compact icon-only remove button -->\r\n <button type=\"button\" class=\"file-remove-btn d-flex align-items-center justify-content-center rounded-50\"\r\n [disabled]=\"f.isUploading\" (click)=\"!f.isUploading && removeUploadedFile(i)\"\r\n aria-label=\"Remove file\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Validation / file errors -->\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"fileUploadError\">{{ fileUploadError }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage && !fileUploadError\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\"\r\n *ngIf=\"config.hint && !errorMessage && !fileUploadError && !config.attachmentConfig?.acceptLabel\">\r\n {{ config.hint }}\r\n </span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Media Upload (Type 2) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isMediaUpload\" class=\"field-wrapper media-upload-wrapper d-flex flex-column gap-6 p-0 border-none b-none\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Hidden file input lives outside *ngIf \u2014 triggered via ViewChild -->\r\n <input #mediaDeviceInput type=\"file\" hidden multiple accept=\"image/*\" (change)=\"onMediaFileSelected($event)\">\r\n\r\n <!-- Two-column layout -->\r\n <div class=\"mu-layout d-grid align-items-start\">\r\n\r\n <!-- \u2500\u2500 LEFT PANEL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"mu-left d-flex flex-column gap-20\">\r\n\r\n <!-- Header: title + max-items badge -->\r\n <div class=\"mu-header d-flex align-items-start flex-wrap gap-10\">\r\n <h3 class=\"mu-title m-0 c-111827 fs-1-35rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </h3>\r\n <span\r\n class=\"mu-badge d-inline-flex align-items-center rounded-20 color-white font-weight-600 fs-0-72rem p-4px-12px b-111827\"\r\n *ngIf=\"config.attachmentConfig?.maxFiles\">\r\n {{ controller.labels['LBL_MEDIA_MAX_PREFIX'] || 'Maximum' }}\r\n {{ config.attachmentConfig?.maxFiles }}\r\n {{ controller.labels['LBL_MEDIA_MAX_SUFFIX'] || 'Image & Videos' }}\r\n </span>\r\n </div>\r\n\r\n <!-- Description -->\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\" *ngIf=\"config.attachmentConfig?.description\">\r\n {{ config.attachmentConfig?.description }}\r\n </p>\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\"\r\n *ngIf=\"!config.attachmentConfig?.description && controller.labels['LBL_MEDIA_DESC']\">\r\n {{ controller.labels['LBL_MEDIA_DESC'] }}\r\n </p>\r\n\r\n <!-- Feature bullet list -->\r\n <ul class=\"mu-features m-0 p-0 d-flex flex-column gap-8 ls-none\"\r\n *ngIf=\"config.attachmentConfig?.features?.length || controller.labels['LBL_MEDIA_FEATURE_1']\">\r\n <ng-container *ngIf=\"config.attachmentConfig?.features?.length\">\r\n <li *ngFor=\"let f of config.attachmentConfig?.features\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ f }}\r\n </li>\r\n </ng-container>\r\n <ng-container *ngIf=\"!config.attachmentConfig?.features?.length\">\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_1']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_1'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_2']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_2'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_3']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_3'] }}\r\n </li>\r\n </ng-container>\r\n </ul>\r\n\r\n <!-- Backdrop to close dropdown on outside click -->\r\n <div class=\"media-menu-backdrop\" *ngIf=\"showMediaMenu\"\r\n (click)=\"$event.stopPropagation(); showMediaMenu = false\"></div>\r\n\r\n <!-- Add Media button + dropdown -->\r\n <div class=\"media-add-container position-relative\" (click)=\"showMediaMenu = !showMediaMenu\">\r\n <lib-button id=\"btn-add-media-{{ config.name }}\" [variant]=\"'warning'\"\r\n [icon]=\"{type: 'material', value: 'add_photo_alternate'}\">\r\n {{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}\r\n <mat-icon class=\"menu-chevron fs-18px\">add</mat-icon>\r\n </lib-button>\r\n\r\n <div class=\"media-dropdown position-absolute rounded-12 overflow-hidden b-ffffff b-1px-solid-E5E7EB\"\r\n *ngIf=\"showMediaMenu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\r\n <!-- Video -->\r\n <button id=\"btn-media-video-{{ config.name }}\" type=\"button\"\r\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\r\n (click)=\"onMediaMenuVideo(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\n class=\"media-drop-icon media-drop-icon--video d-flex align-items-center justify-content-center rounded-8\"><mat-icon>videocam</mat-icon></span>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_VIDEO'] || 'Video' }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_VIDEO_DESC'] || 'Add\r\n YouTube URL'\r\n }}</span>\r\n </span>\r\n </button>\r\n <!-- Device -->\r\n <button id=\"btn-media-device-{{ config.name }}\" type=\"button\"\r\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\r\n (click)=\"onMediaMenuDevice(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\n class=\"media-drop-icon media-drop-icon--device d-flex align-items-center justify-content-center rounded-8\"><mat-icon>upload</mat-icon></span>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_DEVICE'] || 'Upload from device'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_DEVICE_DESC'] ||\r\n 'Select images from your\r\n computer' }}</span>\r\n </span>\r\n </button>\r\n <!-- Library -->\r\n <button id=\"btn-media-library-{{ config.name }}\" type=\"button\"\r\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\r\n (click)=\"onMediaMenuLibrary(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\n class=\"media-drop-icon media-drop-icon--library d-flex align-items-center justify-content-center rounded-8\"><mat-icon>photo_library</mat-icon></span>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_LIBRARY'] || 'Upload from library'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_LIBRARY_DESC'] ||\r\n 'Choose from default\r\n images' }}</span>\r\n </span>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- YouTube URL Input (inline below button) -->\r\n <div class=\"youtube-input-panel d-flex flex-column gap-8 p-16 rounded-10 b-FFFAF1 b-1px-solid-E5E7EB\"\r\n *ngIf=\"showYoutubeInput\">\r\n <label class=\"youtube-panel-label d-flex align-items-center gap-6 font-weight-600 fs-0-875rem c-111827\">\r\n {{ controller.labels['LBL_YOUTUBE_URL'] || 'Video URL' }}\r\n </label>\r\n <div class=\"youtube-input-row d-flex gap-8\">\r\n <input id=\"input-youtube-url-{{ config.name }}\" type=\"url\" class=\"field-input youtube-url-input\"\r\n [(ngModel)]=\"youtubeUrlInput\" [ngModelOptions]=\"{standalone: true}\"\r\n [placeholder]=\"controller.labels['PH_YOUTUBE_URL'] || 'Video URL'\" (keyup.enter)=\"addYoutubeMedia()\">\r\n <lib-button id=\"btn-add-youtube-{{ config.name }}\" [variant]=\"'secondary'\" (click)=\"addYoutubeMedia()\">\r\n {{ controller.labels['LBL_ADD'] || 'Add' }}\r\n </lib-button>\r\n </div>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"youtubeUrlError\">{{ youtubeUrlError }}</span>\r\n </div>\r\n\r\n <div\r\n class=\"media-upload-status d-flex align-items-center gap-8 mt-4 color-error rounded-8 font-weight-500 p-10px-14px b-FEF2F2 fs-0-85rem\"\r\n *ngIf=\"mediaUploadError\">\r\n <mat-icon class=\"status-icon fs-18px\">error_outline</mat-icon>\r\n <span>{{ mediaUploadError }}</span>\r\n </div>\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n </div>\r\n <!-- end left panel -->\r\n\r\n <!-- \u2500\u2500 RIGHT PANEL (carousel) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"mu-right d-flex flex-column gap-12\">\r\n\r\n <!-- Carousel (when items exist) -->\r\n <div class=\"media-carousel-section d-flex flex-column gap-12\" *ngIf=\"mediaItems.length\">\r\n <div\r\n class=\"media-carousel-main position-relative w-100 overflow-hidden d-flex align-items-center justify-content-center br-12px b-0F172A\">\r\n <button id=\"btn-carousel-prev-{{ config.name }}\" type=\"button\"\r\n class=\"carousel-nav carousel-nav--prev position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\r\n (click)=\"mediaCarouselPrev()\" [disabled]=\"mediaCarouselIndex === 0\" aria-label=\"Previous\">\r\n <mat-icon>chevron_left</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-viewer position-absolute d-flex align-items-center justify-content-center\"\r\n *ngFor=\"let item of mediaItems; let i = index\" [hidden]=\"i !== mediaCarouselIndex\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"carousel-uploading d-flex flex-column align-items-center gap-12 c-94A3B8 fs-0-85rem\">\r\n <div class=\"carousel-spinner rounded-50 b-3px-solid-rgba-255-255-255-0-15\"></div>\r\n <span>{{ controller.labels['LBL_UPLOADING'] || 'Uploading\u2026' }}</span>\r\n </div>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'youtube'\">\r\n <iframe class=\"carousel-iframe w-100 h-100 br-12px\" [src]=\"item.url | trustedUrl\" frameborder=\"0\"\r\n allowfullscreen\r\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\">\r\n </iframe>\r\n </ng-container>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'image'\">\r\n <img class=\"carousel-image w-100 h-100 br-12px\" [src]=\"item.url\" alt=\"Media\">\r\n </ng-container>\r\n <button id=\"btn-remove-media-{{ config.name }}-{{ i }}\" type=\"button\"\r\n class=\"carousel-remove-btn position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-0-0-0-0-55\"\r\n [disabled]=\"item.isUploading\" (click)=\"removeMediaItem(i)\" aria-label=\"Remove\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <button id=\"btn-carousel-next-{{ config.name }}\" type=\"button\"\r\n class=\"carousel-nav carousel-nav--next position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\r\n (click)=\"mediaCarouselNext()\" [disabled]=\"mediaCarouselIndex === mediaItems.length - 1\" aria-label=\"Next\">\r\n <mat-icon>chevron_right</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-dots position-absolute d-flex gap-6\">\r\n <span *ngFor=\"let item of mediaItems; let i = index\"\r\n class=\"carousel-dot rounded-50 cursor-pointer b-rgba-255-255-255-0-45\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\"></span>\r\n </div>\r\n </div>\r\n\r\n <!-- Thumbnail strip -->\r\n <div class=\"media-thumbnail-strip d-flex flex-wrap gap-8 pb-4px\">\r\n <div *ngFor=\"let item of mediaThumbnails; let i = index\"\r\n class=\"media-thumb rounded-8 overflow-hidden cursor-pointer d-flex align-items-center justify-content-center b-2px-solid-transparent b-E2E8F0\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"thumb-uploading d-flex align-items-center justify-content-center w-100 h-100\">\r\n <div class=\"thumb-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && item.thumbnailUrl\"\r\n [src]=\"item.thumbnailUrl\" class=\"thumb-img w-100 h-100\" alt=\"Video thumbnail\">\r\n <div *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && !item.thumbnailUrl\"\r\n class=\"thumb-yt-placeholder d-flex align-items-center justify-content-center w-100 h-100 b-1E293B c-EF4444\">\r\n <mat-icon>play_circle</mat-icon>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'image' && item.url\" [src]=\"item.url\"\r\n class=\"thumb-img w-100 h-100\" alt=\"Image thumbnail\">\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty right-side placeholder -->\r\n <div\r\n class=\"mu-right-empty d-flex flex-column align-items-center justify-content-center gap-10 h-100 text-center p-24 br-12px b-FFFAF1 c-94A3B8 b-2px-dashed-CBD5E1\"\r\n *ngIf=\"!mediaItems.length\" (click)=\"onMediaMenuDevice()\">\r\n <mat-icon class=\"mu-right-empty-icon fs-52px\">perm_media</mat-icon>\r\n <p>{{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}</p>\r\n </div>\r\n\r\n </div>\r\n <!-- end right panel -->\r\n\r\n </div><!-- end mu-layout -->\r\n </div>\r\n\r\n\r\n <!-- \u2550\u2550 Library Image Picker Modal \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <!-- Wrapper is always in DOM (hidden) so @ViewChild can move it to body -->\r\n <div #libraryModal class=\"media-library-portal-host\" [class.is-open]=\"showLibraryModal\">\r\n\r\n <!-- Backdrop -->\r\n <div class=\"media-library-overlay\" (click)=\"closeLibraryModal()\"></div>\r\n\r\n <!-- Modal card -->\r\n <div class=\"media-library-modal d-flex flex-column overflow-hidden b-ffffff br-16px\"\r\n role=\"dialog\" aria-modal=\"true\">\r\n <div class=\"library-modal-header d-flex align-items-start justify-content-between p-24px-28px bb-1px-solid-E5E7EB\">\r\n <div class=\"header-left d-flex flex-column gap-8\">\r\n <h3 class=\"library-modal-title m-0 color-dark fs-1-25rem\">\r\n {{ controller.labels['LBL_ADD_IMAGES'] || 'Add Images' }}\r\n </h3>\r\n <p class=\"library-modal-subtitle m-0 color-gray fs-0-85rem\">\r\n {{ controller.labels['LBL_LIBRARY_MODAL_DESC'] || 'Select images from your library.' }}\r\n </p>\r\n </div>\r\n <button id=\"btn-close-library-{{ config.name }}\" type=\"button\"\r\n class=\"library-close-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 border-none b-none c-9CA3AF\"\r\n (click)=\"closeLibraryModal()\" aria-label=\"Close\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Loading -->\r\n <div class=\"library-loading\" *ngIf=\"libraryLoading\">\r\n <div class=\"lib-spinner rounded-50 b-3px-solid-E2E8F0\"></div>\r\n <span>{{ controller.labels['LBL_LOADING'] || 'Loading\u2026' }}</span>\r\n </div>\r\n\r\n <!-- Error -->\r\n <div class=\"library-error d-flex align-items-center gap-8 color-error b-FEF2F2 fs-0-875rem p-16px-24px\"\r\n *ngIf=\"libraryError && !libraryLoading\">\r\n <mat-icon>error_outline</mat-icon>\r\n {{ libraryError }}\r\n </div>\r\n\r\n <!-- Image grid -->\r\n <div class=\"library-grid d-grid gap-16 flex-1 p-28px b-F9FAFB\" *ngIf=\"!libraryLoading && libraryImages.length\">\r\n <div *ngFor=\"let img of libraryImages; let li = index\" id=\"lib-img-{{ config.name }}-{{ li }}\"\r\n class=\"library-grid-item position-relative rounded-12 overflow-hidden cursor-pointer bg-white b-3px-solid-transparent\"\r\n [class.selected]=\"isLibraryItemSelected(img)\" (click)=\"toggleLibraryItem(img)\">\r\n <img [src]=\"getLibraryItemUrl(img)\" class=\"library-grid-img w-100 h-100 d-block\" alt=\"Library image\">\r\n <div\r\n class=\"library-check position-absolute bg-white rounded-50 d-flex align-items-center justify-content-center c-3B82F6\"\r\n *ngIf=\"isLibraryItemSelected(img)\">\r\n <mat-icon>check_circle</mat-icon>\r\n </div>\r\n <div class=\"library-overlay-hover position-absolute b-rgba-59-130-246-0-12\"></div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty library -->\r\n <div\r\n class=\"library-empty d-flex flex-column align-items-center justify-content-center gap-12 flex-1 c-9CA3AF fs-0-875rem p-48px-24px\"\r\n *ngIf=\"!libraryLoading && !libraryError && libraryImages.length === 0\">\r\n <mat-icon>image_not_supported</mat-icon>\r\n <span>{{ controller.labels['LBL_LIBRARY_EMPTY'] || 'No images found in library.' }}</span>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"library-modal-footer d-flex align-items-center justify-content-end bg-white p-20px-28px bt-1px-solid-E5E7EB\">\r\n <div class=\"library-footer-actions d-flex gap-12\">\r\n <lib-button id=\"btn-library-cancel-{{ config.name }}\" [variant]=\"'outline'\" (click)=\"closeLibraryModal()\">\r\n {{ controller.labels['LBL_CANCEL'] || 'Cancel' }}\r\n </lib-button>\r\n <lib-button id=\"btn-library-confirm-{{ config.name }}\" [variant]=\"'primary'\"\r\n [disabled]=\"librarySelectedIds.size === 0\" (click)=\"confirmLibrarySelection()\">\r\n {{ controller.labels['LBL_CONTINUE'] || 'Continue' }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n\r\n\r\n <!-- \u2550\u2550 Location Field \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isLocation\" class=\"field-wrapper location-field-wrapper d-flex flex-column gap-6 gap-12\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Field label -->\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n <p class=\"location-subtitle m-0 c-6B7280 fs-0-8125rem\" *ngIf=\"config.hint\">{{ config.hint }}</p>\r\n\r\n <!-- Three-tab bar -->\r\n <div class=\"location-tabs d-flex gap-12 mb-24\">\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'VENUE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('VENUE')\">\r\n {{ controller.labels['LBL_LOC_VENUE'] || 'Venue' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'ONLINE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('ONLINE')\">\r\n {{ controller.labels['LBL_LOC_ONLINE'] || 'Online Event' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'TBA' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('TBA')\">\r\n {{ controller.labels['LBL_LOC_TBA'] || 'To be Announced' }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- VENUE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'VENUE'\" class=\"loc-panel loc-venue-panel d-flex flex-column gap-12\">\r\n\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ADDRESS'] || 'Location address' }}\r\n </p>\r\n\r\n <!-- Added venue rows -->\r\n <div class=\"loc-venue-list d-flex flex-column gap-8\" *ngIf=\"locationVenues.length > 0\">\r\n <div *ngFor=\"let venue of locationVenues; let i = index\"\r\n class=\"loc-venue-item d-flex align-items-center gap-10 p-10px-14px br-7px b-ffffff b-1px-solid-D1D5DB\">\r\n <mat-icon class=\"loc-venue-search-icon fs-18px c-9CA3AF\">search</mat-icon>\r\n <span class=\"loc-venue-text flex-1 overflow-hidden fs-0-875rem c-111827\">{{ venue.address || venue.name ||\r\n venue.description }}</span>\r\n <button type=\"button\"\r\n class=\"loc-action-btn loc-delete-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none p-4px\"\r\n (click)=\"removeLocationVenue(i)\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Location count badge -->\r\n <p class=\"loc-count-text m-0 font-weight-600 fs-0-8125rem c-3B82F6\"\r\n *ngIf=\"locationVenues.length > 0 && config.locationConfig?.allowMulti\">\r\n {{ locationVenues.length }} {{ controller.labels['LBL_LOC_COUNT_SUFFIX'] || 'Locations Added!' }}\r\n </p>\r\n\r\n <!-- Search input (hide when max reached) -->\r\n <div class=\"loc-search-container position-relative\" *ngIf=\"!locationMaxReached\">\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n <input\r\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\r\n [placeholder]=\"config.locationConfig?.venuePlaceholder || (controller.labels['PH_LOC_VENUE'] || 'Type to search venue...')\"\r\n [value]=\"locationSearchText\" (input)=\"handleLocationSearchInput($event)\" (blur)=\"hideLocationSuggestions()\"\r\n autocomplete=\"off\" [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n <!-- Suggestions dropdown -->\r\n <div class=\"loc-suggestions-panel position-absolute overflow-hidden br-8px b-ffffff b-1px-solid-D1D5DB\"\r\n *ngIf=\"locationShowSuggestions && locationSuggestions.length\">\r\n <div *ngFor=\"let sug of locationSuggestions\"\r\n class=\"loc-suggestion-item d-flex align-items-center gap-10 cursor-pointer p-10px-14px\"\r\n (mousedown)=\"onLocationSuggestionSelect(sug)\">\r\n <mat-icon class=\"loc-suggestion-icon fs-18px c-E53E3E\">place</mat-icon>\r\n <span class=\"loc-suggestion-text overflow-hidden fs-0-875rem c-374151\">{{ sug.description }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Add another button -->\r\n <button type=\"button\"\r\n class=\"loc-add-btn d-inline-flex align-items-center gap-6 cursor-pointer font-weight-600 p-0 b-none border-none fs-0-875rem c-1A56DB\"\r\n *ngIf=\"locationVenues.length > 0 && !locationMaxReached && config.locationConfig?.allowMulti\">\r\n <mat-icon>add_circle_outline</mat-icon>\r\n <span>{{ controller.labels['LBL_LOC_ADD_ANOTHER'] || 'Add another Location' }}</span>\r\n </button>\r\n\r\n <!-- Map -->\r\n <div class=\"loc-map-container overflow-hidden br-10px b-1px-solid-E5E7EB\"\r\n *ngIf=\"config.locationConfig?.showMap !== false\">\r\n <ng-container *ngIf=\"config.locationConfig?.googleMapsApiKey; else simpleEmbed\">\r\n <div [id]=\"'loc-map-' + config.name\" class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\"></div>\r\n </ng-container>\r\n <ng-template #simpleEmbed>\r\n <iframe class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\" [src]=\"getLocationMapEmbedUrl() | trustedUrl\"\r\n frameborder=\"0\" allowfullscreen loading=\"lazy\">\r\n </iframe>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- Map hint -->\r\n <p class=\"loc-map-hint m-0 text-center fs-0-78rem c-6B7280\">\r\n {{ controller.labels['LBL_LOC_MAP_HINT'] || 'Type the venue address. A map will appear to assist you.' }}\r\n </p>\r\n </div>\r\n\r\n <!-- ONLINE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'ONLINE'\" class=\"loc-panel loc-online-panel d-flex flex-column gap-12\">\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ONLINE_URL'] || 'Event URL' }}\r\n </p>\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">link</mat-icon>\r\n <input\r\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\r\n type=\"url\"\r\n [placeholder]=\"config.locationConfig?.onlinePlaceholder || (controller.labels['PH_LOC_ONLINE'] || 'https://zoom.us/j/...')\"\r\n [value]=\"locationOnlineUrl\" (input)=\"onLocationUrlChange($any($event.target).value)\"\r\n [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n </div>\r\n\r\n <!-- TBA TAB -->\r\n <div *ngIf=\"locationActiveTab === 'TBA'\"\r\n class=\"loc-panel loc-tba-panel d-flex flex-column gap-12 justify-content-center\">\r\n <div\r\n class=\"loc-tba-content d-flex flex-column align-items-center justify-content-center text-center gap-12 p-32px-24px b-F9FAFB b-1px-dashed-D1D5DB br-10px\">\r\n <mat-icon class=\"loc-tba-icon fs-40px c-9CA3AF\">schedule</mat-icon>\r\n <p class=\"loc-tba-text m-0 c-6B7280 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_TBA_DESC'] || \"This event's location is yet to be announced. Check back later\r\n for updates.\" }}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <!-- Hidden real form control -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n</div>", styles: [".d-flex{display:flex}.d-inline-flex{display:inline-flex}.d-grid{display:grid}.d-block{display:block}.d-none{display:none}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.align-items-center{align-items:center}.align-items-start{align-items:flex-start}.align-items-end{align-items:flex-end}.justify-content-center{justify-content:center}.justify-content-between{justify-content:space-between}.justify-content-start{justify-content:flex-start}.justify-content-end{justify-content:flex-end}.grid-cols-12{grid-template-columns:repeat(12,1fr)}.w-100{width:100%}.h-100{height:100%}.position-relative{position:relative}.position-absolute{position:absolute}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-poppins{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)}.font-weight-400{font-weight:400}.font-weight-500{font-weight:500}.font-weight-600{font-weight:600}.text-13{font-size:13px}.text-14{font-size:14px}.text-16{font-size:16px}.color-white{color:#fff}.color-dark{color:#111827}.color-gray{color:#6b7280}.color-error{color:var(--cc-sf-error-text-color, #DC2626)}.bg-white{background-color:#fff}.bg-transparent{background-color:transparent}.m-0{margin:0}.mt-4{margin-top:4px}.mt-8{margin-top:8px}.mt-10{margin-top:10px}.mt-12{margin-top:12px}.mt-16{margin-top:16px}.mt-20{margin-top:20px}.mt-24{margin-top:24px}.mb-0{margin-bottom:0}.mb-4{margin-bottom:4px}.mb-8{margin-bottom:8px}.mb-10{margin-bottom:10px}.mb-12{margin-bottom:12px}.mb-16{margin-bottom:16px}.mb-20{margin-bottom:20px}.mb-24{margin-bottom:24px}.ml-16{margin-left:16px}.ml-20{margin-left:20px}.p-0{padding:0}.p-16{padding:16px}.p-20{padding:20px}.p-24{padding:24px}.pt-20{padding-top:20px}.pb-10{padding-bottom:10px}.gap-4{gap:4px}.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-16{gap:16px}.gap-20{gap:20px}.rounded-4{border-radius:4px}.rounded-6{border-radius:6px}.rounded-8{border-radius:8px}.rounded-10{border-radius:10px}.rounded-12{border-radius:12px}.rounded-20{border-radius:20px}.rounded-24{border-radius:24px}.rounded-50{border-radius:50%}.cursor-pointer{cursor:pointer}.overflow-hidden{overflow:hidden}.resize-vertical{resize:vertical}.box-sizing-border{box-sizing:border-box}.border-none{border:none!important}.mb-16px{margin-bottom:var(--cc-sf-grid-gap, 16px)!important}.c-DC2626{color:var(--cc-sf-label-required-color, #DC2626)!important}.ml-0-125rem{margin-left:.125rem!important}.fs-0-875rem{font-size:.875rem!important}.c-111827{color:var(--cc-sf-label-color, #111827)!important}.br-7px{border-radius:var(--cc-sf-input-radius, 7px)!important}.c-6B7280{color:var(--cc-sf-hint-color, #6B7280)!important}.fs-0-75rem{font-size:var(--cc-sf-error-text-size, .75rem)!important}.b-none{background:none!important}.p-32px-24px{padding:32px 24px!important}.us-none{-webkit-user-select:none!important;user-select:none!important}.c-1E293B{color:var(--cc-sf-label-color, #1E293B)!important}.c-3B82F6{color:var(--cc-sf-chip-selected-bg, #3B82F6)!important}.fs-0-78rem{font-size:.78rem!important}.p-10px-14px{padding:10px 14px!important}.fs-0-85rem{font-size:.85rem!important}.fs-0-72rem{font-size:.72rem!important}.c-94A3B8{color:#94a3b8!important}.p-4px{padding:4px!important}.br-8px{border-radius:var(--cc-sf-input-radius, 8px)!important}.bc-F3F4F6{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.br-none{border-right:none!important}.bl-none{border-left:none!important}.pe-none{pointer-events:none!important}.fs-1-1rem{font-size:1.1rem!important}.c-9CA3AF{color:var(--cc-sf-hint-color, #9CA3AF)!important}.pl-2-4rem{padding-left:2.4rem!important}.fs-0-8125rem{font-size:.8125rem!important}.ls-none{list-style:none!important}.br-12px{border-radius:var(--mu-carousel-radius, 12px)!important}.b-FFFAF1{background:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.fs-18px{font-size:18px!important}.b-FEF2F2{background:var(--cc-sf-error-bg, #FEF2F2)!important}.bc-DC2626{border-color:var(--cc-sf-error-border, #DC2626)!important}.c-202124{color:var(--cc-sf-label-color, #202124)!important}.fs-18px{font-size:var(--cc-sf-label-size, 18px)!important}.mb-0-5rem{margin-bottom:.5rem!important}.pt-0-625rem{padding-top:var(--cc-sf-input-padding-y, .625rem)!important}.pb-0-625rem{padding-bottom:var(--cc-sf-input-padding-y, .625rem)!important}.pl-16px{padding-left:var(--cc-sf-input-padding-x, 16px)!important}.pr-16px{padding-right:var(--cc-sf-input-padding-x, 16px)!important}.bc-ffffff{background-color:var(--cc-sf-section-bg, #ffffff)!important}.b-1px-solid-D1D5DB{border:1px solid var(--cc-sf-input-border, #D1D5DB)!important}.fs-0-75rem{font-size:.75rem!important}.c-1F2937{color:var(--cc-sf-section-label-color, #1F2937)!important}.p-6px-14px{padding:var(--cc-sf-chip-padding, 6px 14px)!important}.b-ffffff{background:var(--loc-suggestion-bg, #ffffff)!important}.c-374151{color:var(--cc-sf-label-color, #374151)!important}.br-20px{border-radius:var(--cc-sf-chip-radius, 20px)!important}.fs-0-875rem{font-size:var(--cc-sf-btn-font-size, .875rem)!important}.bc-D1D5DB{background-color:var(--cc-sf-switch-track-off, #D1D5DB)!important}.pr-2-75rem{padding-right:2.75rem!important}.p-0-25rem{padding:.25rem!important}.p-0-625rem-0-875rem{padding:var(--cc-sf-generated-padding, .625rem .875rem)!important}.b-F3F4F6{background:var(--cc-sf-generated-bg, #F3F4F6)!important}.b-1px-solid-E5E7EB{border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.br-8px{border-radius:var(--cc-sf-uploaded-item-radius, 8px)!important}.c-6B7280{color:var(--ms-desc-color, #6B7280)!important}.mb-20px{margin-bottom:var(--cc-sf-section-gap, 20px)!important}.br-10px{border-radius:var(--cc-sf-input-radius, 10px)!important}.p-20px{padding:var(--cc-sf-section-padding, 20px)!important}.fs-1rem{font-size:1rem!important}.m-0-0-16px-0{margin:0 0 16px!important}.bb-2px-solid-E5E7EB{border-bottom:var(--cc-sf-section-label-border, 2px solid #E5E7EB)!important}.p-16px{padding:var(--cc-sf-instance-padding, 16px)!important}.b-F9FAFB{background:var(--loc-tba-bg, #F9FAFB)!important}.bb-1px-dashed-D1D5DB{border-bottom:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)!important}.c-4B5563{color:var(--cc-sf-instance-num-color, #4B5563)!important}.fs-0-8125rem{font-size:var(--cc-sf-hint-size, .8125rem)!important}.pb-0{padding-bottom:0!important}.p-18px-24px{padding:18px 24px!important}.c-111827{color:var(--ms-title-color, #111827)!important}.bt-1px-solid-E5E7EB{border-top:1px solid #E5E7EB!important}.p-4px-10px{padding:4px 10px!important}.b-FFF5F5{background:var(--cc-sf-btn-remove-bg, #FFF5F5)!important}.c-E53E3E{color:var(--loc-delete-color, #E53E3E)!important}.b-1px-solid-FED7D7{border:var(--cc-sf-btn-remove-border, 1px solid #FED7D7)!important}.br-4px{border-radius:var(--cc-sf-btn-remove-radius, 4px)!important}.p-8px-16px{padding:8px 16px!important}.b-transparent{background:var(--cc-sf-btn-add-bg, transparent)!important}.c-3B82F6{color:var(--cc-sf-input-focus-border, #3B82F6)!important}.b-1px-dashed-CBD5E1{border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1)!important}.br-6px{border-radius:var(--cc-sf-btn-add-radius, 6px)!important}.b-1-5px-dashed-CBD5E1{border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1)!important}.br-12px{border-radius:var(--cc-sf-dropzone-radius, 12px)!important}.bc-FFFAF1{background-color:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.c-94A3B8{color:var(--cc-sf-uploaded-remove-color, #94A3B8)!important}.fs-0-9rem{font-size:var(--cc-sf-input-font-size, .9rem)!important}.c-64748B{color:var(--cc-sf-dropzone-hint-color, #64748B)!important}.b-1px-solid-E2E8F0{border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0)!important}.b-2px-solid-E2E8F0{border:2px solid #E2E8F0!important}.pr-3-5rem{padding-right:3.5rem!important}.p-0-0-875rem{padding:0 .875rem!important}.bc-FFFFFF{background-color:var(--cc-sf-input-bg, #FFFFFF)!important}.b-1-5px-solid-D1D5DB{border:var(--cc-sf-input-border, 1.5px solid #D1D5DB)!important}.mb-0-75rem{margin-bottom:.75rem!important}.mt-6px{margin-top:6px!important}.pr-2-4rem{padding-right:2.4rem!important}.p-0-2rem{padding:.2rem!important}.fs-1-35rem{font-size:1.35rem!important}.p-4px-12px{padding:4px 12px!important}.b-111827{background:var(--cc-sf-label-color, #111827)!important}.b-2px-dashed-CBD5E1{border:2px dashed var(--cc-sf-dropzone-border, #CBD5E1)!important}.fs-52px{font-size:52px!important}.p-12px-16px{padding:12px 16px!important}.bb-1px-solid-F3F4F6{border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)!important}.b-0F172A{background:var(--mu-carousel-bg, #0F172A)!important}.b-3px-solid-rgba-255-255-255-0-15{border:3px solid rgba(255,255,255,.15)!important}.b-rgba-255-255-255-0-85{background:#ffffffd9!important}.b-rgba-0-0-0-0-55{background:#0000008c!important}.b-rgba-255-255-255-0-45{background:#ffffff73!important}.pb-4px{padding-bottom:4px!important}.b-2px-solid-transparent{border:2px solid transparent!important}.b-E2E8F0{background:var(--mu-thumb-bg, #E2E8F0)!important}.b-1E293B{background:#1e293b!important}.c-EF4444{color:#ef4444!important}.b-rgba-0-0-0-0-5{background:#00000080!important}.br-16px{border-radius:var(--mu-modal-radius, 16px)!important}.p-24px-28px{padding:24px 28px!important}.bb-1px-solid-E5E7EB{border-bottom:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.fs-1-25rem{font-size:1.25rem!important}.p-48px-24px{padding:48px 24px!important}.b-3px-solid-E2E8F0{border:3px solid #E2E8F0!important}.p-16px-24px{padding:16px 24px!important}.p-28px{padding:28px!important}.b-3px-solid-transparent{border:3px solid transparent!important}.b-rgba-59-130-246-0-12{background:#3b82f61f!important}.p-20px-28px{padding:20px 28px!important}.c-1A56DB{color:var(--loc-add-color, #1A56DB)!important}.b-1px-dashed-D1D5DB{border:1px dashed var(--cc-sf-input-disabled-border, #D1D5DB)!important}.fs-40px{font-size:40px!important}.c-9CA3AF{color:var(--loc-tba-icon-color, #9CA3AF)!important}.form-field{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)!important}:host{--cc-sf-input-border: #D1D5DB;--cc-sf-input-bg: #ffffff;--cc-sf-input-radius: 9px;--cc-sf-input-height: 44px;--cc-sf-label-color: #111827;--cc-sf-hint-color: #9CA3AF;--cc-sf-error-border: #EF4444;--cc-sf-error-bg: #FFF5F5;--cc-sf-accent-color: #6366F1;--cc-sf-input-focus-border: #6366F1;--cc-sf-input-hover-border: #A5B4FC;--cc-sf-input-placeholder: #C4C9D4;--cc-sf-input-disabled-bg: #F8F9FB;--cc-sf-input-disabled-border: #E5E7EB;--cc-sf-switch-track-on: #6366F1;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-thumb: #ffffff;--cc-sf-selected-color: #6366F1}.form-row{gap:var(--cc-sf-grid-gap, 16px)}.form-row.horizontal{display:flex;flex-direction:row}.form-row.horizontal>*{flex:1}.form-row:not(.horizontal){flex-direction:column}.form-row.grid-row{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.form-row.grid-row{grid-template-columns:1fr}.form-row.grid-row .row-field{grid-column:span 12!important}}.field-label{font-size:.75rem;font-weight:600;line-height:1;letter-spacing:.04em;text-transform:uppercase;color:#6b7280}.field-input,input.matInput,.mat-mdc-input-element{display:block;width:100%;height:var(--cc-sf-input-height)!important;padding:0 14px!important;font-family:inherit;font-size:.9rem;color:var(--cc-sf-label-color);background-color:var(--cc-sf-input-bg)!important;border:1.5px solid var(--cc-sf-input-border)!important;border-radius:var(--cc-sf-input-radius)!important;box-sizing:border-box;box-shadow:0 1px 2px #0000000a!important;transition:all .2s cubic-bezier(.4,0,.2,1)}.field-input::placeholder,input.matInput::placeholder,.mat-mdc-input-element::placeholder{font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder)}.field-input{opacity:var(--cc-sf-input-opacity, 1);line-height:var(--cc-sf-input-line-height, 1.5);transition:var(--cc-sf-input-transition, all .2s ease)}.field-input::placeholder{font-weight:var(--cc-sf-placeholder-weight, 400);font-size:var(--cc-sf-placeholder-size, 14px);line-height:var(--cc-sf-placeholder-line-height, 100%);color:var(--cc-sf-input-placeholder)}.field-input:hover:not(:disabled):not([readonly]){border-color:var(--cc-sf-input-hover-border)!important;box-shadow:0 1px 4px #6366f114!important}.field-input:focus{outline:none;border-color:var(--cc-sf-input-focus-border)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important;background-color:#fefeff!important}.field-input:disabled,.field-input[readonly]{background-color:var(--cc-sf-input-disabled-bg)!important;color:#9ca3af!important;cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important;box-shadow:none!important}.field-input.is-invalid{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)!important}.field-input.is-invalid:focus{box-shadow:0 0 0 3px #ef44441f,0 1px 4px #ef44441a!important}.field-input.textarea{resize:vertical;min-height:100px;height:auto;padding:12px 16px!important}input[type=time].time-input{cursor:pointer}input[type=time].time-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:.7;filter:invert(30%)}input[type=time].time-input::-webkit-calendar-picker-indicator:hover{opacity:1}select.field-input{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E\");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;cursor:pointer}select.field-input:disabled{cursor:not-allowed}.char-count-hint{font-style:normal;line-height:100%;letter-spacing:.02em;margin-top:.4rem}.radio-group.layout-column,.checkbox-group.layout-column{display:grid!important;grid-template-columns:repeat(12,1fr);gap:16px;width:100%}.radio-group.layout-row,.checkbox-group.layout-row{flex-direction:column!important;gap:12px;width:100%}.radio-label input,.checkbox-label input{cursor:pointer;accent-color:var(--cc-sf-chip-selected-bg, #F05A28)}.radio-label.card-item,.checkbox-label.card-item{display:flex!important;flex-direction:row-reverse!important;justify-content:space-between!important;align-items:center!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB);border-radius:12px;padding:16px 20px;box-sizing:border-box;transition:all .2s ease;background:var(--cc-sf-input-bg, #ffffff);margin-bottom:0}.radio-label.card-item input,.checkbox-label.card-item input{margin-left:16px}.radio-label.card-item.selected,.checkbox-label.card-item.selected{border-color:var(--cc-sf-selected-color);background-color:#f05a280d}.radio-label.card-item .option-content .label-text,.checkbox-label.card-item .option-content .label-text,.checkbox-single .checkbox-label{font-weight:var(--cc-sf-label-weight, 500)}.chip-label{transition:var(--cc-sf-input-transition, all .2s ease)}.chip-label:hover{background:var(--cc-sf-chip-hover-bg, #F3F4F6)}.chip-label.selected{background:var(--cc-sf-selected-color);color:var(--cc-sf-chip-selected-color, #ffffff);border-color:var(--cc-sf-selected-color)}.switch{width:50px;height:24px;display:inline-block}.switch input{opacity:0;width:0;height:0;position:absolute}.switch input:checked+.slider{background-color:var(--cc-sf-switch-track-on)!important}.switch input:checked+.slider:before{transform:translate(26px)}.switch .slider{inset:0;transition:.4s;background-color:var(--cc-sf-switch-track-off);border-radius:24px}.switch .slider:before{position:absolute;content:\"\";height:18px;width:18px;left:3px;bottom:3px;background-color:var(--cc-sf-switch-thumb);transition:.4s;border-radius:50%}.rating-group .star{transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star mat-icon{font-size:var(--cc-sf-star-size, 28px);width:var(--cc-sf-star-size, 28px);height:var(--cc-sf-star-size, 28px);line-height:var(--cc-sf-star-size, 28px);color:var(--cc-sf-star-empty, #D1D5DB);transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star.filled mat-icon,.rating-group .star.half mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.rating-group .star:hover mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.password-wrapper .password-toggle{right:.625rem;top:50%;transform:translateY(-50%);line-height:1;transition:color var(--cc-sf-input-transition, .2s ease)}.password-wrapper .password-toggle mat-icon.eye-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;line-height:1.125rem}.password-wrapper .password-toggle:hover{color:var(--cc-sf-label-color, #374151)}.password-wrapper .password-toggle:focus{outline:none}.group-section-wrapper .group-label{font-size:var(--cc-sf-section-label-size, 1rem);font-weight:var(--cc-sf-section-label-weight, 600);color:var(--cc-sf-section-label-color, #1F2937);margin:0 0 16px;padding-left:12px;padding-top:2px;padding-bottom:2px;border-left:var(--cc-sf-section-header-accent-width, 4px) solid var(--cc-sf-section-header-accent-color, #3B82F6);line-height:1.4}.group-section-wrapper .group-fields.sf-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.group-section-wrapper .group-fields.sf-grid{grid-template-columns:1fr}.group-section-wrapper .group-fields.sf-grid .sf-col{grid-column:span 12!important}}.group-section-wrapper .group-accordion-instance{border:var(--cc-sf-instance-border, 1px solid #E5E7EB);border-radius:var(--cc-sf-section-border-radius-inner, 8px);margin-bottom:8px;overflow:hidden;transition:border-color .2s ease}.group-section-wrapper .group-accordion-instance:last-of-type{margin-bottom:0}.group-section-wrapper .group-accordion-instance .group-accordion-header{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;background:var(--cc-sf-repeater-accordion-header-bg, #F9FAFB);cursor:pointer;-webkit-user-select:none;user-select:none;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header:hover{background:var(--cc-sf-repeater-accordion-active-bg, #EFF6FF)}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-badge{width:22px;height:22px;border-radius:50%;background:var(--cc-sf-repeater-badge-bg, #E5E7EB);color:var(--cc-sf-repeater-badge-color, #374151);font-size:.75rem;font-weight:600;display:flex;align-items:center;justify-content:center;flex-shrink:0}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-title{font-size:var(--cc-sf-instance-num-size, .8125rem);font-weight:600;color:var(--cc-sf-repeater-accordion-header-color, #1F2937)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn{background:none;border:none;cursor:pointer;color:var(--cc-sf-btn-remove-color, #E53E3E);padding:4px;border-radius:4px;line-height:1;display:flex;align-items:center;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-chevron{font-size:1.25rem;width:1.25rem;height:1.25rem;line-height:1.25rem;color:var(--cc-sf-instance-num-color, #4B5563)}.group-section-wrapper .group-accordion-instance .group-accordion-body{padding:var(--cc-sf-instance-padding, 16px);background:var(--cc-sf-instance-bg, #F9FAFB);border-top:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)}.group-section-wrapper .btn-add-group{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:10px 20px;margin-top:12px;background:var(--cc-sf-btn-add-bg, transparent);color:var(--cc-sf-btn-add-color, #3B82F6);border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1);border-radius:var(--cc-sf-btn-add-radius, 6px);cursor:pointer;font-family:inherit;font-size:var(--cc-sf-btn-font-size, .875rem);font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.group-section-wrapper .btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.group-section-wrapper .group-instance:last-child{margin-bottom:0}.group-section-wrapper.multi-save-active{border:none;box-shadow:none;padding:0;background:transparent}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button{color:var(--ms-btn-add-color, #3B82F6);font-weight:600;font-size:.875rem;padding:8px 12px}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button:hover{color:var(--ms-btn-add-hover, #2563EB);background-color:var(--cc-sf-btn-add-hover-bg, #EFF6FF)}.group-section-wrapper.multi-save-active .group-instance.is-card{cursor:pointer;transition:all .2s ease-in-out}.group-section-wrapper.multi-save-active .group-instance.is-card:hover{box-shadow:var(--ms-card-shadow-hover, 0 8px 24px rgba(0, 0, 0, .08));border-color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-title{white-space:nowrap;text-overflow:ellipsis}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-desc{line-height:1.4;display:-webkit-box;-webkit-line-clamp:1;line-clamp:1;-webkit-box-orient:vertical}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view.is-expanded .card-content .card-desc{-webkit-line-clamp:unset;line-clamp:unset}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon{font-size:22px;width:22px;height:22px;color:var(--cc-sf-hint-color, #9CA3AF);transition:color .2s}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-delete:hover{color:var(--cc-sf-error-border, #DC2626)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-edit:hover{color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-expand{color:var(--cc-sf-input-disabled-border, #E5E7EB)}.btn-remove{transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-remove mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.btn-remove:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.btn-add-group{font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.upload-drop-zone{background-color:var(--cc-sf-dropzone-bg, #F8FAFC);border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1);border-radius:var(--cc-sf-dropzone-radius, 12px);transition:background-color .2s ease,border-color .2s ease}.upload-drop-zone:hover{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-hover-border, #93C5FD)}.upload-drop-zone.drag-over{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-over-border, #3B82F6);box-shadow:var(--cc-sf-dropzone-over-shadow, 0 0 0 4px rgba(59, 130, 246, .12))}.upload-drop-zone.is-invalid{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.upload-icon-wrap .dropzone-icon-pill{width:52px;height:52px;border-radius:50%;background:var(--cc-sf-dropzone-icon-bg, rgba(59, 130, 246, .1))}.upload-icon-wrap mat-icon.upload-cloud-icon{font-size:28px;width:28px;height:28px;line-height:28px;color:var(--cc-sf-accent-color, #3B82F6)}.upload-main-text{color:var(--cc-sf-label-color, #1E293B)}.upload-sub-text{color:var(--cc-sf-hint-color, #64748B)}.upload-link{color:var(--cc-sf-dropzone-link-color, #3B82F6);font-weight:500}.upload-formats{color:var(--cc-sf-dropzone-link-color, #3B82F6)}.upload-size-badge{display:inline-block;padding:2px 8px;border-radius:20px;background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-hint-color, #6B7280);font-weight:500}.uploaded-item{background:var(--cc-sf-uploaded-item-bg, #ffffff);border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0);border-radius:var(--cc-sf-uploaded-item-radius, 8px);transition:box-shadow .15s ease}.uploaded-item:hover{box-shadow:0 2px 6px #0000000f}.uploaded-item mat-icon.file-type-icon{font-size:20px;width:20px;height:20px;line-height:20px;flex-shrink:0;color:var(--cc-sf-hint-color, #64748B)}.uploaded-item .file-thumb{width:36px;height:36px;object-fit:cover;flex-shrink:0}.uploaded-item .file-info{min-width:0;gap:2px}.uploaded-item .file-info .file-name{white-space:nowrap;text-overflow:ellipsis}.uploaded-item .file-remove-btn{flex-shrink:0;width:32px;height:32px;background:none;border:none;cursor:pointer;color:var(--cc-sf-uploaded-remove-color, #94A3B8);padding:0;display:flex;align-items:center;justify-content:center;transition:color .15s ease,background .15s ease}.uploaded-item .file-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.uploaded-item .file-remove-btn:hover:not(:disabled){color:var(--cc-sf-uploaded-remove-hover-color, #DC2626);background:var(--cc-sf-uploaded-remove-hover-bg, #FEF2F2)}.uploaded-item .file-remove-btn:disabled{opacity:.4;cursor:not-allowed}.uploaded-item.uploading{background:var(--cc-sf-uploaded-uploading-bg, #F8FAFC);border-color:var(--cc-sf-uploaded-uploading-border, #CBD5E1);opacity:.85}.upload-spinner{width:20px;height:20px;flex-shrink:0;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}@keyframes cc-spin{to{transform:rotate(360deg)}}.uploading-label{font-style:italic}.input-group{align-items:stretch}.input-group .field-input{flex:1;width:auto}.input-prefix+.input-group .field-input{border-top-left-radius:0;border-bottom-left-radius:0}.input-group .field-input:has(+.input-suffix){border-top-right-radius:0;border-bottom-right-radius:0}.input-group .field-input.has-icon-right{padding-right:3rem}.input-group.readonly .field-input{cursor:default}.input-prefix,.input-suffix{display:flex!important;align-items:center;white-space:nowrap;padding:0 14px;background-color:var(--cc-sf-input-disabled-bg);border:1px solid var(--cc-sf-input-border);font-size:.875rem;color:var(--cc-sf-hint-color);-webkit-user-select:none;user-select:none}.input-prefix{border-right:none;border-top-left-radius:var(--cc-sf-input-radius, 8px);border-bottom-left-radius:var(--cc-sf-input-radius, 8px)}.input-suffix{border-left:none;border-top-right-radius:var(--cc-sf-input-radius, 8px);border-bottom-right-radius:var(--cc-sf-input-radius, 8px)}.readonly-icons{right:.875rem;top:50%;transform:translateY(-50%)}.readonly-icons mat-icon.lock-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem;opacity:.5;color:var(--cc-sf-hint-color, #6B7280)}.date-icon-wrapper{right:.5rem;top:50%;transform:translateY(-50%);pointer-events:auto}.date-icon-wrapper .mat-icon-button{width:32px;height:32px;line-height:32px}.subfields-group-wrapper .subfields-row{transition:all .2s ease}.subfields-group-wrapper .subfields-row.is-invalid .subfield-item ::ng-deep .field-input{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.subfields-group-wrapper .subfields-row .subfield-item{min-width:0}.subfields-group-wrapper .subfields-row .subfield-item ::ng-deep .field-label{font-size:.75rem!important;margin-bottom:4px!important;font-weight:500!important;color:var(--cc-sf-hint-color, #6B7280)!important}.subfields-group-wrapper .subfields-row .subfield-separator{font-weight:700}.autocomplete-wrapper .ac-input{padding-left:40px!important}.autocomplete-wrapper .ac-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1;transition:color var(--cc-sf-input-transition, .2s ease)}.autocomplete-wrapper .ac-clear-btn{right:.6rem;transition:color .15s ease,background .15s ease}.autocomplete-wrapper .ac-clear-btn mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.autocomplete-wrapper .ac-clear-btn:hover{color:var(--cc-sf-label-color, #374151);background:var(--cc-sf-input-disabled-bg, #F3F4F6)}.autocomplete-wrapper .ac-clear-btn:focus{outline:none}.autocomplete-wrapper:focus-within .ac-search-icon{color:var(--cc-sf-accent-color, #3B82F6)}.autocomplete-wrapper.is-invalid .ac-input{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)}.autocomplete-wrapper.readonly .ac-input{background-color:var(--cc-sf-input-disabled-bg);color:var(--cc-sf-input-disabled-color, #6B7280);cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important}.ac-no-results{font-style:italic}::ng-deep .mat-mdc-autocomplete-panel,::ng-deep .mat-autocomplete-panel{background:var(--cc-sf-input-bg, #ffffff)!important;border-radius:var(--cc-sf-input-radius, 9px)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important;box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f!important;padding:4px 0!important;min-width:200px}::ng-deep .mat-mdc-autocomplete-panel mat-option,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option,::ng-deep .mat-mdc-autocomplete-panel .mat-option,::ng-deep .mat-autocomplete-panel mat-option,::ng-deep .mat-autocomplete-panel .mat-mdc-option,::ng-deep .mat-autocomplete-panel .mat-option{background:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #111827)!important;font-size:.875rem!important;padding:10px 16px!important;min-height:40px!important;line-height:1.4!important;display:flex!important;flex-direction:column!important;align-items:flex-start!important;transition:background var(--cc-sf-input-transition, .2s ease)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]){background:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-option.mdc-list-item--selected{background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)!important;color:var(--cc-sf-selected-color, #6366F1)!important;font-weight:600!important}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-option-label{font-weight:500;color:var(--cc-sf-label-color, #111827);font-size:.875rem;display:block}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-option .ac-display-fields{align-items:center;line-height:1}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-item{display:inline-flex;align-items:center;font-size:.72rem;color:var(--cc-sf-hint-color, #6B7280);gap:3px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-chip{background:var(--cc-sf-input-disabled-bg, #F3F4F6);border-radius:4px;padding:2px 6px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-text{color:var(--cc-sf-hint-color, #6B7280)}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--cc-sf-input-border, #D1D5DB);vertical-align:middle}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-label{font-weight:600;color:var(--cc-sf-hint-color, #9CA3AF);margin-right:2px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-icon{font-size:11px;width:11px;height:11px;line-height:11px;color:var(--cc-sf-hint-color, #9CA3AF);flex-shrink:0}.mu-layout{grid-template-columns:1fr 1fr;gap:32px}@media(max-width:768px){.mu-layout{grid-template-columns:1fr}}.mu-title{font-weight:700;line-height:1.3}.mu-badge{white-space:nowrap;flex-shrink:0}.mu-description{line-height:1.6}.mu-feature-item .mu-check{width:16px;height:16px;line-height:16px;flex-shrink:0}.mu-right{min-height:260px}.mu-right-empty{min-height:250px;max-width:400px;box-shadow:0 2px 10px #0000000d;transition:box-shadow .2s ease}.mu-right-empty:hover{cursor:pointer;box-shadow:0 4px 16px #0000001a}.mu-right-empty .mu-right-empty-icon{width:52px;height:52px;line-height:52px;opacity:.3}.mu-right-empty p{margin:0;font-size:.85rem}.media-add-container{display:inline-block}.media-add-container ::ng-deep button{display:flex;align-items:center;gap:6px}.media-add-container ::ng-deep button .menu-chevron{width:18px;height:18px;line-height:18px;transition:transform .2s ease}.media-dropdown{top:calc(100% + 6px);left:0;z-index:200;min-width:240px;box-shadow:var(--mu-dropdown-shadow, 0 8px 32px rgba(0, 0, 0, .12));animation:mu-fade-in .15s ease}@keyframes mu-fade-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.media-dropdown-item{transition:background .15s ease}.media-dropdown-item:last-child{border-bottom:none}.media-dropdown-item:hover{background:var(--cc-sf-dropzone-hover-bg, #F0F9FF)}.media-drop-icon{width:36px;height:36px;flex-shrink:0}.media-drop-icon mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.media-drop-icon--video{background:var(--mu-icon-video-bg, #FFF0F0);color:var(--mu-icon-video-color, #EF4444)}.media-drop-icon--device{background:var(--mu-icon-device-bg, #EFF6FF);color:var(--mu-icon-device-color, #3B82F6)}.media-drop-icon--library{background:var(--mu-icon-library-bg, #F0FDF4);color:var(--mu-icon-library-color, #22C55E)}.media-drop-text{gap:2px}.youtube-input-panel{animation:mu-fade-in .18s ease}.youtube-panel-label mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:var(--mu-icon-video-color, #EF4444)}.youtube-input-row{align-items:stretch}.media-menu-backdrop{position:fixed;inset:0;z-index:100}.media-upload-status{animation:mu-fade-in .2s ease}.media-upload-status .status-icon{width:18px;height:18px;line-height:18px}.media-carousel-main{max-width:400px;height:var(--mu-carousel-height, 250px)}.carousel-viewer{inset:0}.carousel-viewer .carousel-image{object-fit:cover}.carousel-viewer .carousel-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.carousel-nav{top:50%;transform:translateY(-50%);z-index:10;width:40px;height:40px;box-shadow:0 2px 8px #0003;transition:background .2s ease,opacity .2s ease;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.carousel-nav mat-icon{font-size:22px;width:22px;height:22px;line-height:22px;color:#1e293b}.carousel-nav:hover:not(:disabled){background:#fff}.carousel-nav:disabled{opacity:.3;cursor:not-allowed}.carousel-nav--prev{left:12px}.carousel-nav--next{right:12px}.carousel-remove-btn{top:10px;right:10px;z-index:10;width:32px;height:32px;transition:background .2s ease}.carousel-remove-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:#fff}.carousel-remove-btn:hover:not(:disabled){background:#dc2626d9}.carousel-remove-btn:disabled{opacity:.4;cursor:not-allowed}.carousel-dots{bottom:10px;left:50%;transform:translate(-50%);z-index:10}.carousel-dot{width:8px;height:8px;transition:background .2s ease,transform .2s ease}.carousel-dot.active{background:#fff;transform:scale(1.3)}.media-thumbnail-strip{max-width:400px;overflow-x:auto}.media-thumbnail-strip::-webkit-scrollbar{height:4px}.media-thumbnail-strip::-webkit-scrollbar-thumb{background:var(--cc-sf-input-disabled-border, #D1D5DB);border-radius:2px}.media-thumb{flex-shrink:0;width:72px;height:52px;transition:border-color .2s ease,transform .15s ease}.media-thumb.active{border-color:var(--mu-thumb-active-border, var(--cc-sf-accent-color, #3B82F6));transform:scale(1.04)}.media-thumb:hover:not(.active){border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.media-thumb .thumb-img{object-fit:cover}.media-thumb .thumb-yt-placeholder mat-icon{font-size:28px;width:28px;height:28px;line-height:28px}.media-thumb .thumb-uploading .thumb-spinner{width:20px;height:20px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.media-library-portal-host{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;visibility:hidden;opacity:0;pointer-events:none;transition:opacity .2s ease,visibility .2s ease}.media-library-portal-host.is-open{visibility:visible;opacity:1;pointer-events:auto}.media-library-portal-host.is-open .media-library-modal{transform:scale(1) translateY(0)}.media-library-overlay{position:absolute;inset:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.media-library-modal{position:relative;z-index:10000;width:90vw;max-width:900px;max-height:90vh;background:#fff;border-radius:16px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transform:scale(.95) translateY(10px);transition:transform .3s cubic-bezier(.175,.885,.32,1.275)}.library-modal-header{flex-shrink:0}.library-modal-title{font-weight:700}.library-modal-subtitle{line-height:1.5;max-width:600px}.library-close-btn{width:32px;height:32px;transition:background .15s ease,color .15s ease}.library-close-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-close-btn:hover{background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-label-color, #374151)}.library-loading mat-icon,.library-empty mat-icon{font-size:40px;width:40px;height:40px;line-height:40px;opacity:.5}.lib-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.library-error{flex-shrink:0}.library-error mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-grid{grid-template-columns:repeat(5,1fr);max-height:50vh;overflow-y:auto}.library-grid::-webkit-scrollbar{width:8px}.library-grid::-webkit-scrollbar-track{background:#f1f1f1}.library-grid::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:10px;border:2px solid #F1F1F1}.library-grid-item{aspect-ratio:1/1;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.library-grid-item:hover{transform:translateY(-4px) scale(1.02);box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}.library-grid-item.selected{border-color:var(--cc-sf-accent-color, #3B82F6)}.library-grid-item.selected .library-grid-img{opacity:.7}.library-grid-item:hover .library-overlay-hover{opacity:1}.library-grid-img{object-fit:cover}.library-overlay-hover{inset:0;opacity:0;transition:opacity .15s ease}.library-check{top:6px;right:6px;width:22px;height:22px;box-shadow:0 1px 4px #00000026}.library-check mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.library-modal-footer{flex-shrink:0}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary{background-color:var(--cc-sf-accent-color, #3B82F6)!important;border-color:var(--cc-sf-accent-color, #3B82F6)!important;color:#fff!important;font-weight:600;padding-left:32px;padding-right:32px}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:hover{background-color:var(--cc-sf-btn-primary-hover-bg, #2563EB)!important}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:disabled{background-color:#93c5fd!important;cursor:not-allowed}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline{font-weight:600;padding-left:24px;padding-right:24px;border-color:#d1d5db;color:#374151}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline:hover{background-color:#f9fafb}.location-subtitle{line-height:1.5}.loc-tab-btn ::ng-deep button{width:100%}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning){background-color:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #000000)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning):hover{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.loc-venue-item{transition:box-shadow .15s ease,border-color .15s ease}.loc-venue-item:hover{box-shadow:0 2px 8px #0000000f;border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.loc-venue-search-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-venue-text{white-space:nowrap;text-overflow:ellipsis}.loc-action-btn{transition:background .15s ease,color .15s ease;flex-shrink:0}.loc-action-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.loc-action-btn.loc-delete-btn{color:var(--loc-delete-color, #E53E3E)}.loc-action-btn.loc-delete-btn:hover{background:var(--cc-sf-error-bg, #FEF2F2)}.loc-action-btn.loc-edit-btn{color:var(--cc-sf-hint-color, #9CA3AF)}.loc-action-btn.loc-edit-btn:hover{color:var(--cc-sf-input-focus-border, #3B82F6);background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)}.loc-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1}.loc-suggestions-panel{top:calc(100% + 4px);left:0;right:0;z-index:300;box-shadow:0 8px 24px #0000001a;animation:mu-fade-in .15s ease;max-height:260px;overflow-y:auto}.loc-suggestion-item{transition:background .12s ease}.loc-suggestion-item:hover,.loc-suggestion-item:focus{background:var(--loc-suggestion-hover-bg, #F0F9FF)}.loc-suggestion-item:not(:last-child){border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)}.loc-suggestion-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-suggestion-text{white-space:nowrap;text-overflow:ellipsis}.loc-add-btn{transition:opacity .15s ease}.loc-add-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.loc-add-btn:hover{opacity:.8}.loc-map-container{box-shadow:0 2px 10px #0000000f}.loc-tba-panel{min-height:120px}.loc-tba-icon{width:40px;height:40px;line-height:40px;opacity:.6}.loc-tba-text{line-height:1.6;max-width:360px}.radio-label{display:flex!important}.radio-label .option-content{padding-left:10px}\n"], dependencies: [{ 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$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.SelectMultipleControlValueAccessor, selector: "select[multiple][formControlName],select[multiple][formControl],select[multiple][ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i5.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i6.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i9.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }, { kind: "component", type: i11.QuillEditorComponent, selector: "quill-editor" }, { kind: "component", type: FormFieldComponent, selector: "lib-form-field", inputs: ["config", "controller", "formGroup", "allowMulti"] }, { kind: "pipe", type: TrustedUrlPipe, name: "trustedUrl" }] });
|
|
2387
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FormFieldComponent, isStandalone: false, selector: "lib-form-field", inputs: { config: "config", controller: "controller", formGroup: "formGroup", allowMulti: "allowMulti" }, host: { listeners: { "document:click": "onDocumentClick()", "document:keydown.escape": "onEscapeKey()" } }, viewQueries: [{ propertyName: "mediaDeviceInput", first: true, predicate: ["mediaDeviceInput"], descendants: true }, { propertyName: "libraryModalRef", first: true, predicate: ["libraryModal"], descendants: true }], ngImport: i0, template: "<div class=\"form-field mb-16px\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\r\n\r\n <!-- \u2550\u2550 ROW Layout \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRow\" class=\"form-row grid-row\">\r\n <ng-container *ngFor=\"let child of config.children\">\r\n <div class=\"row-field\" [style.gridColumn]=\"'span ' + getChildColSpan(child)\" *ngIf=\"child.isEnabled !== false\">\r\n <lib-form-field [config]=\"child\" [controller]=\"controller\" [formGroup]=\"formGroup\" [allowMulti]=\"allowMulti\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- \u2550\u2550 GROUP \u2014 allowMulti (repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig?.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\"\r\n [class.multi-save-active]=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n\r\n <!-- Multi-Save: header row with label + top-right Add button -->\r\n <div class=\"multi-save-header d-flex justify-content-between align-items-center mb-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig?.label\">{{ config.sectionConfig!.label }}</h3>\r\n <lib-button [variant]=\"'outline'\" [icon]=\"{type: 'material', value: 'add'}\" (click)=\"addGroupInstance()\"\r\n class=\"btn-add-multi\">\r\n {{ addMultiLabel }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- Standard heading (non-multiSave) -->\r\n <h3 class=\"group-label\"\r\n *ngIf=\"config.sectionConfig?.label && !config.sectionConfig?.multiSaveConfig?.active\">{{\r\n config.sectionConfig!.label }}</h3>\r\n\r\n <!-- \u2500\u2500 Standard (non-multiSave) repeater: accordion instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <ng-container *ngIf=\"!config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-accordion-instance\"\r\n [class.is-expanded]=\"isGroupExpanded(i)\">\r\n\r\n <!-- Accordion header -->\r\n <div class=\"group-accordion-header\" (click)=\"toggleGroupAccordion(i)\"\r\n role=\"button\" [attr.aria-expanded]=\"isGroupExpanded(i)\">\r\n <div class=\"accordion-header-left d-flex align-items-center gap-10\">\r\n <span class=\"instance-badge\">{{ i + 1 }}</span>\r\n <span class=\"instance-title\">{{ config.sectionConfig!.label }} #{{ i + 1 }}</span>\r\n </div>\r\n <div class=\"accordion-header-right d-flex align-items-center gap-6\">\r\n <button type=\"button\" class=\"accordion-remove-btn\"\r\n *ngIf=\"instanceList.length > 1\"\r\n (click)=\"$event.stopPropagation(); removeGroupInstance(i)\"\r\n aria-label=\"Remove\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n <mat-icon class=\"accordion-chevron\">\r\n {{ isGroupExpanded(i) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <!-- Accordion body (always mounted so form controls survive collapse) -->\r\n <div class=\"group-accordion-body\" [hidden]=\"!isGroupExpanded(i)\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\">\r\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"true\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Full-width dashed Add button -->\r\n <button type=\"button\" class=\"btn-add-group\" (click)=\"addGroupInstance()\">\r\n <mat-icon>add</mat-icon> {{ addLabel }} {{ config.sectionConfig!.label }}\r\n </button>\r\n </ng-container>\r\n\r\n <!-- \u2500\u2500 MultiSave: card instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <ng-container *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-instance position-relative mb-16 overflow-hidden\"\r\n [class.is-editing]=\"instance.isEditing\"\r\n [class.is-card]=\"!instance.isEditing\">\r\n\r\n <!-- Edit / new form view -->\r\n <div [hidden]=\"!instance.isEditing\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\">\r\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"true\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Save / Cancel -->\r\n <div class=\"group-footer d-flex justify-content-end align-items-center gap-16 p-0-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"multiSaveError\">{{ multiSaveError }}</span>\r\n <div class=\"footer-actions d-flex gap-12\">\r\n <lib-button [variant]=\"'outline'\" (click)=\"cancelGroupInstance(i)\">Cancel</lib-button>\r\n <lib-button [variant]=\"'primary'\" (click)=\"saveGroupInstance(i)\">Save</lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Card view (saved state) -->\r\n <ng-container *ngIf=\"!instance.isEditing\">\r\n <div class=\"card-view d-flex justify-content-between align-items-center p-18px-24px\"\r\n [class.is-expanded]=\"instance.isExpanded\">\r\n <div class=\"card-content flex-1 d-flex flex-column gap-4 overflow-hidden\">\r\n <span class=\"card-title font-weight-600 overflow-hidden fs-1rem c-111827\">{{\r\n instance.fg.get(config.sectionConfig!.multiSaveConfig!.summaryField || '')?.value\r\n || '\u2014' }}</span>\r\n </div>\r\n <div class=\"card-actions d-flex align-items-center gap-16 ml-20\">\r\n <mat-icon class=\"icon-delete\" (click)=\"removeGroupInstance(i, true)\">delete_outline</mat-icon>\r\n <mat-icon class=\"icon-edit\" (click)=\"editGroupInstance(i)\">edit_outline</mat-icon>\r\n <mat-icon class=\"icon-expand\" (click)=\"toggleExpandGroupInstance(i)\">\r\n {{ instance.isExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- \u2550\u2550 GROUP \u2014 single (non-repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig && !config.sectionConfig.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig.label\">{{ config.sectionConfig.label }}</h3>\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\" *ngIf=\"field.isEnabled !== false\">\r\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"groupFormGroup\" [allowMulti]=\"false\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n\r\n <!-- \u2550\u2550 Text Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isTextField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <textarea *ngIf=\"config.subType === 'LONG'\" class=\"field-input textarea\" [placeholder]=\"config.placeholder || ''\"\r\n [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\" rows=\"4\">\r\n </textarea>\r\n\r\n <!-- Password input with show/hide toggle -->\r\n <div *ngIf=\"config.subType === 'PASSWORD'\" class=\"password-wrapper position-relative d-flex align-items-center\">\r\n <input [type]=\"showPassword ? 'text' : 'password'\" class=\"field-input password-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <button type=\"button\"\r\n class=\"password-toggle position-absolute cursor-pointer d-flex align-items-center justify-content-center b-none border-none c-6B7280 p-0-25rem\"\r\n (click)=\"showPassword = !showPassword\" tabindex=\"-1\"\r\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\r\n <mat-icon class=\"eye-icon\">{{ showPassword ? 'visibility' : 'visibility_off' }}</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input *ngIf=\"config.subType !== 'LONG' && config.subType !== 'PASSWORD'\"\r\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\" class=\"field-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\"\r\n [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Number Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isNumberField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input type=\"number\" class=\"field-input\" [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\"\r\n [min]=\"config.numberConfig?.min ?? null\" [max]=\"config.numberConfig?.max ?? null\"\r\n [step]=\"config.numberConfig?.step || 1\" [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Date Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isDateField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input matInput [matDatepicker]=\"datePicker\" class=\"field-input date-input has-icon-right\"\r\n [formControlName]=\"config.name!\" [min]=\"config.dateConfig?.minDate\" [max]=\"config.dateConfig?.maxDate\"\r\n [class.is-invalid]=\"errorMessage\" [placeholder]=\"config.placeholder || ''\" [readonly]=\"config.readonly\"\r\n (click)=\"!config.readonly && datePicker.open()\">\r\n <div class=\"date-icon-wrapper position-absolute d-flex align-items-center justify-content-center\"\r\n *ngIf=\"!config.readonly\">\r\n <mat-datepicker-toggle matSuffix [for]=\"datePicker\"></mat-datepicker-toggle>\r\n </div>\r\n <mat-datepicker #datePicker></mat-datepicker>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Time Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isTimeField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input type=\"time\" class=\"field-input time-input\" [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\" [readonly]=\"!!config.readonly\">\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Autocomplete \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isAutocomplete\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Hidden real control (stores the code value) -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <div class=\"autocomplete-wrapper position-relative d-flex align-items-center w-100\"\r\n [class.is-invalid]=\"errorMessage\" [class.readonly]=\"config.readonly\">\r\n <!-- Search icon -->\r\n <mat-icon class=\"ac-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n\r\n <input class=\"field-input ac-input\" [formControl]=\"autocompleteInputCtrl\" [matAutocomplete]=\"auto\"\r\n [placeholder]=\"config.placeholder || 'Search\u2026'\" [readonly]=\"!!config.readonly\" [class.is-invalid]=\"errorMessage\"\r\n (blur)=\"onAutocompleteClear()\" autocomplete=\"off\">\r\n\r\n <!-- Clear button -->\r\n <button type=\"button\"\r\n class=\"ac-clear-btn position-absolute d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none c-9CA3AF p-0-2rem\"\r\n *ngIf=\"autocompleteInputCtrl.value && !config.readonly\"\r\n (click)=\"autocompleteInputCtrl.setValue(''); updateValue(null)\" tabindex=\"-1\" aria-label=\"Clear\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n\r\n <mat-autocomplete #auto=\"matAutocomplete\" [panelWidth]=\"'auto'\">\r\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.label\"\r\n (onSelectionChange)=\"onAutocompleteSelected(option)\">\r\n <span class=\"ac-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Dynamic display fields (image / email / phone / text) -->\r\n <div class=\"ac-display-fields d-flex flex-wrap gap-6 mt-2\" *ngIf=\"option['displayMeta']?.length\">\r\n <ng-container *ngFor=\"let field of option['displayMeta']\">\r\n\r\n <!-- Image avatar -->\r\n <span *ngIf=\"field.type === 'image' && field.value\" class=\"ac-df-item ac-df-image\">\r\n <img [src]=\"field.value\" [alt]=\"field.label || 'image'\" class=\"ac-df-avatar\">\r\n </span>\r\n\r\n <!-- Email -->\r\n <span *ngIf=\"field.type === 'email' && field.value\" class=\"ac-df-item ac-df-chip\">\r\n <mat-icon class=\"ac-df-icon\">mail_outline</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Phone -->\r\n <span *ngIf=\"field.type === 'phone' && field.value\" class=\"ac-df-item ac-df-chip\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\">phone</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Custom / Icon-based / Generic Text -->\r\n <span *ngIf=\"field.type !== 'image' && field.type !== 'email' && field.type !== 'phone' && field.value\" \r\n class=\"ac-df-item\" [class.ac-df-chip]=\"!!field.icon\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\" *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n </ng-container>\r\n </div>\r\n </mat-option>\r\n <mat-option *ngIf=\"filteredOptions.length === 0\" disabled class=\"ac-no-results fs-0-8125rem c-6B7280\">\r\n No results found\r\n </mat-option>\r\n </mat-autocomplete>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Dropdown \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isDropdown\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <select *ngIf=\"config.subType === 'SINGLE'\" class=\"field-input\" [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\">\r\n <option [ngValue]=\"null\" disabled selected>{{ config.placeholder || 'Select' }}</option>\r\n <option *ngFor=\"let option of config.optionConfig?.optionList\" [value]=\"option.code\">\r\n {{ option.label }}\r\n </option>\r\n </select>\r\n\r\n <!-- MULTIPLE SELECT: custom panel with checkboxes -->\r\n <div *ngIf=\"config.subType === 'MULTIPLE'\" class=\"multi-select-wrapper\"\r\n [class.is-invalid]=\"errorMessage\">\r\n\r\n <div class=\"field-input multi-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\r\n [class.ms-open]=\"isMultiDropdownOpen\"\r\n (click)=\"toggleMultiDropdown($event)\">\r\n <span *ngIf=\"multiSelectedCount > 0\" class=\"multi-select-value fs-0-9rem\">\r\n {{ multiSelectedCount }} selected\r\n </span>\r\n <span *ngIf=\"multiSelectedCount === 0\" class=\"multi-select-placeholder\">\r\n {{ config.placeholder || selectPlaceholderLabel }}\r\n </span>\r\n <mat-icon class=\"multi-select-arrow\">\r\n {{ isMultiDropdownOpen ? expandLessLabel : expandMoreLabel }}\r\n </mat-icon>\r\n </div>\r\n\r\n <div class=\"multi-select-panel\" *ngIf=\"isMultiDropdownOpen\"\r\n (click)=\"$event.stopPropagation()\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\r\n class=\"multi-select-option d-flex align-items-center gap-8 cursor-pointer\">\r\n <input type=\"checkbox\"\r\n [checked]=\"isChecked(option.code)\"\r\n [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\r\n </label>\r\n <div *ngIf=\"!config.optionConfig?.optionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noOptionsAvailableLabel }}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Radio \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRadio\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"radio-group\" [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\" class=\"radio-label\"\r\n [class.card-item]=\"config.subType === 'CARD'\"\r\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"radio\" [formControlName]=\"config.name!\" [value]=\"option.code\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Checkbox \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label && config.subType === 'LIST'\"\r\n class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\r\n <label class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span>{{ config.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <div *ngIf=\"config.subType === 'LIST' || config.subType === 'CARD'\" class=\"checkbox-group d-flex flex-column gap-8\"\r\n [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\r\n class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\"\r\n [class.card-item]=\"config.subType === 'CARD'\" [class.selected]=\"isChecked(option.code)\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Chip \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isChip\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"chip-group d-flex flex-wrap gap-8\" [class.is-invalid]=\"errorMessage\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\r\n class=\"chip-label cursor-pointer p-6px-14px b-ffffff c-374151 b-1px-solid-D1D5DB br-20px fs-0-875rem\"\r\n [class.selected]=\"isChecked(option.code)\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\" hidden>\r\n <span>{{ option.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Switch \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isSwitch\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label class=\"switch-container d-flex justify-content-between align-items-center cursor-pointer\">\r\n <span class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label }}</span>\r\n <div class=\"switch position-relative\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span class=\"slider position-absolute cursor-pointer\"></span>\r\n </div>\r\n </label>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Rich Text \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRichText\" class=\"field-wrapper component-rich-text d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rich-text-container\" [class.is-invalid]=\"errorMessage\">\r\n <quill-editor [formControlName]=\"config.name!\" class=\"rich-text-editor d-block w-100\"\r\n [placeholder]=\"config.richTextConfig?.placeholder || config.placeholder || ''\"\r\n [styles]=\"{height: config.richTextConfig?.height || '200px'}\">\r\n </quill-editor>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Rating \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRating\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rating-group d-flex gap-4\" [class.is-invalid]=\"errorMessage\">\r\n <span *ngFor=\"let star of getStarArray()\" class=\"star d-inline-flex align-items-center cursor-pointer\"\r\n [class.filled]=\"isStarFilled(star)\" [class.half]=\"isStarHalf(star)\" (click)=\"onRatingChange(star, $event)\">\r\n <mat-icon>{{ isStarFilled(star) || isStarHalf(star) ? 'star' : 'star_border' }}</mat-icon>\r\n </span>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Generated Field (read-only) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isGenerated\" class=\"field-wrapper d-flex flex-column gap-6\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label\r\n }}</label>\r\n <div class=\"generated-value fs-0-875rem p-0-625rem-0-875rem b-F3F4F6 b-1px-solid-E5E7EB br-8px c-6B7280\">{{ value ||\r\n '-' }}</div>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint\">{{ config.hint }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 File Upload \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isFileUpload\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Drop Zone -->\r\n <div\r\n class=\"upload-drop-zone d-flex flex-column align-items-center justify-content-center gap-8 cursor-pointer text-center p-32px-24px us-none\"\r\n [class.drag-over]=\"isDragOver\" [class.has-files]=\"value?.length\" [class.is-invalid]=\"errorMessage\"\r\n (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onFileDrop($event)\"\r\n (click)=\"fileInput.click()\">\r\n\r\n <!-- Icon with accent-colour pill background -->\r\n <div class=\"upload-icon-wrap mb-4\">\r\n <div class=\"dropzone-icon-pill d-flex align-items-center justify-content-center\">\r\n <mat-icon class=\"upload-cloud-icon\">cloud_upload</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <p class=\"upload-main-text font-weight-600 m-0 fs-0-9rem\">Drag & drop files here</p>\r\n <p class=\"upload-sub-text m-0 fs-0-8rem c-64748B\">or <span class=\"upload-link\">Browse files</span></p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"config.attachmentConfig?.acceptLabel\">\r\n Supported: <span class=\"upload-formats font-weight-500\">{{ config.attachmentConfig!.acceptLabel }}</span>\r\n </p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"!config.attachmentConfig?.acceptLabel && config.hint\">\r\n {{ config.hint }}\r\n </p>\r\n <span class=\"upload-size-badge fs-0-72rem\" *ngIf=\"config.attachmentConfig?.maxSizeMB\">\r\n Max {{ config.attachmentConfig!.maxSizeMB }}MB\r\n </span>\r\n\r\n <!-- Hidden native file input -->\r\n <input #fileInput type=\"file\" hidden [attr.multiple]=\"config.attachmentConfig?.multiple ? true : null\"\r\n [attr.accept]=\"config.attachmentConfig?.accept || null\" (change)=\"onFileSelected($event)\">\r\n </div>\r\n\r\n <!-- Uploaded file list -->\r\n <div class=\"uploaded-list d-flex flex-column gap-8 mt-10\" *ngIf=\"value?.length\">\r\n <div *ngFor=\"let f of value; let i = index\"\r\n class=\"uploaded-item d-flex align-items-center gap-10 p-10px-14px br-8px\"\r\n [class.uploading]=\"f.isUploading\">\r\n\r\n <!-- Uploading spinner -->\r\n <ng-container *ngIf=\"f.isUploading; else fileReady\">\r\n <div class=\"upload-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size uploading-label fs-0-72rem\">Uploading...</span>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Normal state once upload is done -->\r\n <ng-template #fileReady>\r\n <mat-icon class=\"file-type-icon\">{{ getFileIcon(f.type) }}</mat-icon>\r\n <img *ngIf=\"f.type?.startsWith('image') && f.dataUrl\" [src]=\"f.dataUrl\" class=\"file-thumb rounded-4\"\r\n alt=\"preview\">\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size fs-0-72rem\">{{ formatFileSize(f.size) }}</span>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Compact icon-only remove button -->\r\n <button type=\"button\" class=\"file-remove-btn d-flex align-items-center justify-content-center rounded-50\"\r\n [disabled]=\"f.isUploading\" (click)=\"!f.isUploading && removeUploadedFile(i)\"\r\n aria-label=\"Remove file\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Validation / file errors -->\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"fileUploadError\">{{ fileUploadError }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage && !fileUploadError\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\"\r\n *ngIf=\"config.hint && !errorMessage && !fileUploadError && !config.attachmentConfig?.acceptLabel\">\r\n {{ config.hint }}\r\n </span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Media Upload (Type 2) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isMediaUpload\" class=\"field-wrapper media-upload-wrapper d-flex flex-column gap-6 p-0 border-none b-none\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Hidden file input lives outside *ngIf \u2014 triggered via ViewChild -->\r\n <input #mediaDeviceInput type=\"file\" hidden multiple accept=\"image/*\" (change)=\"onMediaFileSelected($event)\">\r\n\r\n <!-- Two-column layout -->\r\n <div class=\"mu-layout d-grid align-items-start\">\r\n\r\n <!-- \u2500\u2500 LEFT PANEL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"mu-left d-flex flex-column gap-20\">\r\n\r\n <!-- Header: title + max-items badge -->\r\n <div class=\"mu-header d-flex align-items-start flex-wrap gap-10\">\r\n <h3 class=\"mu-title m-0 c-111827 fs-1-35rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </h3>\r\n <span\r\n class=\"mu-badge d-inline-flex align-items-center rounded-20 color-white font-weight-600 fs-0-72rem p-4px-12px b-111827\"\r\n *ngIf=\"config.attachmentConfig?.maxFiles\">\r\n {{ controller.labels['LBL_MEDIA_MAX_PREFIX'] || 'Maximum' }}\r\n {{ config.attachmentConfig?.maxFiles }}\r\n {{ controller.labels['LBL_MEDIA_MAX_SUFFIX'] || 'Image & Videos' }}\r\n </span>\r\n </div>\r\n\r\n <!-- Description -->\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\" *ngIf=\"config.attachmentConfig?.description\">\r\n {{ config.attachmentConfig?.description }}\r\n </p>\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\"\r\n *ngIf=\"!config.attachmentConfig?.description && controller.labels['LBL_MEDIA_DESC']\">\r\n {{ controller.labels['LBL_MEDIA_DESC'] }}\r\n </p>\r\n\r\n <!-- Feature bullet list -->\r\n <ul class=\"mu-features m-0 p-0 d-flex flex-column gap-8 ls-none\"\r\n *ngIf=\"config.attachmentConfig?.features?.length || controller.labels['LBL_MEDIA_FEATURE_1']\">\r\n <ng-container *ngIf=\"config.attachmentConfig?.features?.length\">\r\n <li *ngFor=\"let f of config.attachmentConfig?.features\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ f }}\r\n </li>\r\n </ng-container>\r\n <ng-container *ngIf=\"!config.attachmentConfig?.features?.length\">\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_1']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_1'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_2']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_2'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_3']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_3'] }}\r\n </li>\r\n </ng-container>\r\n </ul>\r\n\r\n <!-- Backdrop to close dropdown on outside click -->\r\n <div class=\"media-menu-backdrop\" *ngIf=\"showMediaMenu\"\r\n (click)=\"$event.stopPropagation(); showMediaMenu = false\"></div>\r\n\r\n <!-- Add Media button + dropdown -->\r\n <div class=\"media-add-container position-relative\" (click)=\"showMediaMenu = !showMediaMenu\">\r\n <lib-button id=\"btn-add-media-{{ config.name }}\" [variant]=\"'warning'\"\r\n [icon]=\"{type: 'material', value: 'add_photo_alternate'}\">\r\n {{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}\r\n <mat-icon class=\"menu-chevron fs-18px\">add</mat-icon>\r\n </lib-button>\r\n\r\n <div class=\"media-dropdown position-absolute rounded-12 overflow-hidden b-ffffff b-1px-solid-E5E7EB\"\r\n *ngIf=\"showMediaMenu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\r\n <!-- Video -->\r\n <button id=\"btn-media-video-{{ config.name }}\" type=\"button\"\r\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\r\n (click)=\"onMediaMenuVideo(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\n class=\"media-drop-icon media-drop-icon--video d-flex align-items-center justify-content-center rounded-8\"><mat-icon>videocam</mat-icon></span>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_VIDEO'] || 'Video' }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_VIDEO_DESC'] || 'Add\r\n YouTube URL'\r\n }}</span>\r\n </span>\r\n </button>\r\n <!-- Device -->\r\n <button id=\"btn-media-device-{{ config.name }}\" type=\"button\"\r\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\r\n (click)=\"onMediaMenuDevice(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\n class=\"media-drop-icon media-drop-icon--device d-flex align-items-center justify-content-center rounded-8\"><mat-icon>upload</mat-icon></span>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_DEVICE'] || 'Upload from device'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_DEVICE_DESC'] ||\r\n 'Select images from your\r\n computer' }}</span>\r\n </span>\r\n </button>\r\n <!-- Library -->\r\n <button id=\"btn-media-library-{{ config.name }}\" type=\"button\"\r\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\r\n (click)=\"onMediaMenuLibrary(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\n class=\"media-drop-icon media-drop-icon--library d-flex align-items-center justify-content-center rounded-8\"><mat-icon>photo_library</mat-icon></span>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_LIBRARY'] || 'Upload from library'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_LIBRARY_DESC'] ||\r\n 'Choose from default\r\n images' }}</span>\r\n </span>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- YouTube URL Input (inline below button) -->\r\n <div class=\"youtube-input-panel d-flex flex-column gap-8 p-16 rounded-10 b-FFFAF1 b-1px-solid-E5E7EB\"\r\n *ngIf=\"showYoutubeInput\">\r\n <label class=\"youtube-panel-label d-flex align-items-center gap-6 font-weight-600 fs-0-875rem c-111827\">\r\n {{ controller.labels['LBL_YOUTUBE_URL'] || 'Video URL' }}\r\n </label>\r\n <div class=\"youtube-input-row d-flex gap-8\">\r\n <input id=\"input-youtube-url-{{ config.name }}\" type=\"url\" class=\"field-input youtube-url-input\"\r\n [(ngModel)]=\"youtubeUrlInput\" [ngModelOptions]=\"{standalone: true}\"\r\n [placeholder]=\"controller.labels['PH_YOUTUBE_URL'] || 'Video URL'\" (keyup.enter)=\"addYoutubeMedia()\">\r\n <lib-button id=\"btn-add-youtube-{{ config.name }}\" [variant]=\"'secondary'\" (click)=\"addYoutubeMedia()\">\r\n {{ controller.labels['LBL_ADD'] || 'Add' }}\r\n </lib-button>\r\n </div>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"youtubeUrlError\">{{ youtubeUrlError }}</span>\r\n </div>\r\n\r\n <div\r\n class=\"media-upload-status d-flex align-items-center gap-8 mt-4 color-error rounded-8 font-weight-500 p-10px-14px b-FEF2F2 fs-0-85rem\"\r\n *ngIf=\"mediaUploadError\">\r\n <mat-icon class=\"status-icon fs-18px\">error_outline</mat-icon>\r\n <span>{{ mediaUploadError }}</span>\r\n </div>\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n </div>\r\n <!-- end left panel -->\r\n\r\n <!-- \u2500\u2500 RIGHT PANEL (carousel) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"mu-right d-flex flex-column gap-12\">\r\n\r\n <!-- Carousel (when items exist) -->\r\n <div class=\"media-carousel-section d-flex flex-column gap-12\" *ngIf=\"mediaItems.length\">\r\n <div\r\n class=\"media-carousel-main position-relative w-100 overflow-hidden d-flex align-items-center justify-content-center br-12px b-0F172A\">\r\n <button id=\"btn-carousel-prev-{{ config.name }}\" type=\"button\"\r\n class=\"carousel-nav carousel-nav--prev position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\r\n (click)=\"mediaCarouselPrev()\" [disabled]=\"mediaCarouselIndex === 0\" aria-label=\"Previous\">\r\n <mat-icon>chevron_left</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-viewer position-absolute d-flex align-items-center justify-content-center\"\r\n *ngFor=\"let item of mediaItems; let i = index\" [hidden]=\"i !== mediaCarouselIndex\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"carousel-uploading d-flex flex-column align-items-center gap-12 c-94A3B8 fs-0-85rem\">\r\n <div class=\"carousel-spinner rounded-50 b-3px-solid-rgba-255-255-255-0-15\"></div>\r\n <span>{{ controller.labels['LBL_UPLOADING'] || 'Uploading\u2026' }}</span>\r\n </div>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'youtube'\">\r\n <iframe class=\"carousel-iframe w-100 h-100 br-12px\" [src]=\"item.url | trustedUrl\" frameborder=\"0\"\r\n allowfullscreen\r\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\">\r\n </iframe>\r\n </ng-container>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'image'\">\r\n <img class=\"carousel-image w-100 h-100 br-12px\" [src]=\"item.url\" alt=\"Media\">\r\n </ng-container>\r\n <button id=\"btn-remove-media-{{ config.name }}-{{ i }}\" type=\"button\"\r\n class=\"carousel-remove-btn position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-0-0-0-0-55\"\r\n [disabled]=\"item.isUploading\" (click)=\"removeMediaItem(i)\" aria-label=\"Remove\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <button id=\"btn-carousel-next-{{ config.name }}\" type=\"button\"\r\n class=\"carousel-nav carousel-nav--next position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\r\n (click)=\"mediaCarouselNext()\" [disabled]=\"mediaCarouselIndex === mediaItems.length - 1\" aria-label=\"Next\">\r\n <mat-icon>chevron_right</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-dots position-absolute d-flex gap-6\">\r\n <span *ngFor=\"let item of mediaItems; let i = index\"\r\n class=\"carousel-dot rounded-50 cursor-pointer b-rgba-255-255-255-0-45\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\"></span>\r\n </div>\r\n </div>\r\n\r\n <!-- Thumbnail strip -->\r\n <div class=\"media-thumbnail-strip d-flex flex-wrap gap-8 pb-4px\">\r\n <div *ngFor=\"let item of mediaThumbnails; let i = index\"\r\n class=\"media-thumb rounded-8 overflow-hidden cursor-pointer d-flex align-items-center justify-content-center b-2px-solid-transparent b-E2E8F0\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"thumb-uploading d-flex align-items-center justify-content-center w-100 h-100\">\r\n <div class=\"thumb-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && item.thumbnailUrl\"\r\n [src]=\"item.thumbnailUrl\" class=\"thumb-img w-100 h-100\" alt=\"Video thumbnail\">\r\n <div *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && !item.thumbnailUrl\"\r\n class=\"thumb-yt-placeholder d-flex align-items-center justify-content-center w-100 h-100 b-1E293B c-EF4444\">\r\n <mat-icon>play_circle</mat-icon>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'image' && item.url\" [src]=\"item.url\"\r\n class=\"thumb-img w-100 h-100\" alt=\"Image thumbnail\">\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty right-side placeholder -->\r\n <div\r\n class=\"mu-right-empty d-flex flex-column align-items-center justify-content-center gap-10 h-100 text-center p-24 br-12px b-FFFAF1 c-94A3B8 b-2px-dashed-CBD5E1\"\r\n *ngIf=\"!mediaItems.length\" (click)=\"onMediaMenuDevice()\">\r\n <mat-icon class=\"mu-right-empty-icon fs-52px\">perm_media</mat-icon>\r\n <p>{{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}</p>\r\n </div>\r\n\r\n </div>\r\n <!-- end right panel -->\r\n\r\n </div><!-- end mu-layout -->\r\n </div>\r\n\r\n\r\n <!-- \u2550\u2550 Library Image Picker Modal \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <!-- Wrapper is always in DOM (hidden) so @ViewChild can move it to body -->\r\n <div #libraryModal class=\"media-library-portal-host\" [class.is-open]=\"showLibraryModal\">\r\n\r\n <!-- Backdrop -->\r\n <div class=\"media-library-overlay\" (click)=\"closeLibraryModal()\"></div>\r\n\r\n <!-- Modal card -->\r\n <div class=\"media-library-modal d-flex flex-column overflow-hidden b-ffffff br-16px\"\r\n role=\"dialog\" aria-modal=\"true\">\r\n <div class=\"library-modal-header d-flex align-items-start justify-content-between p-24px-28px bb-1px-solid-E5E7EB\">\r\n <div class=\"header-left d-flex flex-column gap-8\">\r\n <h3 class=\"library-modal-title m-0 color-dark fs-1-25rem\">\r\n {{ controller.labels['LBL_ADD_IMAGES'] || 'Add Images' }}\r\n </h3>\r\n <p class=\"library-modal-subtitle m-0 color-gray fs-0-85rem\">\r\n {{ controller.labels['LBL_LIBRARY_MODAL_DESC'] || 'Select images from your library.' }}\r\n </p>\r\n </div>\r\n <button id=\"btn-close-library-{{ config.name }}\" type=\"button\"\r\n class=\"library-close-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 border-none b-none c-9CA3AF\"\r\n (click)=\"closeLibraryModal()\" aria-label=\"Close\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Loading -->\r\n <div class=\"library-loading\" *ngIf=\"libraryLoading\">\r\n <div class=\"lib-spinner rounded-50 b-3px-solid-E2E8F0\"></div>\r\n <span>{{ controller.labels['LBL_LOADING'] || 'Loading\u2026' }}</span>\r\n </div>\r\n\r\n <!-- Error -->\r\n <div class=\"library-error d-flex align-items-center gap-8 color-error b-FEF2F2 fs-0-875rem p-16px-24px\"\r\n *ngIf=\"libraryError && !libraryLoading\">\r\n <mat-icon>error_outline</mat-icon>\r\n {{ libraryError }}\r\n </div>\r\n\r\n <!-- Image grid -->\r\n <div class=\"library-grid d-grid gap-16 flex-1 p-28px b-F9FAFB\" *ngIf=\"!libraryLoading && libraryImages.length\">\r\n <div *ngFor=\"let img of libraryImages; let li = index\" id=\"lib-img-{{ config.name }}-{{ li }}\"\r\n class=\"library-grid-item position-relative rounded-12 overflow-hidden cursor-pointer bg-white b-3px-solid-transparent\"\r\n [class.selected]=\"isLibraryItemSelected(img)\" (click)=\"toggleLibraryItem(img)\">\r\n <img [src]=\"getLibraryItemUrl(img)\" class=\"library-grid-img w-100 h-100 d-block\" alt=\"Library image\">\r\n <div\r\n class=\"library-check position-absolute bg-white rounded-50 d-flex align-items-center justify-content-center c-3B82F6\"\r\n *ngIf=\"isLibraryItemSelected(img)\">\r\n <mat-icon>check_circle</mat-icon>\r\n </div>\r\n <div class=\"library-overlay-hover position-absolute b-rgba-59-130-246-0-12\"></div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty library -->\r\n <div\r\n class=\"library-empty d-flex flex-column align-items-center justify-content-center gap-12 flex-1 c-9CA3AF fs-0-875rem p-48px-24px\"\r\n *ngIf=\"!libraryLoading && !libraryError && libraryImages.length === 0\">\r\n <mat-icon>image_not_supported</mat-icon>\r\n <span>{{ controller.labels['LBL_LIBRARY_EMPTY'] || 'No images found in library.' }}</span>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"library-modal-footer d-flex align-items-center justify-content-end bg-white p-20px-28px bt-1px-solid-E5E7EB\">\r\n <div class=\"library-footer-actions d-flex gap-12\">\r\n <lib-button id=\"btn-library-cancel-{{ config.name }}\" [variant]=\"'outline'\" (click)=\"closeLibraryModal()\">\r\n {{ controller.labels['LBL_CANCEL'] || 'Cancel' }}\r\n </lib-button>\r\n <lib-button id=\"btn-library-confirm-{{ config.name }}\" [variant]=\"'primary'\"\r\n [disabled]=\"librarySelectedIds.size === 0\" (click)=\"confirmLibrarySelection()\">\r\n {{ controller.labels['LBL_CONTINUE'] || 'Continue' }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n\r\n\r\n <!-- \u2550\u2550 Location Field \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isLocation\" class=\"field-wrapper location-field-wrapper d-flex flex-column gap-6 gap-12\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Field label -->\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n <p class=\"location-subtitle m-0 c-6B7280 fs-0-8125rem\" *ngIf=\"config.hint\">{{ config.hint }}</p>\r\n\r\n <!-- Three-tab bar -->\r\n <div class=\"location-tabs d-flex gap-12 mb-24\">\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'VENUE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('VENUE')\">\r\n {{ controller.labels['LBL_LOC_VENUE'] || 'Venue' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'ONLINE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('ONLINE')\">\r\n {{ controller.labels['LBL_LOC_ONLINE'] || 'Online Event' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'TBA' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('TBA')\">\r\n {{ controller.labels['LBL_LOC_TBA'] || 'To be Announced' }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- VENUE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'VENUE'\" class=\"loc-panel loc-venue-panel d-flex flex-column gap-12\">\r\n\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ADDRESS'] || 'Location address' }}\r\n </p>\r\n\r\n <!-- Added venue rows -->\r\n <div class=\"loc-venue-list d-flex flex-column gap-8\" *ngIf=\"locationVenues.length > 0\">\r\n <div *ngFor=\"let venue of locationVenues; let i = index\"\r\n class=\"loc-venue-item d-flex align-items-center gap-10 p-10px-14px br-7px b-ffffff b-1px-solid-D1D5DB\">\r\n <mat-icon class=\"loc-venue-search-icon fs-18px c-9CA3AF\">search</mat-icon>\r\n <span class=\"loc-venue-text flex-1 overflow-hidden fs-0-875rem c-111827\">{{ venue.address || venue.name ||\r\n venue.description }}</span>\r\n <button type=\"button\"\r\n class=\"loc-action-btn loc-delete-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none p-4px\"\r\n (click)=\"removeLocationVenue(i)\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Location count badge -->\r\n <p class=\"loc-count-text m-0 font-weight-600 fs-0-8125rem c-3B82F6\"\r\n *ngIf=\"locationVenues.length > 0 && config.locationConfig?.allowMulti\">\r\n {{ locationVenues.length }} {{ controller.labels['LBL_LOC_COUNT_SUFFIX'] || 'Locations Added!' }}\r\n </p>\r\n\r\n <!-- Search input (hide when max reached) -->\r\n <div class=\"loc-search-container position-relative\" *ngIf=\"!locationMaxReached\">\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n <input\r\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\r\n [placeholder]=\"config.locationConfig?.venuePlaceholder || (controller.labels['PH_LOC_VENUE'] || 'Type to search venue...')\"\r\n [value]=\"locationSearchText\" (input)=\"handleLocationSearchInput($event)\" (blur)=\"hideLocationSuggestions()\"\r\n autocomplete=\"off\" [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n <!-- Suggestions dropdown -->\r\n <div class=\"loc-suggestions-panel position-absolute overflow-hidden br-8px b-ffffff b-1px-solid-D1D5DB\"\r\n *ngIf=\"locationShowSuggestions && locationSuggestions.length\">\r\n <div *ngFor=\"let sug of locationSuggestions\"\r\n class=\"loc-suggestion-item d-flex align-items-center gap-10 cursor-pointer p-10px-14px\"\r\n (mousedown)=\"onLocationSuggestionSelect(sug)\">\r\n <mat-icon class=\"loc-suggestion-icon fs-18px c-E53E3E\">place</mat-icon>\r\n <span class=\"loc-suggestion-text overflow-hidden fs-0-875rem c-374151\">{{ sug.description }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Add another button -->\r\n <button type=\"button\"\r\n class=\"loc-add-btn d-inline-flex align-items-center gap-6 cursor-pointer font-weight-600 p-0 b-none border-none fs-0-875rem c-1A56DB\"\r\n *ngIf=\"locationVenues.length > 0 && !locationMaxReached && config.locationConfig?.allowMulti\">\r\n <mat-icon>add_circle_outline</mat-icon>\r\n <span>{{ controller.labels['LBL_LOC_ADD_ANOTHER'] || 'Add another Location' }}</span>\r\n </button>\r\n\r\n <!-- Map -->\r\n <div class=\"loc-map-container overflow-hidden br-10px b-1px-solid-E5E7EB\"\r\n *ngIf=\"config.locationConfig?.showMap !== false\">\r\n <ng-container *ngIf=\"config.locationConfig?.googleMapsApiKey; else simpleEmbed\">\r\n <div [id]=\"'loc-map-' + config.name\" class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\"></div>\r\n </ng-container>\r\n <ng-template #simpleEmbed>\r\n <iframe class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\" [src]=\"getLocationMapEmbedUrl() | trustedUrl\"\r\n frameborder=\"0\" allowfullscreen loading=\"lazy\">\r\n </iframe>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- Map hint -->\r\n <p class=\"loc-map-hint m-0 text-center fs-0-78rem c-6B7280\">\r\n {{ controller.labels['LBL_LOC_MAP_HINT'] || 'Type the venue address. A map will appear to assist you.' }}\r\n </p>\r\n </div>\r\n\r\n <!-- ONLINE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'ONLINE'\" class=\"loc-panel loc-online-panel d-flex flex-column gap-12\">\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ONLINE_URL'] || 'Event URL' }}\r\n </p>\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">link</mat-icon>\r\n <input\r\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\r\n type=\"url\"\r\n [placeholder]=\"config.locationConfig?.onlinePlaceholder || (controller.labels['PH_LOC_ONLINE'] || 'https://zoom.us/j/...')\"\r\n [value]=\"locationOnlineUrl\" (input)=\"onLocationUrlChange($any($event.target).value)\"\r\n [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n </div>\r\n\r\n <!-- TBA TAB -->\r\n <div *ngIf=\"locationActiveTab === 'TBA'\"\r\n class=\"loc-panel loc-tba-panel d-flex flex-column gap-12 justify-content-center\">\r\n <div\r\n class=\"loc-tba-content d-flex flex-column align-items-center justify-content-center text-center gap-12 p-32px-24px b-F9FAFB b-1px-dashed-D1D5DB br-10px\">\r\n <mat-icon class=\"loc-tba-icon fs-40px c-9CA3AF\">schedule</mat-icon>\r\n <p class=\"loc-tba-text m-0 c-6B7280 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_TBA_DESC'] || \"This event's location is yet to be announced. Check back later\r\n for updates.\" }}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <!-- Hidden real form control -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n</div>", styles: [".d-flex{display:flex}.d-inline-flex{display:inline-flex}.d-grid{display:grid}.d-block{display:block}.d-none{display:none}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.align-items-center{align-items:center}.align-items-start{align-items:flex-start}.align-items-end{align-items:flex-end}.justify-content-center{justify-content:center}.justify-content-between{justify-content:space-between}.justify-content-start{justify-content:flex-start}.justify-content-end{justify-content:flex-end}.grid-cols-12{grid-template-columns:repeat(12,1fr)}.w-100{width:100%}.h-100{height:100%}.position-relative{position:relative}.position-absolute{position:absolute}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-poppins{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)}.font-weight-400{font-weight:400}.font-weight-500{font-weight:500}.font-weight-600{font-weight:600}.text-13{font-size:13px}.text-14{font-size:14px}.text-16{font-size:16px}.color-white{color:#fff}.color-dark{color:#111827}.color-gray{color:#6b7280}.color-error{color:var(--cc-sf-error-text-color, #DC2626)}.bg-white{background-color:#fff}.bg-transparent{background-color:transparent}.m-0{margin:0}.mt-4{margin-top:4px}.mt-8{margin-top:8px}.mt-10{margin-top:10px}.mt-12{margin-top:12px}.mt-16{margin-top:16px}.mt-20{margin-top:20px}.mt-24{margin-top:24px}.mb-0{margin-bottom:0}.mb-4{margin-bottom:4px}.mb-8{margin-bottom:8px}.mb-10{margin-bottom:10px}.mb-12{margin-bottom:12px}.mb-16{margin-bottom:16px}.mb-20{margin-bottom:20px}.mb-24{margin-bottom:24px}.ml-16{margin-left:16px}.ml-20{margin-left:20px}.p-0{padding:0}.p-16{padding:16px}.p-20{padding:20px}.p-24{padding:24px}.pt-20{padding-top:20px}.pb-10{padding-bottom:10px}.gap-4{gap:4px}.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-16{gap:16px}.gap-20{gap:20px}.rounded-4{border-radius:4px}.rounded-6{border-radius:6px}.rounded-8{border-radius:8px}.rounded-10{border-radius:10px}.rounded-12{border-radius:12px}.rounded-20{border-radius:20px}.rounded-24{border-radius:24px}.rounded-50{border-radius:50%}.cursor-pointer{cursor:pointer}.overflow-hidden{overflow:hidden}.resize-vertical{resize:vertical}.box-sizing-border{box-sizing:border-box}.border-none{border:none!important}.mb-16px{margin-bottom:var(--cc-sf-grid-gap, 16px)!important}.c-DC2626{color:var(--cc-sf-label-required-color, #DC2626)!important}.ml-0-125rem{margin-left:.125rem!important}.fs-0-875rem{font-size:.875rem!important}.c-111827{color:var(--cc-sf-label-color, #111827)!important}.br-7px{border-radius:var(--cc-sf-input-radius, 7px)!important}.c-6B7280{color:var(--cc-sf-hint-color, #6B7280)!important}.fs-0-75rem{font-size:var(--cc-sf-error-text-size, .75rem)!important}.b-none{background:none!important}.p-32px-24px{padding:32px 24px!important}.us-none{-webkit-user-select:none!important;user-select:none!important}.c-1E293B{color:var(--cc-sf-label-color, #1E293B)!important}.c-3B82F6{color:var(--cc-sf-chip-selected-bg, #3B82F6)!important}.fs-0-78rem{font-size:.78rem!important}.p-10px-14px{padding:10px 14px!important}.fs-0-85rem{font-size:.85rem!important}.fs-0-72rem{font-size:.72rem!important}.c-94A3B8{color:#94a3b8!important}.p-4px{padding:4px!important}.br-8px{border-radius:var(--cc-sf-input-radius, 8px)!important}.bc-F3F4F6{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.br-none{border-right:none!important}.bl-none{border-left:none!important}.pe-none{pointer-events:none!important}.fs-1-1rem{font-size:1.1rem!important}.c-9CA3AF{color:var(--cc-sf-hint-color, #9CA3AF)!important}.pl-2-4rem{padding-left:2.4rem!important}.fs-0-8125rem{font-size:.8125rem!important}.ls-none{list-style:none!important}.br-12px{border-radius:var(--mu-carousel-radius, 12px)!important}.b-FFFAF1{background:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.fs-18px{font-size:18px!important}.b-FEF2F2{background:var(--cc-sf-error-bg, #FEF2F2)!important}.bc-DC2626{border-color:var(--cc-sf-error-border, #DC2626)!important}.c-202124{color:var(--cc-sf-label-color, #202124)!important}.fs-18px{font-size:var(--cc-sf-label-size, 18px)!important}.mb-0-5rem{margin-bottom:.5rem!important}.pt-0-625rem{padding-top:var(--cc-sf-input-padding-y, .625rem)!important}.pb-0-625rem{padding-bottom:var(--cc-sf-input-padding-y, .625rem)!important}.pl-16px{padding-left:var(--cc-sf-input-padding-x, 16px)!important}.pr-16px{padding-right:var(--cc-sf-input-padding-x, 16px)!important}.bc-ffffff{background-color:var(--cc-sf-section-bg, #ffffff)!important}.b-1px-solid-D1D5DB{border:1px solid var(--cc-sf-input-border, #D1D5DB)!important}.fs-0-75rem{font-size:.75rem!important}.c-1F2937{color:var(--cc-sf-section-label-color, #1F2937)!important}.p-6px-14px{padding:var(--cc-sf-chip-padding, 6px 14px)!important}.b-ffffff{background:var(--loc-suggestion-bg, #ffffff)!important}.c-374151{color:var(--cc-sf-label-color, #374151)!important}.br-20px{border-radius:var(--cc-sf-chip-radius, 20px)!important}.fs-0-875rem{font-size:var(--cc-sf-btn-font-size, .875rem)!important}.bc-D1D5DB{background-color:var(--cc-sf-switch-track-off, #D1D5DB)!important}.pr-2-75rem{padding-right:2.75rem!important}.p-0-25rem{padding:.25rem!important}.p-0-625rem-0-875rem{padding:var(--cc-sf-generated-padding, .625rem .875rem)!important}.b-F3F4F6{background:var(--cc-sf-generated-bg, #F3F4F6)!important}.b-1px-solid-E5E7EB{border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.br-8px{border-radius:var(--cc-sf-uploaded-item-radius, 8px)!important}.c-6B7280{color:var(--ms-desc-color, #6B7280)!important}.mb-20px{margin-bottom:var(--cc-sf-section-gap, 20px)!important}.br-10px{border-radius:var(--cc-sf-input-radius, 10px)!important}.p-20px{padding:var(--cc-sf-section-padding, 20px)!important}.fs-1rem{font-size:1rem!important}.m-0-0-16px-0{margin:0 0 16px!important}.bb-2px-solid-E5E7EB{border-bottom:var(--cc-sf-section-label-border, 2px solid #E5E7EB)!important}.p-16px{padding:var(--cc-sf-instance-padding, 16px)!important}.b-F9FAFB{background:var(--loc-tba-bg, #F9FAFB)!important}.bb-1px-dashed-D1D5DB{border-bottom:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)!important}.c-4B5563{color:var(--cc-sf-instance-num-color, #4B5563)!important}.fs-0-8125rem{font-size:var(--cc-sf-hint-size, .8125rem)!important}.pb-0{padding-bottom:0!important}.p-18px-24px{padding:18px 24px!important}.c-111827{color:var(--ms-title-color, #111827)!important}.bt-1px-solid-E5E7EB{border-top:1px solid #E5E7EB!important}.p-4px-10px{padding:4px 10px!important}.b-FFF5F5{background:var(--cc-sf-btn-remove-bg, #FFF5F5)!important}.c-E53E3E{color:var(--loc-delete-color, #E53E3E)!important}.b-1px-solid-FED7D7{border:var(--cc-sf-btn-remove-border, 1px solid #FED7D7)!important}.br-4px{border-radius:var(--cc-sf-btn-remove-radius, 4px)!important}.p-8px-16px{padding:8px 16px!important}.b-transparent{background:var(--cc-sf-btn-add-bg, transparent)!important}.c-3B82F6{color:var(--cc-sf-input-focus-border, #3B82F6)!important}.b-1px-dashed-CBD5E1{border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1)!important}.br-6px{border-radius:var(--cc-sf-btn-add-radius, 6px)!important}.b-1-5px-dashed-CBD5E1{border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1)!important}.br-12px{border-radius:var(--cc-sf-dropzone-radius, 12px)!important}.bc-FFFAF1{background-color:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.c-94A3B8{color:var(--cc-sf-uploaded-remove-color, #94A3B8)!important}.fs-0-9rem{font-size:var(--cc-sf-input-font-size, .9rem)!important}.c-64748B{color:var(--cc-sf-dropzone-hint-color, #64748B)!important}.b-1px-solid-E2E8F0{border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0)!important}.b-2px-solid-E2E8F0{border:2px solid #E2E8F0!important}.pr-3-5rem{padding-right:3.5rem!important}.p-0-0-875rem{padding:0 .875rem!important}.bc-FFFFFF{background-color:var(--cc-sf-input-bg, #FFFFFF)!important}.b-1-5px-solid-D1D5DB{border:var(--cc-sf-input-border, 1.5px solid #D1D5DB)!important}.mb-0-75rem{margin-bottom:.75rem!important}.mt-6px{margin-top:6px!important}.pr-2-4rem{padding-right:2.4rem!important}.p-0-2rem{padding:.2rem!important}.fs-1-35rem{font-size:1.35rem!important}.p-4px-12px{padding:4px 12px!important}.b-111827{background:var(--cc-sf-label-color, #111827)!important}.b-2px-dashed-CBD5E1{border:2px dashed var(--cc-sf-dropzone-border, #CBD5E1)!important}.fs-52px{font-size:52px!important}.p-12px-16px{padding:12px 16px!important}.bb-1px-solid-F3F4F6{border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)!important}.b-0F172A{background:var(--mu-carousel-bg, #0F172A)!important}.b-3px-solid-rgba-255-255-255-0-15{border:3px solid rgba(255,255,255,.15)!important}.b-rgba-255-255-255-0-85{background:#ffffffd9!important}.b-rgba-0-0-0-0-55{background:#0000008c!important}.b-rgba-255-255-255-0-45{background:#ffffff73!important}.pb-4px{padding-bottom:4px!important}.b-2px-solid-transparent{border:2px solid transparent!important}.b-E2E8F0{background:var(--mu-thumb-bg, #E2E8F0)!important}.b-1E293B{background:#1e293b!important}.c-EF4444{color:#ef4444!important}.b-rgba-0-0-0-0-5{background:#00000080!important}.br-16px{border-radius:var(--mu-modal-radius, 16px)!important}.p-24px-28px{padding:24px 28px!important}.bb-1px-solid-E5E7EB{border-bottom:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.fs-1-25rem{font-size:1.25rem!important}.p-48px-24px{padding:48px 24px!important}.b-3px-solid-E2E8F0{border:3px solid #E2E8F0!important}.p-16px-24px{padding:16px 24px!important}.p-28px{padding:28px!important}.b-3px-solid-transparent{border:3px solid transparent!important}.b-rgba-59-130-246-0-12{background:#3b82f61f!important}.p-20px-28px{padding:20px 28px!important}.c-1A56DB{color:var(--loc-add-color, #1A56DB)!important}.b-1px-dashed-D1D5DB{border:1px dashed var(--cc-sf-input-disabled-border, #D1D5DB)!important}.fs-40px{font-size:40px!important}.c-9CA3AF{color:var(--loc-tba-icon-color, #9CA3AF)!important}.form-field{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)!important}:host{--cc-sf-input-border: #D1D5DB;--cc-sf-input-bg: #ffffff;--cc-sf-input-radius: 9px;--cc-sf-input-height: 44px;--cc-sf-label-color: #111827;--cc-sf-hint-color: #9CA3AF;--cc-sf-error-border: #EF4444;--cc-sf-error-bg: #FFF5F5;--cc-sf-accent-color: #6366F1;--cc-sf-input-focus-border: #6366F1;--cc-sf-input-hover-border: #A5B4FC;--cc-sf-input-placeholder: #C4C9D4;--cc-sf-input-disabled-bg: #F8F9FB;--cc-sf-input-disabled-border: #E5E7EB;--cc-sf-switch-track-on: #6366F1;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-thumb: #ffffff;--cc-sf-selected-color: #6366F1}.form-row{gap:var(--cc-sf-grid-gap, 16px)}.form-row.horizontal{display:flex;flex-direction:row}.form-row.horizontal>*{flex:1}.form-row:not(.horizontal){flex-direction:column}.form-row.grid-row{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.form-row.grid-row{grid-template-columns:1fr}.form-row.grid-row .row-field{grid-column:span 12!important}}.field-label{font-size:.75rem;font-weight:600;line-height:1;letter-spacing:.04em;text-transform:uppercase;color:#6b7280}.field-input,input.matInput,.mat-mdc-input-element{display:block;width:100%;height:var(--cc-sf-input-height)!important;padding:0 14px!important;font-family:inherit;font-size:.9rem;color:var(--cc-sf-label-color);background-color:var(--cc-sf-input-bg)!important;border:1.5px solid var(--cc-sf-input-border)!important;border-radius:var(--cc-sf-input-radius)!important;box-sizing:border-box;box-shadow:0 1px 2px #0000000a!important;transition:all .2s cubic-bezier(.4,0,.2,1)}.field-input::placeholder,input.matInput::placeholder,.mat-mdc-input-element::placeholder{font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder)}.field-input{opacity:var(--cc-sf-input-opacity, 1);line-height:var(--cc-sf-input-line-height, 1.5);transition:var(--cc-sf-input-transition, all .2s ease)}.field-input::placeholder{font-weight:var(--cc-sf-placeholder-weight, 400);font-size:var(--cc-sf-placeholder-size, 14px);line-height:var(--cc-sf-placeholder-line-height, 100%);color:var(--cc-sf-input-placeholder)}.field-input:hover:not(:disabled):not([readonly]){border-color:var(--cc-sf-input-hover-border)!important;box-shadow:0 1px 4px #6366f114!important}.field-input:focus{outline:none;border-color:var(--cc-sf-input-focus-border)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important;background-color:#fefeff!important}.field-input:disabled,.field-input[readonly]{background-color:var(--cc-sf-input-disabled-bg)!important;color:#9ca3af!important;cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important;box-shadow:none!important}.field-input.is-invalid{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)!important}.field-input.is-invalid:focus{box-shadow:0 0 0 3px #ef44441f,0 1px 4px #ef44441a!important}.field-input.textarea{resize:vertical;min-height:100px;height:auto;padding:12px 16px!important}input[type=time].time-input{cursor:pointer}input[type=time].time-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:.7;filter:invert(30%)}input[type=time].time-input::-webkit-calendar-picker-indicator:hover{opacity:1}select.field-input{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E\");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;cursor:pointer}select.field-input:disabled{cursor:not-allowed}.multi-select-wrapper{position:relative}.multi-select-wrapper .multi-select-trigger{min-height:var(--cc-sf-input-height, 44px);height:auto;-webkit-user-select:none;user-select:none}.multi-select-wrapper .multi-select-trigger.ms-open{border-color:var(--cc-sf-input-focus-border, #6366F1)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important}.multi-select-wrapper .multi-select-trigger .multi-select-value{flex:1;color:var(--cc-sf-label-color, #111827);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.multi-select-wrapper .multi-select-trigger .multi-select-placeholder{flex:1;font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder, #C4C9D4)}.multi-select-wrapper .multi-select-trigger .multi-select-arrow{font-size:20px;width:20px;height:20px;line-height:20px;color:#6b7280;flex-shrink:0}.multi-select-wrapper .multi-select-panel{position:absolute;top:calc(100% + 4px);left:0;right:0;background:#fff;border:1.5px solid var(--cc-sf-input-border, #D1D5DB);border-radius:var(--cc-sf-input-radius, 9px);box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f;z-index:1050;max-height:240px;overflow-y:auto}.multi-select-wrapper .multi-select-panel::-webkit-scrollbar{width:4px}.multi-select-wrapper .multi-select-panel::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:2px}.multi-select-wrapper .multi-select-option{padding:8px 12px;border-bottom:1px solid #F3F4F6;transition:background .1s}.multi-select-wrapper .multi-select-option:last-of-type{border-bottom:none}.multi-select-wrapper .multi-select-option:hover{background:#f9fafb}.multi-select-wrapper .multi-select-option input[type=checkbox]{flex-shrink:0;cursor:pointer;accent-color:var(--cc-sf-selected-color, #6366F1);width:15px;height:15px}.multi-select-wrapper .multi-select-empty{padding:12px;text-align:center}.multi-select-wrapper.is-invalid .multi-select-trigger{border-color:var(--cc-sf-error-border, #EF4444)!important;background-color:var(--cc-sf-error-bg, #FFF5F5)!important}.char-count-hint{font-style:normal;line-height:100%;letter-spacing:.02em;margin-top:.4rem}.radio-group.layout-column,.checkbox-group.layout-column{display:grid!important;grid-template-columns:repeat(12,1fr);gap:16px;width:100%}.radio-group.layout-row,.checkbox-group.layout-row{flex-direction:column!important;gap:12px;width:100%}.radio-label input,.checkbox-label input{cursor:pointer;accent-color:var(--cc-sf-chip-selected-bg, #F05A28)}.radio-label.card-item,.checkbox-label.card-item{display:flex!important;flex-direction:row-reverse!important;justify-content:space-between!important;align-items:center!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB);border-radius:12px;padding:16px 20px;box-sizing:border-box;transition:all .2s ease;background:var(--cc-sf-input-bg, #ffffff);margin-bottom:0}.radio-label.card-item input,.checkbox-label.card-item input{margin-left:16px}.radio-label.card-item.selected,.checkbox-label.card-item.selected{border-color:var(--cc-sf-selected-color);background-color:#f05a280d}.radio-label.card-item .option-content .label-text,.checkbox-label.card-item .option-content .label-text,.checkbox-single .checkbox-label{font-weight:var(--cc-sf-label-weight, 500)}.chip-label{transition:var(--cc-sf-input-transition, all .2s ease)}.chip-label:hover{background:var(--cc-sf-chip-hover-bg, #F3F4F6)}.chip-label.selected{background:var(--cc-sf-selected-color);color:var(--cc-sf-chip-selected-color, #ffffff);border-color:var(--cc-sf-selected-color)}.switch{width:50px;height:24px;display:inline-block}.switch input{opacity:0;width:0;height:0;position:absolute}.switch input:checked+.slider{background-color:var(--cc-sf-switch-track-on)!important}.switch input:checked+.slider:before{transform:translate(26px)}.switch .slider{inset:0;transition:.4s;background-color:var(--cc-sf-switch-track-off);border-radius:24px}.switch .slider:before{position:absolute;content:\"\";height:18px;width:18px;left:3px;bottom:3px;background-color:var(--cc-sf-switch-thumb);transition:.4s;border-radius:50%}.rating-group .star{transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star mat-icon{font-size:var(--cc-sf-star-size, 28px);width:var(--cc-sf-star-size, 28px);height:var(--cc-sf-star-size, 28px);line-height:var(--cc-sf-star-size, 28px);color:var(--cc-sf-star-empty, #D1D5DB);transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star.filled mat-icon,.rating-group .star.half mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.rating-group .star:hover mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.password-wrapper .password-toggle{right:.625rem;top:50%;transform:translateY(-50%);line-height:1;transition:color var(--cc-sf-input-transition, .2s ease)}.password-wrapper .password-toggle mat-icon.eye-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;line-height:1.125rem}.password-wrapper .password-toggle:hover{color:var(--cc-sf-label-color, #374151)}.password-wrapper .password-toggle:focus{outline:none}.group-section-wrapper .group-label{font-size:var(--cc-sf-section-label-size, 1rem);font-weight:var(--cc-sf-section-label-weight, 600);color:var(--cc-sf-section-label-color, #1F2937);margin:0 0 16px;padding-left:12px;padding-top:2px;padding-bottom:2px;border-left:var(--cc-sf-section-header-accent-width, 4px) solid var(--cc-sf-section-header-accent-color, #3B82F6);line-height:1.4}.group-section-wrapper .group-fields.sf-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.group-section-wrapper .group-fields.sf-grid{grid-template-columns:1fr}.group-section-wrapper .group-fields.sf-grid .sf-col{grid-column:span 12!important}}.group-section-wrapper .group-accordion-instance{border:var(--cc-sf-instance-border, 1px solid #E5E7EB);border-radius:var(--cc-sf-section-border-radius-inner, 8px);margin-bottom:8px;overflow:hidden;transition:border-color .2s ease}.group-section-wrapper .group-accordion-instance:last-of-type{margin-bottom:0}.group-section-wrapper .group-accordion-instance .group-accordion-header{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;background:var(--cc-sf-repeater-accordion-header-bg, #F9FAFB);cursor:pointer;-webkit-user-select:none;user-select:none;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header:hover{background:var(--cc-sf-repeater-accordion-active-bg, #EFF6FF)}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-badge{width:22px;height:22px;border-radius:50%;background:var(--cc-sf-repeater-badge-bg, #E5E7EB);color:var(--cc-sf-repeater-badge-color, #374151);font-size:.75rem;font-weight:600;display:flex;align-items:center;justify-content:center;flex-shrink:0}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-title{font-size:var(--cc-sf-instance-num-size, .8125rem);font-weight:600;color:var(--cc-sf-repeater-accordion-header-color, #1F2937)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn{background:none;border:none;cursor:pointer;color:var(--cc-sf-btn-remove-color, #E53E3E);padding:4px;border-radius:4px;line-height:1;display:flex;align-items:center;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-chevron{font-size:1.25rem;width:1.25rem;height:1.25rem;line-height:1.25rem;color:var(--cc-sf-instance-num-color, #4B5563)}.group-section-wrapper .group-accordion-instance .group-accordion-body{padding:var(--cc-sf-instance-padding, 16px);background:var(--cc-sf-instance-bg, #F9FAFB);border-top:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)}.group-section-wrapper .btn-add-group{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:10px 20px;margin-top:12px;background:var(--cc-sf-btn-add-bg, transparent);color:var(--cc-sf-btn-add-color, #3B82F6);border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1);border-radius:var(--cc-sf-btn-add-radius, 6px);cursor:pointer;font-family:inherit;font-size:var(--cc-sf-btn-font-size, .875rem);font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.group-section-wrapper .btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.group-section-wrapper .group-instance:last-child{margin-bottom:0}.group-section-wrapper.multi-save-active{border:none;box-shadow:none;padding:0;background:transparent}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button{color:var(--ms-btn-add-color, #3B82F6);font-weight:600;font-size:.875rem;padding:8px 12px}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button:hover{color:var(--ms-btn-add-hover, #2563EB);background-color:var(--cc-sf-btn-add-hover-bg, #EFF6FF)}.group-section-wrapper.multi-save-active .group-instance.is-card{cursor:pointer;transition:all .2s ease-in-out}.group-section-wrapper.multi-save-active .group-instance.is-card:hover{box-shadow:var(--ms-card-shadow-hover, 0 8px 24px rgba(0, 0, 0, .08));border-color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-title{white-space:nowrap;text-overflow:ellipsis}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-desc{line-height:1.4;display:-webkit-box;-webkit-line-clamp:1;line-clamp:1;-webkit-box-orient:vertical}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view.is-expanded .card-content .card-desc{-webkit-line-clamp:unset;line-clamp:unset}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon{font-size:22px;width:22px;height:22px;color:var(--cc-sf-hint-color, #9CA3AF);transition:color .2s}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-delete:hover{color:var(--cc-sf-error-border, #DC2626)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-edit:hover{color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-expand{color:var(--cc-sf-input-disabled-border, #E5E7EB)}.btn-remove{transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-remove mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.btn-remove:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.btn-add-group{font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.upload-drop-zone{background-color:var(--cc-sf-dropzone-bg, #F8FAFC);border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1);border-radius:var(--cc-sf-dropzone-radius, 12px);transition:background-color .2s ease,border-color .2s ease}.upload-drop-zone:hover{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-hover-border, #93C5FD)}.upload-drop-zone.drag-over{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-over-border, #3B82F6);box-shadow:var(--cc-sf-dropzone-over-shadow, 0 0 0 4px rgba(59, 130, 246, .12))}.upload-drop-zone.is-invalid{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.upload-icon-wrap .dropzone-icon-pill{width:52px;height:52px;border-radius:50%;background:var(--cc-sf-dropzone-icon-bg, rgba(59, 130, 246, .1))}.upload-icon-wrap mat-icon.upload-cloud-icon{font-size:28px;width:28px;height:28px;line-height:28px;color:var(--cc-sf-accent-color, #3B82F6)}.upload-main-text{color:var(--cc-sf-label-color, #1E293B)}.upload-sub-text{color:var(--cc-sf-hint-color, #64748B)}.upload-link{color:var(--cc-sf-dropzone-link-color, #3B82F6);font-weight:500}.upload-formats{color:var(--cc-sf-dropzone-link-color, #3B82F6)}.upload-size-badge{display:inline-block;padding:2px 8px;border-radius:20px;background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-hint-color, #6B7280);font-weight:500}.uploaded-item{background:var(--cc-sf-uploaded-item-bg, #ffffff);border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0);border-radius:var(--cc-sf-uploaded-item-radius, 8px);transition:box-shadow .15s ease}.uploaded-item:hover{box-shadow:0 2px 6px #0000000f}.uploaded-item mat-icon.file-type-icon{font-size:20px;width:20px;height:20px;line-height:20px;flex-shrink:0;color:var(--cc-sf-hint-color, #64748B)}.uploaded-item .file-thumb{width:36px;height:36px;object-fit:cover;flex-shrink:0}.uploaded-item .file-info{min-width:0;gap:2px}.uploaded-item .file-info .file-name{white-space:nowrap;text-overflow:ellipsis}.uploaded-item .file-remove-btn{flex-shrink:0;width:32px;height:32px;background:none;border:none;cursor:pointer;color:var(--cc-sf-uploaded-remove-color, #94A3B8);padding:0;display:flex;align-items:center;justify-content:center;transition:color .15s ease,background .15s ease}.uploaded-item .file-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.uploaded-item .file-remove-btn:hover:not(:disabled){color:var(--cc-sf-uploaded-remove-hover-color, #DC2626);background:var(--cc-sf-uploaded-remove-hover-bg, #FEF2F2)}.uploaded-item .file-remove-btn:disabled{opacity:.4;cursor:not-allowed}.uploaded-item.uploading{background:var(--cc-sf-uploaded-uploading-bg, #F8FAFC);border-color:var(--cc-sf-uploaded-uploading-border, #CBD5E1);opacity:.85}.upload-spinner{width:20px;height:20px;flex-shrink:0;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}@keyframes cc-spin{to{transform:rotate(360deg)}}.uploading-label{font-style:italic}.input-group{align-items:stretch}.input-group .field-input{flex:1;width:auto}.input-prefix+.input-group .field-input{border-top-left-radius:0;border-bottom-left-radius:0}.input-group .field-input:has(+.input-suffix){border-top-right-radius:0;border-bottom-right-radius:0}.input-group .field-input.has-icon-right{padding-right:3rem}.input-group.readonly .field-input{cursor:default}.input-prefix,.input-suffix{display:flex!important;align-items:center;white-space:nowrap;padding:0 14px;background-color:var(--cc-sf-input-disabled-bg);border:1px solid var(--cc-sf-input-border);font-size:.875rem;color:var(--cc-sf-hint-color);-webkit-user-select:none;user-select:none}.input-prefix{border-right:none;border-top-left-radius:var(--cc-sf-input-radius, 8px);border-bottom-left-radius:var(--cc-sf-input-radius, 8px)}.input-suffix{border-left:none;border-top-right-radius:var(--cc-sf-input-radius, 8px);border-bottom-right-radius:var(--cc-sf-input-radius, 8px)}.readonly-icons{right:.875rem;top:50%;transform:translateY(-50%)}.readonly-icons mat-icon.lock-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem;opacity:.5;color:var(--cc-sf-hint-color, #6B7280)}.date-icon-wrapper{right:.5rem;top:50%;transform:translateY(-50%);pointer-events:auto}.date-icon-wrapper .mat-icon-button{width:32px;height:32px;line-height:32px}.subfields-group-wrapper .subfields-row{transition:all .2s ease}.subfields-group-wrapper .subfields-row.is-invalid .subfield-item ::ng-deep .field-input{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.subfields-group-wrapper .subfields-row .subfield-item{min-width:0}.subfields-group-wrapper .subfields-row .subfield-item ::ng-deep .field-label{font-size:.75rem!important;margin-bottom:4px!important;font-weight:500!important;color:var(--cc-sf-hint-color, #6B7280)!important}.subfields-group-wrapper .subfields-row .subfield-separator{font-weight:700}.autocomplete-wrapper .ac-input{padding-left:40px!important}.autocomplete-wrapper .ac-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1;transition:color var(--cc-sf-input-transition, .2s ease)}.autocomplete-wrapper .ac-clear-btn{right:.6rem;transition:color .15s ease,background .15s ease}.autocomplete-wrapper .ac-clear-btn mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.autocomplete-wrapper .ac-clear-btn:hover{color:var(--cc-sf-label-color, #374151);background:var(--cc-sf-input-disabled-bg, #F3F4F6)}.autocomplete-wrapper .ac-clear-btn:focus{outline:none}.autocomplete-wrapper:focus-within .ac-search-icon{color:var(--cc-sf-accent-color, #3B82F6)}.autocomplete-wrapper.is-invalid .ac-input{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)}.autocomplete-wrapper.readonly .ac-input{background-color:var(--cc-sf-input-disabled-bg);color:var(--cc-sf-input-disabled-color, #6B7280);cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important}.ac-no-results{font-style:italic}::ng-deep .mat-mdc-autocomplete-panel,::ng-deep .mat-autocomplete-panel{background:var(--cc-sf-input-bg, #ffffff)!important;border-radius:var(--cc-sf-input-radius, 9px)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important;box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f!important;padding:4px 0!important;min-width:200px}::ng-deep .mat-mdc-autocomplete-panel mat-option,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option,::ng-deep .mat-mdc-autocomplete-panel .mat-option,::ng-deep .mat-autocomplete-panel mat-option,::ng-deep .mat-autocomplete-panel .mat-mdc-option,::ng-deep .mat-autocomplete-panel .mat-option{background:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #111827)!important;font-size:.875rem!important;padding:10px 16px!important;min-height:40px!important;line-height:1.4!important;display:flex!important;flex-direction:column!important;align-items:flex-start!important;transition:background var(--cc-sf-input-transition, .2s ease)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]){background:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-option.mdc-list-item--selected{background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)!important;color:var(--cc-sf-selected-color, #6366F1)!important;font-weight:600!important}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-option-label{font-weight:500;color:var(--cc-sf-label-color, #111827);font-size:.875rem;display:block}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-option .ac-display-fields{align-items:center;line-height:1}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-item{display:inline-flex;align-items:center;font-size:.72rem;color:var(--cc-sf-hint-color, #6B7280);gap:3px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-chip{background:var(--cc-sf-input-disabled-bg, #F3F4F6);border-radius:4px;padding:2px 6px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-text{color:var(--cc-sf-hint-color, #6B7280)}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--cc-sf-input-border, #D1D5DB);vertical-align:middle}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-label{font-weight:600;color:var(--cc-sf-hint-color, #9CA3AF);margin-right:2px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-icon{font-size:11px;width:11px;height:11px;line-height:11px;color:var(--cc-sf-hint-color, #9CA3AF);flex-shrink:0}.mu-layout{grid-template-columns:1fr 1fr;gap:32px}@media(max-width:768px){.mu-layout{grid-template-columns:1fr}}.mu-title{font-weight:700;line-height:1.3}.mu-badge{white-space:nowrap;flex-shrink:0}.mu-description{line-height:1.6}.mu-feature-item .mu-check{width:16px;height:16px;line-height:16px;flex-shrink:0}.mu-right{min-height:260px}.mu-right-empty{min-height:250px;max-width:400px;box-shadow:0 2px 10px #0000000d;transition:box-shadow .2s ease}.mu-right-empty:hover{cursor:pointer;box-shadow:0 4px 16px #0000001a}.mu-right-empty .mu-right-empty-icon{width:52px;height:52px;line-height:52px;opacity:.3}.mu-right-empty p{margin:0;font-size:.85rem}.media-add-container{display:inline-block}.media-add-container ::ng-deep button{display:flex;align-items:center;gap:6px}.media-add-container ::ng-deep button .menu-chevron{width:18px;height:18px;line-height:18px;transition:transform .2s ease}.media-dropdown{top:calc(100% + 6px);left:0;z-index:200;min-width:240px;box-shadow:var(--mu-dropdown-shadow, 0 8px 32px rgba(0, 0, 0, .12));animation:mu-fade-in .15s ease}@keyframes mu-fade-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.media-dropdown-item{transition:background .15s ease}.media-dropdown-item:last-child{border-bottom:none}.media-dropdown-item:hover{background:var(--cc-sf-dropzone-hover-bg, #F0F9FF)}.media-drop-icon{width:36px;height:36px;flex-shrink:0}.media-drop-icon mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.media-drop-icon--video{background:var(--mu-icon-video-bg, #FFF0F0);color:var(--mu-icon-video-color, #EF4444)}.media-drop-icon--device{background:var(--mu-icon-device-bg, #EFF6FF);color:var(--mu-icon-device-color, #3B82F6)}.media-drop-icon--library{background:var(--mu-icon-library-bg, #F0FDF4);color:var(--mu-icon-library-color, #22C55E)}.media-drop-text{gap:2px}.youtube-input-panel{animation:mu-fade-in .18s ease}.youtube-panel-label mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:var(--mu-icon-video-color, #EF4444)}.youtube-input-row{align-items:stretch}.media-menu-backdrop{position:fixed;inset:0;z-index:100}.media-upload-status{animation:mu-fade-in .2s ease}.media-upload-status .status-icon{width:18px;height:18px;line-height:18px}.media-carousel-main{max-width:400px;height:var(--mu-carousel-height, 250px)}.carousel-viewer{inset:0}.carousel-viewer .carousel-image{object-fit:cover}.carousel-viewer .carousel-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.carousel-nav{top:50%;transform:translateY(-50%);z-index:10;width:40px;height:40px;box-shadow:0 2px 8px #0003;transition:background .2s ease,opacity .2s ease;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.carousel-nav mat-icon{font-size:22px;width:22px;height:22px;line-height:22px;color:#1e293b}.carousel-nav:hover:not(:disabled){background:#fff}.carousel-nav:disabled{opacity:.3;cursor:not-allowed}.carousel-nav--prev{left:12px}.carousel-nav--next{right:12px}.carousel-remove-btn{top:10px;right:10px;z-index:10;width:32px;height:32px;transition:background .2s ease}.carousel-remove-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:#fff}.carousel-remove-btn:hover:not(:disabled){background:#dc2626d9}.carousel-remove-btn:disabled{opacity:.4;cursor:not-allowed}.carousel-dots{bottom:10px;left:50%;transform:translate(-50%);z-index:10}.carousel-dot{width:8px;height:8px;transition:background .2s ease,transform .2s ease}.carousel-dot.active{background:#fff;transform:scale(1.3)}.media-thumbnail-strip{max-width:400px;overflow-x:auto}.media-thumbnail-strip::-webkit-scrollbar{height:4px}.media-thumbnail-strip::-webkit-scrollbar-thumb{background:var(--cc-sf-input-disabled-border, #D1D5DB);border-radius:2px}.media-thumb{flex-shrink:0;width:72px;height:52px;transition:border-color .2s ease,transform .15s ease}.media-thumb.active{border-color:var(--mu-thumb-active-border, var(--cc-sf-accent-color, #3B82F6));transform:scale(1.04)}.media-thumb:hover:not(.active){border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.media-thumb .thumb-img{object-fit:cover}.media-thumb .thumb-yt-placeholder mat-icon{font-size:28px;width:28px;height:28px;line-height:28px}.media-thumb .thumb-uploading .thumb-spinner{width:20px;height:20px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.media-library-portal-host{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;visibility:hidden;opacity:0;pointer-events:none;transition:opacity .2s ease,visibility .2s ease}.media-library-portal-host.is-open{visibility:visible;opacity:1;pointer-events:auto}.media-library-portal-host.is-open .media-library-modal{transform:scale(1) translateY(0)}.media-library-overlay{position:absolute;inset:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.media-library-modal{position:relative;z-index:10000;width:90vw;max-width:900px;max-height:90vh;background:#fff;border-radius:16px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transform:scale(.95) translateY(10px);transition:transform .3s cubic-bezier(.175,.885,.32,1.275)}.library-modal-header{flex-shrink:0}.library-modal-title{font-weight:700}.library-modal-subtitle{line-height:1.5;max-width:600px}.library-close-btn{width:32px;height:32px;transition:background .15s ease,color .15s ease}.library-close-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-close-btn:hover{background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-label-color, #374151)}.library-loading mat-icon,.library-empty mat-icon{font-size:40px;width:40px;height:40px;line-height:40px;opacity:.5}.lib-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.library-error{flex-shrink:0}.library-error mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-grid{grid-template-columns:repeat(5,1fr);max-height:50vh;overflow-y:auto}.library-grid::-webkit-scrollbar{width:8px}.library-grid::-webkit-scrollbar-track{background:#f1f1f1}.library-grid::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:10px;border:2px solid #F1F1F1}.library-grid-item{aspect-ratio:1/1;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.library-grid-item:hover{transform:translateY(-4px) scale(1.02);box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}.library-grid-item.selected{border-color:var(--cc-sf-accent-color, #3B82F6)}.library-grid-item.selected .library-grid-img{opacity:.7}.library-grid-item:hover .library-overlay-hover{opacity:1}.library-grid-img{object-fit:cover}.library-overlay-hover{inset:0;opacity:0;transition:opacity .15s ease}.library-check{top:6px;right:6px;width:22px;height:22px;box-shadow:0 1px 4px #00000026}.library-check mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.library-modal-footer{flex-shrink:0}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary{background-color:var(--cc-sf-accent-color, #3B82F6)!important;border-color:var(--cc-sf-accent-color, #3B82F6)!important;color:#fff!important;font-weight:600;padding-left:32px;padding-right:32px}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:hover{background-color:var(--cc-sf-btn-primary-hover-bg, #2563EB)!important}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:disabled{background-color:#93c5fd!important;cursor:not-allowed}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline{font-weight:600;padding-left:24px;padding-right:24px;border-color:#d1d5db;color:#374151}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline:hover{background-color:#f9fafb}.location-subtitle{line-height:1.5}.loc-tab-btn ::ng-deep button{width:100%}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning){background-color:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #000000)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning):hover{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.loc-venue-item{transition:box-shadow .15s ease,border-color .15s ease}.loc-venue-item:hover{box-shadow:0 2px 8px #0000000f;border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.loc-venue-search-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-venue-text{white-space:nowrap;text-overflow:ellipsis}.loc-action-btn{transition:background .15s ease,color .15s ease;flex-shrink:0}.loc-action-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.loc-action-btn.loc-delete-btn{color:var(--loc-delete-color, #E53E3E)}.loc-action-btn.loc-delete-btn:hover{background:var(--cc-sf-error-bg, #FEF2F2)}.loc-action-btn.loc-edit-btn{color:var(--cc-sf-hint-color, #9CA3AF)}.loc-action-btn.loc-edit-btn:hover{color:var(--cc-sf-input-focus-border, #3B82F6);background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)}.loc-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1}.loc-suggestions-panel{top:calc(100% + 4px);left:0;right:0;z-index:300;box-shadow:0 8px 24px #0000001a;animation:mu-fade-in .15s ease;max-height:260px;overflow-y:auto}.loc-suggestion-item{transition:background .12s ease}.loc-suggestion-item:hover,.loc-suggestion-item:focus{background:var(--loc-suggestion-hover-bg, #F0F9FF)}.loc-suggestion-item:not(:last-child){border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)}.loc-suggestion-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-suggestion-text{white-space:nowrap;text-overflow:ellipsis}.loc-add-btn{transition:opacity .15s ease}.loc-add-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.loc-add-btn:hover{opacity:.8}.loc-map-container{box-shadow:0 2px 10px #0000000f}.loc-tba-panel{min-height:120px}.loc-tba-icon{width:40px;height:40px;line-height:40px;opacity:.6}.loc-tba-text{line-height:1.6;max-width:360px}.radio-label{display:flex!important}.radio-label .option-content{padding-left:10px}\n"], dependencies: [{ 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$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i5.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i6.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i6.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i6.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i9.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }, { kind: "component", type: i11.QuillEditorComponent, selector: "quill-editor" }, { kind: "component", type: FormFieldComponent, selector: "lib-form-field", inputs: ["config", "controller", "formGroup", "allowMulti"] }, { kind: "pipe", type: TrustedUrlPipe, name: "trustedUrl" }] });
|
|
2345
2388
|
}
|
|
2346
2389
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldComponent, decorators: [{
|
|
2347
2390
|
type: Component,
|
|
2348
|
-
args: [{ selector: 'lib-form-field', standalone: false, template: "<div class=\"form-field mb-16px\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\r\n\r\n <!-- \u2550\u2550 ROW Layout \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRow\" class=\"form-row grid-row\">\r\n <ng-container *ngFor=\"let child of config.children\">\r\n <div class=\"row-field\" [style.gridColumn]=\"'span ' + getChildColSpan(child)\" *ngIf=\"child.isEnabled !== false\">\r\n <lib-form-field [config]=\"child\" [controller]=\"controller\" [formGroup]=\"formGroup\" [allowMulti]=\"allowMulti\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- \u2550\u2550 GROUP \u2014 allowMulti (repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig?.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\"\r\n [class.multi-save-active]=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n\r\n <!-- Multi-Save: header row with label + top-right Add button -->\r\n <div class=\"multi-save-header d-flex justify-content-between align-items-center mb-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig?.label\">{{ config.sectionConfig!.label }}</h3>\r\n <lib-button [variant]=\"'outline'\" [icon]=\"{type: 'material', value: 'add'}\" (click)=\"addGroupInstance()\"\r\n class=\"btn-add-multi\">\r\n {{ addMultiLabel }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- Standard heading (non-multiSave) -->\r\n <h3 class=\"group-label\"\r\n *ngIf=\"config.sectionConfig?.label && !config.sectionConfig?.multiSaveConfig?.active\">{{\r\n config.sectionConfig!.label }}</h3>\r\n\r\n <!-- \u2500\u2500 Standard (non-multiSave) repeater: accordion instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <ng-container *ngIf=\"!config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-accordion-instance\"\r\n [class.is-expanded]=\"isGroupExpanded(i)\">\r\n\r\n <!-- Accordion header -->\r\n <div class=\"group-accordion-header\" (click)=\"toggleGroupAccordion(i)\"\r\n role=\"button\" [attr.aria-expanded]=\"isGroupExpanded(i)\">\r\n <div class=\"accordion-header-left d-flex align-items-center gap-10\">\r\n <span class=\"instance-badge\">{{ i + 1 }}</span>\r\n <span class=\"instance-title\">{{ config.sectionConfig!.label }} #{{ i + 1 }}</span>\r\n </div>\r\n <div class=\"accordion-header-right d-flex align-items-center gap-6\">\r\n <button type=\"button\" class=\"accordion-remove-btn\"\r\n *ngIf=\"instanceList.length > 1\"\r\n (click)=\"$event.stopPropagation(); removeGroupInstance(i)\"\r\n aria-label=\"Remove\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n <mat-icon class=\"accordion-chevron\">\r\n {{ isGroupExpanded(i) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <!-- Accordion body (always mounted so form controls survive collapse) -->\r\n <div class=\"group-accordion-body\" [hidden]=\"!isGroupExpanded(i)\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\">\r\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"true\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Full-width dashed Add button -->\r\n <button type=\"button\" class=\"btn-add-group\" (click)=\"addGroupInstance()\">\r\n <mat-icon>add</mat-icon> {{ addLabel }} {{ config.sectionConfig!.label }}\r\n </button>\r\n </ng-container>\r\n\r\n <!-- \u2500\u2500 MultiSave: card instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <ng-container *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-instance position-relative mb-16 overflow-hidden\"\r\n [class.is-editing]=\"instance.isEditing\"\r\n [class.is-card]=\"!instance.isEditing\">\r\n\r\n <!-- Edit / new form view -->\r\n <div [hidden]=\"!instance.isEditing\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\">\r\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"true\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Save / Cancel -->\r\n <div class=\"group-footer d-flex justify-content-end align-items-center gap-16 p-0-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"multiSaveError\">{{ multiSaveError }}</span>\r\n <div class=\"footer-actions d-flex gap-12\">\r\n <lib-button [variant]=\"'outline'\" (click)=\"cancelGroupInstance(i)\">Cancel</lib-button>\r\n <lib-button [variant]=\"'primary'\" (click)=\"saveGroupInstance(i)\">Save</lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Card view (saved state) -->\r\n <ng-container *ngIf=\"!instance.isEditing\">\r\n <div class=\"card-view d-flex justify-content-between align-items-center p-18px-24px\"\r\n [class.is-expanded]=\"instance.isExpanded\">\r\n <div class=\"card-content flex-1 d-flex flex-column gap-4 overflow-hidden\">\r\n <span class=\"card-title font-weight-600 overflow-hidden fs-1rem c-111827\">{{\r\n instance.fg.get(config.sectionConfig!.multiSaveConfig!.summaryField || '')?.value\r\n || '\u2014' }}</span>\r\n </div>\r\n <div class=\"card-actions d-flex align-items-center gap-16 ml-20\">\r\n <mat-icon class=\"icon-delete\" (click)=\"removeGroupInstance(i, true)\">delete_outline</mat-icon>\r\n <mat-icon class=\"icon-edit\" (click)=\"editGroupInstance(i)\">edit_outline</mat-icon>\r\n <mat-icon class=\"icon-expand\" (click)=\"toggleExpandGroupInstance(i)\">\r\n {{ instance.isExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- \u2550\u2550 GROUP \u2014 single (non-repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig && !config.sectionConfig.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig.label\">{{ config.sectionConfig.label }}</h3>\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\" *ngIf=\"field.isEnabled !== false\">\r\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"groupFormGroup\" [allowMulti]=\"false\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n\r\n <!-- \u2550\u2550 Text Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isTextField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <textarea *ngIf=\"config.subType === 'LONG'\" class=\"field-input textarea\" [placeholder]=\"config.placeholder || ''\"\r\n [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\" rows=\"4\">\r\n </textarea>\r\n\r\n <!-- Password input with show/hide toggle -->\r\n <div *ngIf=\"config.subType === 'PASSWORD'\" class=\"password-wrapper position-relative d-flex align-items-center\">\r\n <input [type]=\"showPassword ? 'text' : 'password'\" class=\"field-input password-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <button type=\"button\"\r\n class=\"password-toggle position-absolute cursor-pointer d-flex align-items-center justify-content-center b-none border-none c-6B7280 p-0-25rem\"\r\n (click)=\"showPassword = !showPassword\" tabindex=\"-1\"\r\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\r\n <mat-icon class=\"eye-icon\">{{ showPassword ? 'visibility' : 'visibility_off' }}</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input *ngIf=\"config.subType !== 'LONG' && config.subType !== 'PASSWORD'\"\r\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\" class=\"field-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\"\r\n [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Number Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isNumberField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input type=\"number\" class=\"field-input\" [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\"\r\n [min]=\"config.numberConfig?.min ?? null\" [max]=\"config.numberConfig?.max ?? null\"\r\n [step]=\"config.numberConfig?.step || 1\" [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Date Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isDateField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input matInput [matDatepicker]=\"datePicker\" class=\"field-input date-input has-icon-right\"\r\n [formControlName]=\"config.name!\" [min]=\"config.dateConfig?.minDate\" [max]=\"config.dateConfig?.maxDate\"\r\n [class.is-invalid]=\"errorMessage\" [placeholder]=\"config.placeholder || ''\" [readonly]=\"config.readonly\"\r\n (click)=\"!config.readonly && datePicker.open()\">\r\n <div class=\"date-icon-wrapper position-absolute d-flex align-items-center justify-content-center\"\r\n *ngIf=\"!config.readonly\">\r\n <mat-datepicker-toggle matSuffix [for]=\"datePicker\"></mat-datepicker-toggle>\r\n </div>\r\n <mat-datepicker #datePicker></mat-datepicker>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Time Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isTimeField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input type=\"time\" class=\"field-input time-input\" [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\" [readonly]=\"!!config.readonly\">\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Autocomplete \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isAutocomplete\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Hidden real control (stores the code value) -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <div class=\"autocomplete-wrapper position-relative d-flex align-items-center w-100\"\r\n [class.is-invalid]=\"errorMessage\" [class.readonly]=\"config.readonly\">\r\n <!-- Search icon -->\r\n <mat-icon class=\"ac-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n\r\n <input class=\"field-input ac-input\" [formControl]=\"autocompleteInputCtrl\" [matAutocomplete]=\"auto\"\r\n [placeholder]=\"config.placeholder || 'Search\u2026'\" [readonly]=\"!!config.readonly\" [class.is-invalid]=\"errorMessage\"\r\n (blur)=\"onAutocompleteClear()\" autocomplete=\"off\">\r\n\r\n <!-- Clear button -->\r\n <button type=\"button\"\r\n class=\"ac-clear-btn position-absolute d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none c-9CA3AF p-0-2rem\"\r\n *ngIf=\"autocompleteInputCtrl.value && !config.readonly\"\r\n (click)=\"autocompleteInputCtrl.setValue(''); updateValue(null)\" tabindex=\"-1\" aria-label=\"Clear\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n\r\n <mat-autocomplete #auto=\"matAutocomplete\" [panelWidth]=\"'auto'\">\r\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.label\"\r\n (onSelectionChange)=\"onAutocompleteSelected(option)\">\r\n <span class=\"ac-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Dynamic display fields (image / email / phone / text) -->\r\n <div class=\"ac-display-fields d-flex flex-wrap gap-6 mt-2\" *ngIf=\"option['displayMeta']?.length\">\r\n <ng-container *ngFor=\"let field of option['displayMeta']\">\r\n\r\n <!-- Image avatar -->\r\n <span *ngIf=\"field.type === 'image' && field.value\" class=\"ac-df-item ac-df-image\">\r\n <img [src]=\"field.value\" [alt]=\"field.label || 'image'\" class=\"ac-df-avatar\">\r\n </span>\r\n\r\n <!-- Email -->\r\n <span *ngIf=\"field.type === 'email' && field.value\" class=\"ac-df-item ac-df-chip\">\r\n <mat-icon class=\"ac-df-icon\">mail_outline</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Phone -->\r\n <span *ngIf=\"field.type === 'phone' && field.value\" class=\"ac-df-item ac-df-chip\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\">phone</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Custom / Icon-based / Generic Text -->\r\n <span *ngIf=\"field.type !== 'image' && field.type !== 'email' && field.type !== 'phone' && field.value\" \r\n class=\"ac-df-item\" [class.ac-df-chip]=\"!!field.icon\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\" *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n </ng-container>\r\n </div>\r\n </mat-option>\r\n <mat-option *ngIf=\"filteredOptions.length === 0\" disabled class=\"ac-no-results fs-0-8125rem c-6B7280\">\r\n No results found\r\n </mat-option>\r\n </mat-autocomplete>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Dropdown \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isDropdown\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <select *ngIf=\"config.subType === 'SINGLE'\" class=\"field-input\" [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\">\r\n <option [ngValue]=\"null\" disabled selected>{{ config.placeholder || 'Select' }}</option>\r\n <option *ngFor=\"let option of config.optionConfig?.optionList\" [value]=\"option.code\">\r\n {{ option.label }}\r\n </option>\r\n </select>\r\n\r\n <select *ngIf=\"config.subType === 'MULTIPLE'\" class=\"field-input\" multiple [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\">\r\n <option *ngFor=\"let option of config.optionConfig?.optionList\" [value]=\"option.code\">\r\n {{ option.label }}\r\n </option>\r\n </select>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Radio \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRadio\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"radio-group\" [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\" class=\"radio-label\"\r\n [class.card-item]=\"config.subType === 'CARD'\"\r\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"radio\" [formControlName]=\"config.name!\" [value]=\"option.code\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Checkbox \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label && config.subType === 'LIST'\"\r\n class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\r\n <label class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span>{{ config.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <div *ngIf=\"config.subType === 'LIST' || config.subType === 'CARD'\" class=\"checkbox-group d-flex flex-column gap-8\"\r\n [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\r\n class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\"\r\n [class.card-item]=\"config.subType === 'CARD'\" [class.selected]=\"isChecked(option.code)\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Chip \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isChip\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"chip-group d-flex flex-wrap gap-8\" [class.is-invalid]=\"errorMessage\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\r\n class=\"chip-label cursor-pointer p-6px-14px b-ffffff c-374151 b-1px-solid-D1D5DB br-20px fs-0-875rem\"\r\n [class.selected]=\"isChecked(option.code)\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\" hidden>\r\n <span>{{ option.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Switch \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isSwitch\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label class=\"switch-container d-flex justify-content-between align-items-center cursor-pointer\">\r\n <span class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label }}</span>\r\n <div class=\"switch position-relative\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span class=\"slider position-absolute cursor-pointer\"></span>\r\n </div>\r\n </label>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Rich Text \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRichText\" class=\"field-wrapper component-rich-text d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rich-text-container\" [class.is-invalid]=\"errorMessage\">\r\n <quill-editor [formControlName]=\"config.name!\" class=\"rich-text-editor d-block w-100\"\r\n [placeholder]=\"config.richTextConfig?.placeholder || config.placeholder || ''\"\r\n [styles]=\"{height: config.richTextConfig?.height || '200px'}\">\r\n </quill-editor>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Rating \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRating\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rating-group d-flex gap-4\" [class.is-invalid]=\"errorMessage\">\r\n <span *ngFor=\"let star of getStarArray()\" class=\"star d-inline-flex align-items-center cursor-pointer\"\r\n [class.filled]=\"isStarFilled(star)\" [class.half]=\"isStarHalf(star)\" (click)=\"onRatingChange(star, $event)\">\r\n <mat-icon>{{ isStarFilled(star) || isStarHalf(star) ? 'star' : 'star_border' }}</mat-icon>\r\n </span>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Generated Field (read-only) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isGenerated\" class=\"field-wrapper d-flex flex-column gap-6\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label\r\n }}</label>\r\n <div class=\"generated-value fs-0-875rem p-0-625rem-0-875rem b-F3F4F6 b-1px-solid-E5E7EB br-8px c-6B7280\">{{ value ||\r\n '-' }}</div>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint\">{{ config.hint }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 File Upload \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isFileUpload\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Drop Zone -->\r\n <div\r\n class=\"upload-drop-zone d-flex flex-column align-items-center justify-content-center gap-8 cursor-pointer text-center p-32px-24px us-none\"\r\n [class.drag-over]=\"isDragOver\" [class.has-files]=\"value?.length\" [class.is-invalid]=\"errorMessage\"\r\n (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onFileDrop($event)\"\r\n (click)=\"fileInput.click()\">\r\n\r\n <!-- Icon with accent-colour pill background -->\r\n <div class=\"upload-icon-wrap mb-4\">\r\n <div class=\"dropzone-icon-pill d-flex align-items-center justify-content-center\">\r\n <mat-icon class=\"upload-cloud-icon\">cloud_upload</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <p class=\"upload-main-text font-weight-600 m-0 fs-0-9rem\">Drag & drop files here</p>\r\n <p class=\"upload-sub-text m-0 fs-0-8rem c-64748B\">or <span class=\"upload-link\">Browse files</span></p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"config.attachmentConfig?.acceptLabel\">\r\n Supported: <span class=\"upload-formats font-weight-500\">{{ config.attachmentConfig!.acceptLabel }}</span>\r\n </p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"!config.attachmentConfig?.acceptLabel && config.hint\">\r\n {{ config.hint }}\r\n </p>\r\n <span class=\"upload-size-badge fs-0-72rem\" *ngIf=\"config.attachmentConfig?.maxSizeMB\">\r\n Max {{ config.attachmentConfig!.maxSizeMB }}MB\r\n </span>\r\n\r\n <!-- Hidden native file input -->\r\n <input #fileInput type=\"file\" hidden [attr.multiple]=\"config.attachmentConfig?.multiple ? true : null\"\r\n [attr.accept]=\"config.attachmentConfig?.accept || null\" (change)=\"onFileSelected($event)\">\r\n </div>\r\n\r\n <!-- Uploaded file list -->\r\n <div class=\"uploaded-list d-flex flex-column gap-8 mt-10\" *ngIf=\"value?.length\">\r\n <div *ngFor=\"let f of value; let i = index\"\r\n class=\"uploaded-item d-flex align-items-center gap-10 p-10px-14px br-8px\"\r\n [class.uploading]=\"f.isUploading\">\r\n\r\n <!-- Uploading spinner -->\r\n <ng-container *ngIf=\"f.isUploading; else fileReady\">\r\n <div class=\"upload-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size uploading-label fs-0-72rem\">Uploading...</span>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Normal state once upload is done -->\r\n <ng-template #fileReady>\r\n <mat-icon class=\"file-type-icon\">{{ getFileIcon(f.type) }}</mat-icon>\r\n <img *ngIf=\"f.type?.startsWith('image') && f.dataUrl\" [src]=\"f.dataUrl\" class=\"file-thumb rounded-4\"\r\n alt=\"preview\">\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size fs-0-72rem\">{{ formatFileSize(f.size) }}</span>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Compact icon-only remove button -->\r\n <button type=\"button\" class=\"file-remove-btn d-flex align-items-center justify-content-center rounded-50\"\r\n [disabled]=\"f.isUploading\" (click)=\"!f.isUploading && removeUploadedFile(i)\"\r\n aria-label=\"Remove file\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Validation / file errors -->\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"fileUploadError\">{{ fileUploadError }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage && !fileUploadError\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\"\r\n *ngIf=\"config.hint && !errorMessage && !fileUploadError && !config.attachmentConfig?.acceptLabel\">\r\n {{ config.hint }}\r\n </span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Media Upload (Type 2) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isMediaUpload\" class=\"field-wrapper media-upload-wrapper d-flex flex-column gap-6 p-0 border-none b-none\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Hidden file input lives outside *ngIf \u2014 triggered via ViewChild -->\r\n <input #mediaDeviceInput type=\"file\" hidden multiple accept=\"image/*\" (change)=\"onMediaFileSelected($event)\">\r\n\r\n <!-- Two-column layout -->\r\n <div class=\"mu-layout d-grid align-items-start\">\r\n\r\n <!-- \u2500\u2500 LEFT PANEL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"mu-left d-flex flex-column gap-20\">\r\n\r\n <!-- Header: title + max-items badge -->\r\n <div class=\"mu-header d-flex align-items-start flex-wrap gap-10\">\r\n <h3 class=\"mu-title m-0 c-111827 fs-1-35rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </h3>\r\n <span\r\n class=\"mu-badge d-inline-flex align-items-center rounded-20 color-white font-weight-600 fs-0-72rem p-4px-12px b-111827\"\r\n *ngIf=\"config.attachmentConfig?.maxFiles\">\r\n {{ controller.labels['LBL_MEDIA_MAX_PREFIX'] || 'Maximum' }}\r\n {{ config.attachmentConfig?.maxFiles }}\r\n {{ controller.labels['LBL_MEDIA_MAX_SUFFIX'] || 'Image & Videos' }}\r\n </span>\r\n </div>\r\n\r\n <!-- Description -->\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\" *ngIf=\"config.attachmentConfig?.description\">\r\n {{ config.attachmentConfig?.description }}\r\n </p>\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\"\r\n *ngIf=\"!config.attachmentConfig?.description && controller.labels['LBL_MEDIA_DESC']\">\r\n {{ controller.labels['LBL_MEDIA_DESC'] }}\r\n </p>\r\n\r\n <!-- Feature bullet list -->\r\n <ul class=\"mu-features m-0 p-0 d-flex flex-column gap-8 ls-none\"\r\n *ngIf=\"config.attachmentConfig?.features?.length || controller.labels['LBL_MEDIA_FEATURE_1']\">\r\n <ng-container *ngIf=\"config.attachmentConfig?.features?.length\">\r\n <li *ngFor=\"let f of config.attachmentConfig?.features\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ f }}\r\n </li>\r\n </ng-container>\r\n <ng-container *ngIf=\"!config.attachmentConfig?.features?.length\">\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_1']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_1'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_2']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_2'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_3']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_3'] }}\r\n </li>\r\n </ng-container>\r\n </ul>\r\n\r\n <!-- Backdrop to close dropdown on outside click -->\r\n <div class=\"media-menu-backdrop\" *ngIf=\"showMediaMenu\"\r\n (click)=\"$event.stopPropagation(); showMediaMenu = false\"></div>\r\n\r\n <!-- Add Media button + dropdown -->\r\n <div class=\"media-add-container position-relative\" (click)=\"showMediaMenu = !showMediaMenu\">\r\n <lib-button id=\"btn-add-media-{{ config.name }}\" [variant]=\"'warning'\"\r\n [icon]=\"{type: 'material', value: 'add_photo_alternate'}\">\r\n {{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}\r\n <mat-icon class=\"menu-chevron fs-18px\">add</mat-icon>\r\n </lib-button>\r\n\r\n <div class=\"media-dropdown position-absolute rounded-12 overflow-hidden b-ffffff b-1px-solid-E5E7EB\"\r\n *ngIf=\"showMediaMenu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\r\n <!-- Video -->\r\n <button id=\"btn-media-video-{{ config.name }}\" type=\"button\"\r\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\r\n (click)=\"onMediaMenuVideo(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\n class=\"media-drop-icon media-drop-icon--video d-flex align-items-center justify-content-center rounded-8\"><mat-icon>videocam</mat-icon></span>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_VIDEO'] || 'Video' }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_VIDEO_DESC'] || 'Add\r\n YouTube URL'\r\n }}</span>\r\n </span>\r\n </button>\r\n <!-- Device -->\r\n <button id=\"btn-media-device-{{ config.name }}\" type=\"button\"\r\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\r\n (click)=\"onMediaMenuDevice(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\n class=\"media-drop-icon media-drop-icon--device d-flex align-items-center justify-content-center rounded-8\"><mat-icon>upload</mat-icon></span>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_DEVICE'] || 'Upload from device'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_DEVICE_DESC'] ||\r\n 'Select images from your\r\n computer' }}</span>\r\n </span>\r\n </button>\r\n <!-- Library -->\r\n <button id=\"btn-media-library-{{ config.name }}\" type=\"button\"\r\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\r\n (click)=\"onMediaMenuLibrary(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\n class=\"media-drop-icon media-drop-icon--library d-flex align-items-center justify-content-center rounded-8\"><mat-icon>photo_library</mat-icon></span>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_LIBRARY'] || 'Upload from library'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_LIBRARY_DESC'] ||\r\n 'Choose from default\r\n images' }}</span>\r\n </span>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- YouTube URL Input (inline below button) -->\r\n <div class=\"youtube-input-panel d-flex flex-column gap-8 p-16 rounded-10 b-FFFAF1 b-1px-solid-E5E7EB\"\r\n *ngIf=\"showYoutubeInput\">\r\n <label class=\"youtube-panel-label d-flex align-items-center gap-6 font-weight-600 fs-0-875rem c-111827\">\r\n {{ controller.labels['LBL_YOUTUBE_URL'] || 'Video URL' }}\r\n </label>\r\n <div class=\"youtube-input-row d-flex gap-8\">\r\n <input id=\"input-youtube-url-{{ config.name }}\" type=\"url\" class=\"field-input youtube-url-input\"\r\n [(ngModel)]=\"youtubeUrlInput\" [ngModelOptions]=\"{standalone: true}\"\r\n [placeholder]=\"controller.labels['PH_YOUTUBE_URL'] || 'Video URL'\" (keyup.enter)=\"addYoutubeMedia()\">\r\n <lib-button id=\"btn-add-youtube-{{ config.name }}\" [variant]=\"'secondary'\" (click)=\"addYoutubeMedia()\">\r\n {{ controller.labels['LBL_ADD'] || 'Add' }}\r\n </lib-button>\r\n </div>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"youtubeUrlError\">{{ youtubeUrlError }}</span>\r\n </div>\r\n\r\n <div\r\n class=\"media-upload-status d-flex align-items-center gap-8 mt-4 color-error rounded-8 font-weight-500 p-10px-14px b-FEF2F2 fs-0-85rem\"\r\n *ngIf=\"mediaUploadError\">\r\n <mat-icon class=\"status-icon fs-18px\">error_outline</mat-icon>\r\n <span>{{ mediaUploadError }}</span>\r\n </div>\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n </div>\r\n <!-- end left panel -->\r\n\r\n <!-- \u2500\u2500 RIGHT PANEL (carousel) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"mu-right d-flex flex-column gap-12\">\r\n\r\n <!-- Carousel (when items exist) -->\r\n <div class=\"media-carousel-section d-flex flex-column gap-12\" *ngIf=\"mediaItems.length\">\r\n <div\r\n class=\"media-carousel-main position-relative w-100 overflow-hidden d-flex align-items-center justify-content-center br-12px b-0F172A\">\r\n <button id=\"btn-carousel-prev-{{ config.name }}\" type=\"button\"\r\n class=\"carousel-nav carousel-nav--prev position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\r\n (click)=\"mediaCarouselPrev()\" [disabled]=\"mediaCarouselIndex === 0\" aria-label=\"Previous\">\r\n <mat-icon>chevron_left</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-viewer position-absolute d-flex align-items-center justify-content-center\"\r\n *ngFor=\"let item of mediaItems; let i = index\" [hidden]=\"i !== mediaCarouselIndex\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"carousel-uploading d-flex flex-column align-items-center gap-12 c-94A3B8 fs-0-85rem\">\r\n <div class=\"carousel-spinner rounded-50 b-3px-solid-rgba-255-255-255-0-15\"></div>\r\n <span>{{ controller.labels['LBL_UPLOADING'] || 'Uploading\u2026' }}</span>\r\n </div>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'youtube'\">\r\n <iframe class=\"carousel-iframe w-100 h-100 br-12px\" [src]=\"item.url | trustedUrl\" frameborder=\"0\"\r\n allowfullscreen\r\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\">\r\n </iframe>\r\n </ng-container>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'image'\">\r\n <img class=\"carousel-image w-100 h-100 br-12px\" [src]=\"item.url\" alt=\"Media\">\r\n </ng-container>\r\n <button id=\"btn-remove-media-{{ config.name }}-{{ i }}\" type=\"button\"\r\n class=\"carousel-remove-btn position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-0-0-0-0-55\"\r\n [disabled]=\"item.isUploading\" (click)=\"removeMediaItem(i)\" aria-label=\"Remove\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <button id=\"btn-carousel-next-{{ config.name }}\" type=\"button\"\r\n class=\"carousel-nav carousel-nav--next position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\r\n (click)=\"mediaCarouselNext()\" [disabled]=\"mediaCarouselIndex === mediaItems.length - 1\" aria-label=\"Next\">\r\n <mat-icon>chevron_right</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-dots position-absolute d-flex gap-6\">\r\n <span *ngFor=\"let item of mediaItems; let i = index\"\r\n class=\"carousel-dot rounded-50 cursor-pointer b-rgba-255-255-255-0-45\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\"></span>\r\n </div>\r\n </div>\r\n\r\n <!-- Thumbnail strip -->\r\n <div class=\"media-thumbnail-strip d-flex flex-wrap gap-8 pb-4px\">\r\n <div *ngFor=\"let item of mediaThumbnails; let i = index\"\r\n class=\"media-thumb rounded-8 overflow-hidden cursor-pointer d-flex align-items-center justify-content-center b-2px-solid-transparent b-E2E8F0\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"thumb-uploading d-flex align-items-center justify-content-center w-100 h-100\">\r\n <div class=\"thumb-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && item.thumbnailUrl\"\r\n [src]=\"item.thumbnailUrl\" class=\"thumb-img w-100 h-100\" alt=\"Video thumbnail\">\r\n <div *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && !item.thumbnailUrl\"\r\n class=\"thumb-yt-placeholder d-flex align-items-center justify-content-center w-100 h-100 b-1E293B c-EF4444\">\r\n <mat-icon>play_circle</mat-icon>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'image' && item.url\" [src]=\"item.url\"\r\n class=\"thumb-img w-100 h-100\" alt=\"Image thumbnail\">\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty right-side placeholder -->\r\n <div\r\n class=\"mu-right-empty d-flex flex-column align-items-center justify-content-center gap-10 h-100 text-center p-24 br-12px b-FFFAF1 c-94A3B8 b-2px-dashed-CBD5E1\"\r\n *ngIf=\"!mediaItems.length\" (click)=\"onMediaMenuDevice()\">\r\n <mat-icon class=\"mu-right-empty-icon fs-52px\">perm_media</mat-icon>\r\n <p>{{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}</p>\r\n </div>\r\n\r\n </div>\r\n <!-- end right panel -->\r\n\r\n </div><!-- end mu-layout -->\r\n </div>\r\n\r\n\r\n <!-- \u2550\u2550 Library Image Picker Modal \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <!-- Wrapper is always in DOM (hidden) so @ViewChild can move it to body -->\r\n <div #libraryModal class=\"media-library-portal-host\" [class.is-open]=\"showLibraryModal\">\r\n\r\n <!-- Backdrop -->\r\n <div class=\"media-library-overlay\" (click)=\"closeLibraryModal()\"></div>\r\n\r\n <!-- Modal card -->\r\n <div class=\"media-library-modal d-flex flex-column overflow-hidden b-ffffff br-16px\"\r\n role=\"dialog\" aria-modal=\"true\">\r\n <div class=\"library-modal-header d-flex align-items-start justify-content-between p-24px-28px bb-1px-solid-E5E7EB\">\r\n <div class=\"header-left d-flex flex-column gap-8\">\r\n <h3 class=\"library-modal-title m-0 color-dark fs-1-25rem\">\r\n {{ controller.labels['LBL_ADD_IMAGES'] || 'Add Images' }}\r\n </h3>\r\n <p class=\"library-modal-subtitle m-0 color-gray fs-0-85rem\">\r\n {{ controller.labels['LBL_LIBRARY_MODAL_DESC'] || 'Select images from your library.' }}\r\n </p>\r\n </div>\r\n <button id=\"btn-close-library-{{ config.name }}\" type=\"button\"\r\n class=\"library-close-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 border-none b-none c-9CA3AF\"\r\n (click)=\"closeLibraryModal()\" aria-label=\"Close\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Loading -->\r\n <div class=\"library-loading\" *ngIf=\"libraryLoading\">\r\n <div class=\"lib-spinner rounded-50 b-3px-solid-E2E8F0\"></div>\r\n <span>{{ controller.labels['LBL_LOADING'] || 'Loading\u2026' }}</span>\r\n </div>\r\n\r\n <!-- Error -->\r\n <div class=\"library-error d-flex align-items-center gap-8 color-error b-FEF2F2 fs-0-875rem p-16px-24px\"\r\n *ngIf=\"libraryError && !libraryLoading\">\r\n <mat-icon>error_outline</mat-icon>\r\n {{ libraryError }}\r\n </div>\r\n\r\n <!-- Image grid -->\r\n <div class=\"library-grid d-grid gap-16 flex-1 p-28px b-F9FAFB\" *ngIf=\"!libraryLoading && libraryImages.length\">\r\n <div *ngFor=\"let img of libraryImages; let li = index\" id=\"lib-img-{{ config.name }}-{{ li }}\"\r\n class=\"library-grid-item position-relative rounded-12 overflow-hidden cursor-pointer bg-white b-3px-solid-transparent\"\r\n [class.selected]=\"isLibraryItemSelected(img)\" (click)=\"toggleLibraryItem(img)\">\r\n <img [src]=\"getLibraryItemUrl(img)\" class=\"library-grid-img w-100 h-100 d-block\" alt=\"Library image\">\r\n <div\r\n class=\"library-check position-absolute bg-white rounded-50 d-flex align-items-center justify-content-center c-3B82F6\"\r\n *ngIf=\"isLibraryItemSelected(img)\">\r\n <mat-icon>check_circle</mat-icon>\r\n </div>\r\n <div class=\"library-overlay-hover position-absolute b-rgba-59-130-246-0-12\"></div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty library -->\r\n <div\r\n class=\"library-empty d-flex flex-column align-items-center justify-content-center gap-12 flex-1 c-9CA3AF fs-0-875rem p-48px-24px\"\r\n *ngIf=\"!libraryLoading && !libraryError && libraryImages.length === 0\">\r\n <mat-icon>image_not_supported</mat-icon>\r\n <span>{{ controller.labels['LBL_LIBRARY_EMPTY'] || 'No images found in library.' }}</span>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"library-modal-footer d-flex align-items-center justify-content-end bg-white p-20px-28px bt-1px-solid-E5E7EB\">\r\n <div class=\"library-footer-actions d-flex gap-12\">\r\n <lib-button id=\"btn-library-cancel-{{ config.name }}\" [variant]=\"'outline'\" (click)=\"closeLibraryModal()\">\r\n {{ controller.labels['LBL_CANCEL'] || 'Cancel' }}\r\n </lib-button>\r\n <lib-button id=\"btn-library-confirm-{{ config.name }}\" [variant]=\"'primary'\"\r\n [disabled]=\"librarySelectedIds.size === 0\" (click)=\"confirmLibrarySelection()\">\r\n {{ controller.labels['LBL_CONTINUE'] || 'Continue' }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n\r\n\r\n <!-- \u2550\u2550 Location Field \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isLocation\" class=\"field-wrapper location-field-wrapper d-flex flex-column gap-6 gap-12\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Field label -->\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n <p class=\"location-subtitle m-0 c-6B7280 fs-0-8125rem\" *ngIf=\"config.hint\">{{ config.hint }}</p>\r\n\r\n <!-- Three-tab bar -->\r\n <div class=\"location-tabs d-flex gap-12 mb-24\">\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'VENUE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('VENUE')\">\r\n {{ controller.labels['LBL_LOC_VENUE'] || 'Venue' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'ONLINE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('ONLINE')\">\r\n {{ controller.labels['LBL_LOC_ONLINE'] || 'Online Event' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'TBA' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('TBA')\">\r\n {{ controller.labels['LBL_LOC_TBA'] || 'To be Announced' }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- VENUE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'VENUE'\" class=\"loc-panel loc-venue-panel d-flex flex-column gap-12\">\r\n\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ADDRESS'] || 'Location address' }}\r\n </p>\r\n\r\n <!-- Added venue rows -->\r\n <div class=\"loc-venue-list d-flex flex-column gap-8\" *ngIf=\"locationVenues.length > 0\">\r\n <div *ngFor=\"let venue of locationVenues; let i = index\"\r\n class=\"loc-venue-item d-flex align-items-center gap-10 p-10px-14px br-7px b-ffffff b-1px-solid-D1D5DB\">\r\n <mat-icon class=\"loc-venue-search-icon fs-18px c-9CA3AF\">search</mat-icon>\r\n <span class=\"loc-venue-text flex-1 overflow-hidden fs-0-875rem c-111827\">{{ venue.address || venue.name ||\r\n venue.description }}</span>\r\n <button type=\"button\"\r\n class=\"loc-action-btn loc-delete-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none p-4px\"\r\n (click)=\"removeLocationVenue(i)\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Location count badge -->\r\n <p class=\"loc-count-text m-0 font-weight-600 fs-0-8125rem c-3B82F6\"\r\n *ngIf=\"locationVenues.length > 0 && config.locationConfig?.allowMulti\">\r\n {{ locationVenues.length }} {{ controller.labels['LBL_LOC_COUNT_SUFFIX'] || 'Locations Added!' }}\r\n </p>\r\n\r\n <!-- Search input (hide when max reached) -->\r\n <div class=\"loc-search-container position-relative\" *ngIf=\"!locationMaxReached\">\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n <input\r\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\r\n [placeholder]=\"config.locationConfig?.venuePlaceholder || (controller.labels['PH_LOC_VENUE'] || 'Type to search venue...')\"\r\n [value]=\"locationSearchText\" (input)=\"handleLocationSearchInput($event)\" (blur)=\"hideLocationSuggestions()\"\r\n autocomplete=\"off\" [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n <!-- Suggestions dropdown -->\r\n <div class=\"loc-suggestions-panel position-absolute overflow-hidden br-8px b-ffffff b-1px-solid-D1D5DB\"\r\n *ngIf=\"locationShowSuggestions && locationSuggestions.length\">\r\n <div *ngFor=\"let sug of locationSuggestions\"\r\n class=\"loc-suggestion-item d-flex align-items-center gap-10 cursor-pointer p-10px-14px\"\r\n (mousedown)=\"onLocationSuggestionSelect(sug)\">\r\n <mat-icon class=\"loc-suggestion-icon fs-18px c-E53E3E\">place</mat-icon>\r\n <span class=\"loc-suggestion-text overflow-hidden fs-0-875rem c-374151\">{{ sug.description }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Add another button -->\r\n <button type=\"button\"\r\n class=\"loc-add-btn d-inline-flex align-items-center gap-6 cursor-pointer font-weight-600 p-0 b-none border-none fs-0-875rem c-1A56DB\"\r\n *ngIf=\"locationVenues.length > 0 && !locationMaxReached && config.locationConfig?.allowMulti\">\r\n <mat-icon>add_circle_outline</mat-icon>\r\n <span>{{ controller.labels['LBL_LOC_ADD_ANOTHER'] || 'Add another Location' }}</span>\r\n </button>\r\n\r\n <!-- Map -->\r\n <div class=\"loc-map-container overflow-hidden br-10px b-1px-solid-E5E7EB\"\r\n *ngIf=\"config.locationConfig?.showMap !== false\">\r\n <ng-container *ngIf=\"config.locationConfig?.googleMapsApiKey; else simpleEmbed\">\r\n <div [id]=\"'loc-map-' + config.name\" class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\"></div>\r\n </ng-container>\r\n <ng-template #simpleEmbed>\r\n <iframe class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\" [src]=\"getLocationMapEmbedUrl() | trustedUrl\"\r\n frameborder=\"0\" allowfullscreen loading=\"lazy\">\r\n </iframe>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- Map hint -->\r\n <p class=\"loc-map-hint m-0 text-center fs-0-78rem c-6B7280\">\r\n {{ controller.labels['LBL_LOC_MAP_HINT'] || 'Type the venue address. A map will appear to assist you.' }}\r\n </p>\r\n </div>\r\n\r\n <!-- ONLINE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'ONLINE'\" class=\"loc-panel loc-online-panel d-flex flex-column gap-12\">\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ONLINE_URL'] || 'Event URL' }}\r\n </p>\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">link</mat-icon>\r\n <input\r\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\r\n type=\"url\"\r\n [placeholder]=\"config.locationConfig?.onlinePlaceholder || (controller.labels['PH_LOC_ONLINE'] || 'https://zoom.us/j/...')\"\r\n [value]=\"locationOnlineUrl\" (input)=\"onLocationUrlChange($any($event.target).value)\"\r\n [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n </div>\r\n\r\n <!-- TBA TAB -->\r\n <div *ngIf=\"locationActiveTab === 'TBA'\"\r\n class=\"loc-panel loc-tba-panel d-flex flex-column gap-12 justify-content-center\">\r\n <div\r\n class=\"loc-tba-content d-flex flex-column align-items-center justify-content-center text-center gap-12 p-32px-24px b-F9FAFB b-1px-dashed-D1D5DB br-10px\">\r\n <mat-icon class=\"loc-tba-icon fs-40px c-9CA3AF\">schedule</mat-icon>\r\n <p class=\"loc-tba-text m-0 c-6B7280 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_TBA_DESC'] || \"This event's location is yet to be announced. Check back later\r\n for updates.\" }}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <!-- Hidden real form control -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n</div>", styles: [".d-flex{display:flex}.d-inline-flex{display:inline-flex}.d-grid{display:grid}.d-block{display:block}.d-none{display:none}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.align-items-center{align-items:center}.align-items-start{align-items:flex-start}.align-items-end{align-items:flex-end}.justify-content-center{justify-content:center}.justify-content-between{justify-content:space-between}.justify-content-start{justify-content:flex-start}.justify-content-end{justify-content:flex-end}.grid-cols-12{grid-template-columns:repeat(12,1fr)}.w-100{width:100%}.h-100{height:100%}.position-relative{position:relative}.position-absolute{position:absolute}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-poppins{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)}.font-weight-400{font-weight:400}.font-weight-500{font-weight:500}.font-weight-600{font-weight:600}.text-13{font-size:13px}.text-14{font-size:14px}.text-16{font-size:16px}.color-white{color:#fff}.color-dark{color:#111827}.color-gray{color:#6b7280}.color-error{color:var(--cc-sf-error-text-color, #DC2626)}.bg-white{background-color:#fff}.bg-transparent{background-color:transparent}.m-0{margin:0}.mt-4{margin-top:4px}.mt-8{margin-top:8px}.mt-10{margin-top:10px}.mt-12{margin-top:12px}.mt-16{margin-top:16px}.mt-20{margin-top:20px}.mt-24{margin-top:24px}.mb-0{margin-bottom:0}.mb-4{margin-bottom:4px}.mb-8{margin-bottom:8px}.mb-10{margin-bottom:10px}.mb-12{margin-bottom:12px}.mb-16{margin-bottom:16px}.mb-20{margin-bottom:20px}.mb-24{margin-bottom:24px}.ml-16{margin-left:16px}.ml-20{margin-left:20px}.p-0{padding:0}.p-16{padding:16px}.p-20{padding:20px}.p-24{padding:24px}.pt-20{padding-top:20px}.pb-10{padding-bottom:10px}.gap-4{gap:4px}.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-16{gap:16px}.gap-20{gap:20px}.rounded-4{border-radius:4px}.rounded-6{border-radius:6px}.rounded-8{border-radius:8px}.rounded-10{border-radius:10px}.rounded-12{border-radius:12px}.rounded-20{border-radius:20px}.rounded-24{border-radius:24px}.rounded-50{border-radius:50%}.cursor-pointer{cursor:pointer}.overflow-hidden{overflow:hidden}.resize-vertical{resize:vertical}.box-sizing-border{box-sizing:border-box}.border-none{border:none!important}.mb-16px{margin-bottom:var(--cc-sf-grid-gap, 16px)!important}.c-DC2626{color:var(--cc-sf-label-required-color, #DC2626)!important}.ml-0-125rem{margin-left:.125rem!important}.fs-0-875rem{font-size:.875rem!important}.c-111827{color:var(--cc-sf-label-color, #111827)!important}.br-7px{border-radius:var(--cc-sf-input-radius, 7px)!important}.c-6B7280{color:var(--cc-sf-hint-color, #6B7280)!important}.fs-0-75rem{font-size:var(--cc-sf-error-text-size, .75rem)!important}.b-none{background:none!important}.p-32px-24px{padding:32px 24px!important}.us-none{-webkit-user-select:none!important;user-select:none!important}.c-1E293B{color:var(--cc-sf-label-color, #1E293B)!important}.c-3B82F6{color:var(--cc-sf-chip-selected-bg, #3B82F6)!important}.fs-0-78rem{font-size:.78rem!important}.p-10px-14px{padding:10px 14px!important}.fs-0-85rem{font-size:.85rem!important}.fs-0-72rem{font-size:.72rem!important}.c-94A3B8{color:#94a3b8!important}.p-4px{padding:4px!important}.br-8px{border-radius:var(--cc-sf-input-radius, 8px)!important}.bc-F3F4F6{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.br-none{border-right:none!important}.bl-none{border-left:none!important}.pe-none{pointer-events:none!important}.fs-1-1rem{font-size:1.1rem!important}.c-9CA3AF{color:var(--cc-sf-hint-color, #9CA3AF)!important}.pl-2-4rem{padding-left:2.4rem!important}.fs-0-8125rem{font-size:.8125rem!important}.ls-none{list-style:none!important}.br-12px{border-radius:var(--mu-carousel-radius, 12px)!important}.b-FFFAF1{background:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.fs-18px{font-size:18px!important}.b-FEF2F2{background:var(--cc-sf-error-bg, #FEF2F2)!important}.bc-DC2626{border-color:var(--cc-sf-error-border, #DC2626)!important}.c-202124{color:var(--cc-sf-label-color, #202124)!important}.fs-18px{font-size:var(--cc-sf-label-size, 18px)!important}.mb-0-5rem{margin-bottom:.5rem!important}.pt-0-625rem{padding-top:var(--cc-sf-input-padding-y, .625rem)!important}.pb-0-625rem{padding-bottom:var(--cc-sf-input-padding-y, .625rem)!important}.pl-16px{padding-left:var(--cc-sf-input-padding-x, 16px)!important}.pr-16px{padding-right:var(--cc-sf-input-padding-x, 16px)!important}.bc-ffffff{background-color:var(--cc-sf-section-bg, #ffffff)!important}.b-1px-solid-D1D5DB{border:1px solid var(--cc-sf-input-border, #D1D5DB)!important}.fs-0-75rem{font-size:.75rem!important}.c-1F2937{color:var(--cc-sf-section-label-color, #1F2937)!important}.p-6px-14px{padding:var(--cc-sf-chip-padding, 6px 14px)!important}.b-ffffff{background:var(--loc-suggestion-bg, #ffffff)!important}.c-374151{color:var(--cc-sf-label-color, #374151)!important}.br-20px{border-radius:var(--cc-sf-chip-radius, 20px)!important}.fs-0-875rem{font-size:var(--cc-sf-btn-font-size, .875rem)!important}.bc-D1D5DB{background-color:var(--cc-sf-switch-track-off, #D1D5DB)!important}.pr-2-75rem{padding-right:2.75rem!important}.p-0-25rem{padding:.25rem!important}.p-0-625rem-0-875rem{padding:var(--cc-sf-generated-padding, .625rem .875rem)!important}.b-F3F4F6{background:var(--cc-sf-generated-bg, #F3F4F6)!important}.b-1px-solid-E5E7EB{border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.br-8px{border-radius:var(--cc-sf-uploaded-item-radius, 8px)!important}.c-6B7280{color:var(--ms-desc-color, #6B7280)!important}.mb-20px{margin-bottom:var(--cc-sf-section-gap, 20px)!important}.br-10px{border-radius:var(--cc-sf-input-radius, 10px)!important}.p-20px{padding:var(--cc-sf-section-padding, 20px)!important}.fs-1rem{font-size:1rem!important}.m-0-0-16px-0{margin:0 0 16px!important}.bb-2px-solid-E5E7EB{border-bottom:var(--cc-sf-section-label-border, 2px solid #E5E7EB)!important}.p-16px{padding:var(--cc-sf-instance-padding, 16px)!important}.b-F9FAFB{background:var(--loc-tba-bg, #F9FAFB)!important}.bb-1px-dashed-D1D5DB{border-bottom:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)!important}.c-4B5563{color:var(--cc-sf-instance-num-color, #4B5563)!important}.fs-0-8125rem{font-size:var(--cc-sf-hint-size, .8125rem)!important}.pb-0{padding-bottom:0!important}.p-18px-24px{padding:18px 24px!important}.c-111827{color:var(--ms-title-color, #111827)!important}.bt-1px-solid-E5E7EB{border-top:1px solid #E5E7EB!important}.p-4px-10px{padding:4px 10px!important}.b-FFF5F5{background:var(--cc-sf-btn-remove-bg, #FFF5F5)!important}.c-E53E3E{color:var(--loc-delete-color, #E53E3E)!important}.b-1px-solid-FED7D7{border:var(--cc-sf-btn-remove-border, 1px solid #FED7D7)!important}.br-4px{border-radius:var(--cc-sf-btn-remove-radius, 4px)!important}.p-8px-16px{padding:8px 16px!important}.b-transparent{background:var(--cc-sf-btn-add-bg, transparent)!important}.c-3B82F6{color:var(--cc-sf-input-focus-border, #3B82F6)!important}.b-1px-dashed-CBD5E1{border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1)!important}.br-6px{border-radius:var(--cc-sf-btn-add-radius, 6px)!important}.b-1-5px-dashed-CBD5E1{border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1)!important}.br-12px{border-radius:var(--cc-sf-dropzone-radius, 12px)!important}.bc-FFFAF1{background-color:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.c-94A3B8{color:var(--cc-sf-uploaded-remove-color, #94A3B8)!important}.fs-0-9rem{font-size:var(--cc-sf-input-font-size, .9rem)!important}.c-64748B{color:var(--cc-sf-dropzone-hint-color, #64748B)!important}.b-1px-solid-E2E8F0{border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0)!important}.b-2px-solid-E2E8F0{border:2px solid #E2E8F0!important}.pr-3-5rem{padding-right:3.5rem!important}.p-0-0-875rem{padding:0 .875rem!important}.bc-FFFFFF{background-color:var(--cc-sf-input-bg, #FFFFFF)!important}.b-1-5px-solid-D1D5DB{border:var(--cc-sf-input-border, 1.5px solid #D1D5DB)!important}.mb-0-75rem{margin-bottom:.75rem!important}.mt-6px{margin-top:6px!important}.pr-2-4rem{padding-right:2.4rem!important}.p-0-2rem{padding:.2rem!important}.fs-1-35rem{font-size:1.35rem!important}.p-4px-12px{padding:4px 12px!important}.b-111827{background:var(--cc-sf-label-color, #111827)!important}.b-2px-dashed-CBD5E1{border:2px dashed var(--cc-sf-dropzone-border, #CBD5E1)!important}.fs-52px{font-size:52px!important}.p-12px-16px{padding:12px 16px!important}.bb-1px-solid-F3F4F6{border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)!important}.b-0F172A{background:var(--mu-carousel-bg, #0F172A)!important}.b-3px-solid-rgba-255-255-255-0-15{border:3px solid rgba(255,255,255,.15)!important}.b-rgba-255-255-255-0-85{background:#ffffffd9!important}.b-rgba-0-0-0-0-55{background:#0000008c!important}.b-rgba-255-255-255-0-45{background:#ffffff73!important}.pb-4px{padding-bottom:4px!important}.b-2px-solid-transparent{border:2px solid transparent!important}.b-E2E8F0{background:var(--mu-thumb-bg, #E2E8F0)!important}.b-1E293B{background:#1e293b!important}.c-EF4444{color:#ef4444!important}.b-rgba-0-0-0-0-5{background:#00000080!important}.br-16px{border-radius:var(--mu-modal-radius, 16px)!important}.p-24px-28px{padding:24px 28px!important}.bb-1px-solid-E5E7EB{border-bottom:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.fs-1-25rem{font-size:1.25rem!important}.p-48px-24px{padding:48px 24px!important}.b-3px-solid-E2E8F0{border:3px solid #E2E8F0!important}.p-16px-24px{padding:16px 24px!important}.p-28px{padding:28px!important}.b-3px-solid-transparent{border:3px solid transparent!important}.b-rgba-59-130-246-0-12{background:#3b82f61f!important}.p-20px-28px{padding:20px 28px!important}.c-1A56DB{color:var(--loc-add-color, #1A56DB)!important}.b-1px-dashed-D1D5DB{border:1px dashed var(--cc-sf-input-disabled-border, #D1D5DB)!important}.fs-40px{font-size:40px!important}.c-9CA3AF{color:var(--loc-tba-icon-color, #9CA3AF)!important}.form-field{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)!important}:host{--cc-sf-input-border: #D1D5DB;--cc-sf-input-bg: #ffffff;--cc-sf-input-radius: 9px;--cc-sf-input-height: 44px;--cc-sf-label-color: #111827;--cc-sf-hint-color: #9CA3AF;--cc-sf-error-border: #EF4444;--cc-sf-error-bg: #FFF5F5;--cc-sf-accent-color: #6366F1;--cc-sf-input-focus-border: #6366F1;--cc-sf-input-hover-border: #A5B4FC;--cc-sf-input-placeholder: #C4C9D4;--cc-sf-input-disabled-bg: #F8F9FB;--cc-sf-input-disabled-border: #E5E7EB;--cc-sf-switch-track-on: #6366F1;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-thumb: #ffffff;--cc-sf-selected-color: #6366F1}.form-row{gap:var(--cc-sf-grid-gap, 16px)}.form-row.horizontal{display:flex;flex-direction:row}.form-row.horizontal>*{flex:1}.form-row:not(.horizontal){flex-direction:column}.form-row.grid-row{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.form-row.grid-row{grid-template-columns:1fr}.form-row.grid-row .row-field{grid-column:span 12!important}}.field-label{font-size:.75rem;font-weight:600;line-height:1;letter-spacing:.04em;text-transform:uppercase;color:#6b7280}.field-input,input.matInput,.mat-mdc-input-element{display:block;width:100%;height:var(--cc-sf-input-height)!important;padding:0 14px!important;font-family:inherit;font-size:.9rem;color:var(--cc-sf-label-color);background-color:var(--cc-sf-input-bg)!important;border:1.5px solid var(--cc-sf-input-border)!important;border-radius:var(--cc-sf-input-radius)!important;box-sizing:border-box;box-shadow:0 1px 2px #0000000a!important;transition:all .2s cubic-bezier(.4,0,.2,1)}.field-input::placeholder,input.matInput::placeholder,.mat-mdc-input-element::placeholder{font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder)}.field-input{opacity:var(--cc-sf-input-opacity, 1);line-height:var(--cc-sf-input-line-height, 1.5);transition:var(--cc-sf-input-transition, all .2s ease)}.field-input::placeholder{font-weight:var(--cc-sf-placeholder-weight, 400);font-size:var(--cc-sf-placeholder-size, 14px);line-height:var(--cc-sf-placeholder-line-height, 100%);color:var(--cc-sf-input-placeholder)}.field-input:hover:not(:disabled):not([readonly]){border-color:var(--cc-sf-input-hover-border)!important;box-shadow:0 1px 4px #6366f114!important}.field-input:focus{outline:none;border-color:var(--cc-sf-input-focus-border)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important;background-color:#fefeff!important}.field-input:disabled,.field-input[readonly]{background-color:var(--cc-sf-input-disabled-bg)!important;color:#9ca3af!important;cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important;box-shadow:none!important}.field-input.is-invalid{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)!important}.field-input.is-invalid:focus{box-shadow:0 0 0 3px #ef44441f,0 1px 4px #ef44441a!important}.field-input.textarea{resize:vertical;min-height:100px;height:auto;padding:12px 16px!important}input[type=time].time-input{cursor:pointer}input[type=time].time-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:.7;filter:invert(30%)}input[type=time].time-input::-webkit-calendar-picker-indicator:hover{opacity:1}select.field-input{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E\");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;cursor:pointer}select.field-input:disabled{cursor:not-allowed}.char-count-hint{font-style:normal;line-height:100%;letter-spacing:.02em;margin-top:.4rem}.radio-group.layout-column,.checkbox-group.layout-column{display:grid!important;grid-template-columns:repeat(12,1fr);gap:16px;width:100%}.radio-group.layout-row,.checkbox-group.layout-row{flex-direction:column!important;gap:12px;width:100%}.radio-label input,.checkbox-label input{cursor:pointer;accent-color:var(--cc-sf-chip-selected-bg, #F05A28)}.radio-label.card-item,.checkbox-label.card-item{display:flex!important;flex-direction:row-reverse!important;justify-content:space-between!important;align-items:center!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB);border-radius:12px;padding:16px 20px;box-sizing:border-box;transition:all .2s ease;background:var(--cc-sf-input-bg, #ffffff);margin-bottom:0}.radio-label.card-item input,.checkbox-label.card-item input{margin-left:16px}.radio-label.card-item.selected,.checkbox-label.card-item.selected{border-color:var(--cc-sf-selected-color);background-color:#f05a280d}.radio-label.card-item .option-content .label-text,.checkbox-label.card-item .option-content .label-text,.checkbox-single .checkbox-label{font-weight:var(--cc-sf-label-weight, 500)}.chip-label{transition:var(--cc-sf-input-transition, all .2s ease)}.chip-label:hover{background:var(--cc-sf-chip-hover-bg, #F3F4F6)}.chip-label.selected{background:var(--cc-sf-selected-color);color:var(--cc-sf-chip-selected-color, #ffffff);border-color:var(--cc-sf-selected-color)}.switch{width:50px;height:24px;display:inline-block}.switch input{opacity:0;width:0;height:0;position:absolute}.switch input:checked+.slider{background-color:var(--cc-sf-switch-track-on)!important}.switch input:checked+.slider:before{transform:translate(26px)}.switch .slider{inset:0;transition:.4s;background-color:var(--cc-sf-switch-track-off);border-radius:24px}.switch .slider:before{position:absolute;content:\"\";height:18px;width:18px;left:3px;bottom:3px;background-color:var(--cc-sf-switch-thumb);transition:.4s;border-radius:50%}.rating-group .star{transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star mat-icon{font-size:var(--cc-sf-star-size, 28px);width:var(--cc-sf-star-size, 28px);height:var(--cc-sf-star-size, 28px);line-height:var(--cc-sf-star-size, 28px);color:var(--cc-sf-star-empty, #D1D5DB);transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star.filled mat-icon,.rating-group .star.half mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.rating-group .star:hover mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.password-wrapper .password-toggle{right:.625rem;top:50%;transform:translateY(-50%);line-height:1;transition:color var(--cc-sf-input-transition, .2s ease)}.password-wrapper .password-toggle mat-icon.eye-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;line-height:1.125rem}.password-wrapper .password-toggle:hover{color:var(--cc-sf-label-color, #374151)}.password-wrapper .password-toggle:focus{outline:none}.group-section-wrapper .group-label{font-size:var(--cc-sf-section-label-size, 1rem);font-weight:var(--cc-sf-section-label-weight, 600);color:var(--cc-sf-section-label-color, #1F2937);margin:0 0 16px;padding-left:12px;padding-top:2px;padding-bottom:2px;border-left:var(--cc-sf-section-header-accent-width, 4px) solid var(--cc-sf-section-header-accent-color, #3B82F6);line-height:1.4}.group-section-wrapper .group-fields.sf-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.group-section-wrapper .group-fields.sf-grid{grid-template-columns:1fr}.group-section-wrapper .group-fields.sf-grid .sf-col{grid-column:span 12!important}}.group-section-wrapper .group-accordion-instance{border:var(--cc-sf-instance-border, 1px solid #E5E7EB);border-radius:var(--cc-sf-section-border-radius-inner, 8px);margin-bottom:8px;overflow:hidden;transition:border-color .2s ease}.group-section-wrapper .group-accordion-instance:last-of-type{margin-bottom:0}.group-section-wrapper .group-accordion-instance .group-accordion-header{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;background:var(--cc-sf-repeater-accordion-header-bg, #F9FAFB);cursor:pointer;-webkit-user-select:none;user-select:none;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header:hover{background:var(--cc-sf-repeater-accordion-active-bg, #EFF6FF)}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-badge{width:22px;height:22px;border-radius:50%;background:var(--cc-sf-repeater-badge-bg, #E5E7EB);color:var(--cc-sf-repeater-badge-color, #374151);font-size:.75rem;font-weight:600;display:flex;align-items:center;justify-content:center;flex-shrink:0}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-title{font-size:var(--cc-sf-instance-num-size, .8125rem);font-weight:600;color:var(--cc-sf-repeater-accordion-header-color, #1F2937)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn{background:none;border:none;cursor:pointer;color:var(--cc-sf-btn-remove-color, #E53E3E);padding:4px;border-radius:4px;line-height:1;display:flex;align-items:center;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-chevron{font-size:1.25rem;width:1.25rem;height:1.25rem;line-height:1.25rem;color:var(--cc-sf-instance-num-color, #4B5563)}.group-section-wrapper .group-accordion-instance .group-accordion-body{padding:var(--cc-sf-instance-padding, 16px);background:var(--cc-sf-instance-bg, #F9FAFB);border-top:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)}.group-section-wrapper .btn-add-group{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:10px 20px;margin-top:12px;background:var(--cc-sf-btn-add-bg, transparent);color:var(--cc-sf-btn-add-color, #3B82F6);border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1);border-radius:var(--cc-sf-btn-add-radius, 6px);cursor:pointer;font-family:inherit;font-size:var(--cc-sf-btn-font-size, .875rem);font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.group-section-wrapper .btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.group-section-wrapper .group-instance:last-child{margin-bottom:0}.group-section-wrapper.multi-save-active{border:none;box-shadow:none;padding:0;background:transparent}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button{color:var(--ms-btn-add-color, #3B82F6);font-weight:600;font-size:.875rem;padding:8px 12px}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button:hover{color:var(--ms-btn-add-hover, #2563EB);background-color:var(--cc-sf-btn-add-hover-bg, #EFF6FF)}.group-section-wrapper.multi-save-active .group-instance.is-card{cursor:pointer;transition:all .2s ease-in-out}.group-section-wrapper.multi-save-active .group-instance.is-card:hover{box-shadow:var(--ms-card-shadow-hover, 0 8px 24px rgba(0, 0, 0, .08));border-color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-title{white-space:nowrap;text-overflow:ellipsis}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-desc{line-height:1.4;display:-webkit-box;-webkit-line-clamp:1;line-clamp:1;-webkit-box-orient:vertical}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view.is-expanded .card-content .card-desc{-webkit-line-clamp:unset;line-clamp:unset}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon{font-size:22px;width:22px;height:22px;color:var(--cc-sf-hint-color, #9CA3AF);transition:color .2s}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-delete:hover{color:var(--cc-sf-error-border, #DC2626)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-edit:hover{color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-expand{color:var(--cc-sf-input-disabled-border, #E5E7EB)}.btn-remove{transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-remove mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.btn-remove:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.btn-add-group{font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.upload-drop-zone{background-color:var(--cc-sf-dropzone-bg, #F8FAFC);border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1);border-radius:var(--cc-sf-dropzone-radius, 12px);transition:background-color .2s ease,border-color .2s ease}.upload-drop-zone:hover{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-hover-border, #93C5FD)}.upload-drop-zone.drag-over{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-over-border, #3B82F6);box-shadow:var(--cc-sf-dropzone-over-shadow, 0 0 0 4px rgba(59, 130, 246, .12))}.upload-drop-zone.is-invalid{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.upload-icon-wrap .dropzone-icon-pill{width:52px;height:52px;border-radius:50%;background:var(--cc-sf-dropzone-icon-bg, rgba(59, 130, 246, .1))}.upload-icon-wrap mat-icon.upload-cloud-icon{font-size:28px;width:28px;height:28px;line-height:28px;color:var(--cc-sf-accent-color, #3B82F6)}.upload-main-text{color:var(--cc-sf-label-color, #1E293B)}.upload-sub-text{color:var(--cc-sf-hint-color, #64748B)}.upload-link{color:var(--cc-sf-dropzone-link-color, #3B82F6);font-weight:500}.upload-formats{color:var(--cc-sf-dropzone-link-color, #3B82F6)}.upload-size-badge{display:inline-block;padding:2px 8px;border-radius:20px;background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-hint-color, #6B7280);font-weight:500}.uploaded-item{background:var(--cc-sf-uploaded-item-bg, #ffffff);border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0);border-radius:var(--cc-sf-uploaded-item-radius, 8px);transition:box-shadow .15s ease}.uploaded-item:hover{box-shadow:0 2px 6px #0000000f}.uploaded-item mat-icon.file-type-icon{font-size:20px;width:20px;height:20px;line-height:20px;flex-shrink:0;color:var(--cc-sf-hint-color, #64748B)}.uploaded-item .file-thumb{width:36px;height:36px;object-fit:cover;flex-shrink:0}.uploaded-item .file-info{min-width:0;gap:2px}.uploaded-item .file-info .file-name{white-space:nowrap;text-overflow:ellipsis}.uploaded-item .file-remove-btn{flex-shrink:0;width:32px;height:32px;background:none;border:none;cursor:pointer;color:var(--cc-sf-uploaded-remove-color, #94A3B8);padding:0;display:flex;align-items:center;justify-content:center;transition:color .15s ease,background .15s ease}.uploaded-item .file-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.uploaded-item .file-remove-btn:hover:not(:disabled){color:var(--cc-sf-uploaded-remove-hover-color, #DC2626);background:var(--cc-sf-uploaded-remove-hover-bg, #FEF2F2)}.uploaded-item .file-remove-btn:disabled{opacity:.4;cursor:not-allowed}.uploaded-item.uploading{background:var(--cc-sf-uploaded-uploading-bg, #F8FAFC);border-color:var(--cc-sf-uploaded-uploading-border, #CBD5E1);opacity:.85}.upload-spinner{width:20px;height:20px;flex-shrink:0;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}@keyframes cc-spin{to{transform:rotate(360deg)}}.uploading-label{font-style:italic}.input-group{align-items:stretch}.input-group .field-input{flex:1;width:auto}.input-prefix+.input-group .field-input{border-top-left-radius:0;border-bottom-left-radius:0}.input-group .field-input:has(+.input-suffix){border-top-right-radius:0;border-bottom-right-radius:0}.input-group .field-input.has-icon-right{padding-right:3rem}.input-group.readonly .field-input{cursor:default}.input-prefix,.input-suffix{display:flex!important;align-items:center;white-space:nowrap;padding:0 14px;background-color:var(--cc-sf-input-disabled-bg);border:1px solid var(--cc-sf-input-border);font-size:.875rem;color:var(--cc-sf-hint-color);-webkit-user-select:none;user-select:none}.input-prefix{border-right:none;border-top-left-radius:var(--cc-sf-input-radius, 8px);border-bottom-left-radius:var(--cc-sf-input-radius, 8px)}.input-suffix{border-left:none;border-top-right-radius:var(--cc-sf-input-radius, 8px);border-bottom-right-radius:var(--cc-sf-input-radius, 8px)}.readonly-icons{right:.875rem;top:50%;transform:translateY(-50%)}.readonly-icons mat-icon.lock-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem;opacity:.5;color:var(--cc-sf-hint-color, #6B7280)}.date-icon-wrapper{right:.5rem;top:50%;transform:translateY(-50%);pointer-events:auto}.date-icon-wrapper .mat-icon-button{width:32px;height:32px;line-height:32px}.subfields-group-wrapper .subfields-row{transition:all .2s ease}.subfields-group-wrapper .subfields-row.is-invalid .subfield-item ::ng-deep .field-input{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.subfields-group-wrapper .subfields-row .subfield-item{min-width:0}.subfields-group-wrapper .subfields-row .subfield-item ::ng-deep .field-label{font-size:.75rem!important;margin-bottom:4px!important;font-weight:500!important;color:var(--cc-sf-hint-color, #6B7280)!important}.subfields-group-wrapper .subfields-row .subfield-separator{font-weight:700}.autocomplete-wrapper .ac-input{padding-left:40px!important}.autocomplete-wrapper .ac-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1;transition:color var(--cc-sf-input-transition, .2s ease)}.autocomplete-wrapper .ac-clear-btn{right:.6rem;transition:color .15s ease,background .15s ease}.autocomplete-wrapper .ac-clear-btn mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.autocomplete-wrapper .ac-clear-btn:hover{color:var(--cc-sf-label-color, #374151);background:var(--cc-sf-input-disabled-bg, #F3F4F6)}.autocomplete-wrapper .ac-clear-btn:focus{outline:none}.autocomplete-wrapper:focus-within .ac-search-icon{color:var(--cc-sf-accent-color, #3B82F6)}.autocomplete-wrapper.is-invalid .ac-input{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)}.autocomplete-wrapper.readonly .ac-input{background-color:var(--cc-sf-input-disabled-bg);color:var(--cc-sf-input-disabled-color, #6B7280);cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important}.ac-no-results{font-style:italic}::ng-deep .mat-mdc-autocomplete-panel,::ng-deep .mat-autocomplete-panel{background:var(--cc-sf-input-bg, #ffffff)!important;border-radius:var(--cc-sf-input-radius, 9px)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important;box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f!important;padding:4px 0!important;min-width:200px}::ng-deep .mat-mdc-autocomplete-panel mat-option,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option,::ng-deep .mat-mdc-autocomplete-panel .mat-option,::ng-deep .mat-autocomplete-panel mat-option,::ng-deep .mat-autocomplete-panel .mat-mdc-option,::ng-deep .mat-autocomplete-panel .mat-option{background:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #111827)!important;font-size:.875rem!important;padding:10px 16px!important;min-height:40px!important;line-height:1.4!important;display:flex!important;flex-direction:column!important;align-items:flex-start!important;transition:background var(--cc-sf-input-transition, .2s ease)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]){background:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-option.mdc-list-item--selected{background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)!important;color:var(--cc-sf-selected-color, #6366F1)!important;font-weight:600!important}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-option-label{font-weight:500;color:var(--cc-sf-label-color, #111827);font-size:.875rem;display:block}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-option .ac-display-fields{align-items:center;line-height:1}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-item{display:inline-flex;align-items:center;font-size:.72rem;color:var(--cc-sf-hint-color, #6B7280);gap:3px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-chip{background:var(--cc-sf-input-disabled-bg, #F3F4F6);border-radius:4px;padding:2px 6px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-text{color:var(--cc-sf-hint-color, #6B7280)}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--cc-sf-input-border, #D1D5DB);vertical-align:middle}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-label{font-weight:600;color:var(--cc-sf-hint-color, #9CA3AF);margin-right:2px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-icon{font-size:11px;width:11px;height:11px;line-height:11px;color:var(--cc-sf-hint-color, #9CA3AF);flex-shrink:0}.mu-layout{grid-template-columns:1fr 1fr;gap:32px}@media(max-width:768px){.mu-layout{grid-template-columns:1fr}}.mu-title{font-weight:700;line-height:1.3}.mu-badge{white-space:nowrap;flex-shrink:0}.mu-description{line-height:1.6}.mu-feature-item .mu-check{width:16px;height:16px;line-height:16px;flex-shrink:0}.mu-right{min-height:260px}.mu-right-empty{min-height:250px;max-width:400px;box-shadow:0 2px 10px #0000000d;transition:box-shadow .2s ease}.mu-right-empty:hover{cursor:pointer;box-shadow:0 4px 16px #0000001a}.mu-right-empty .mu-right-empty-icon{width:52px;height:52px;line-height:52px;opacity:.3}.mu-right-empty p{margin:0;font-size:.85rem}.media-add-container{display:inline-block}.media-add-container ::ng-deep button{display:flex;align-items:center;gap:6px}.media-add-container ::ng-deep button .menu-chevron{width:18px;height:18px;line-height:18px;transition:transform .2s ease}.media-dropdown{top:calc(100% + 6px);left:0;z-index:200;min-width:240px;box-shadow:var(--mu-dropdown-shadow, 0 8px 32px rgba(0, 0, 0, .12));animation:mu-fade-in .15s ease}@keyframes mu-fade-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.media-dropdown-item{transition:background .15s ease}.media-dropdown-item:last-child{border-bottom:none}.media-dropdown-item:hover{background:var(--cc-sf-dropzone-hover-bg, #F0F9FF)}.media-drop-icon{width:36px;height:36px;flex-shrink:0}.media-drop-icon mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.media-drop-icon--video{background:var(--mu-icon-video-bg, #FFF0F0);color:var(--mu-icon-video-color, #EF4444)}.media-drop-icon--device{background:var(--mu-icon-device-bg, #EFF6FF);color:var(--mu-icon-device-color, #3B82F6)}.media-drop-icon--library{background:var(--mu-icon-library-bg, #F0FDF4);color:var(--mu-icon-library-color, #22C55E)}.media-drop-text{gap:2px}.youtube-input-panel{animation:mu-fade-in .18s ease}.youtube-panel-label mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:var(--mu-icon-video-color, #EF4444)}.youtube-input-row{align-items:stretch}.media-menu-backdrop{position:fixed;inset:0;z-index:100}.media-upload-status{animation:mu-fade-in .2s ease}.media-upload-status .status-icon{width:18px;height:18px;line-height:18px}.media-carousel-main{max-width:400px;height:var(--mu-carousel-height, 250px)}.carousel-viewer{inset:0}.carousel-viewer .carousel-image{object-fit:cover}.carousel-viewer .carousel-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.carousel-nav{top:50%;transform:translateY(-50%);z-index:10;width:40px;height:40px;box-shadow:0 2px 8px #0003;transition:background .2s ease,opacity .2s ease;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.carousel-nav mat-icon{font-size:22px;width:22px;height:22px;line-height:22px;color:#1e293b}.carousel-nav:hover:not(:disabled){background:#fff}.carousel-nav:disabled{opacity:.3;cursor:not-allowed}.carousel-nav--prev{left:12px}.carousel-nav--next{right:12px}.carousel-remove-btn{top:10px;right:10px;z-index:10;width:32px;height:32px;transition:background .2s ease}.carousel-remove-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:#fff}.carousel-remove-btn:hover:not(:disabled){background:#dc2626d9}.carousel-remove-btn:disabled{opacity:.4;cursor:not-allowed}.carousel-dots{bottom:10px;left:50%;transform:translate(-50%);z-index:10}.carousel-dot{width:8px;height:8px;transition:background .2s ease,transform .2s ease}.carousel-dot.active{background:#fff;transform:scale(1.3)}.media-thumbnail-strip{max-width:400px;overflow-x:auto}.media-thumbnail-strip::-webkit-scrollbar{height:4px}.media-thumbnail-strip::-webkit-scrollbar-thumb{background:var(--cc-sf-input-disabled-border, #D1D5DB);border-radius:2px}.media-thumb{flex-shrink:0;width:72px;height:52px;transition:border-color .2s ease,transform .15s ease}.media-thumb.active{border-color:var(--mu-thumb-active-border, var(--cc-sf-accent-color, #3B82F6));transform:scale(1.04)}.media-thumb:hover:not(.active){border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.media-thumb .thumb-img{object-fit:cover}.media-thumb .thumb-yt-placeholder mat-icon{font-size:28px;width:28px;height:28px;line-height:28px}.media-thumb .thumb-uploading .thumb-spinner{width:20px;height:20px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.media-library-portal-host{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;visibility:hidden;opacity:0;pointer-events:none;transition:opacity .2s ease,visibility .2s ease}.media-library-portal-host.is-open{visibility:visible;opacity:1;pointer-events:auto}.media-library-portal-host.is-open .media-library-modal{transform:scale(1) translateY(0)}.media-library-overlay{position:absolute;inset:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.media-library-modal{position:relative;z-index:10000;width:90vw;max-width:900px;max-height:90vh;background:#fff;border-radius:16px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transform:scale(.95) translateY(10px);transition:transform .3s cubic-bezier(.175,.885,.32,1.275)}.library-modal-header{flex-shrink:0}.library-modal-title{font-weight:700}.library-modal-subtitle{line-height:1.5;max-width:600px}.library-close-btn{width:32px;height:32px;transition:background .15s ease,color .15s ease}.library-close-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-close-btn:hover{background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-label-color, #374151)}.library-loading mat-icon,.library-empty mat-icon{font-size:40px;width:40px;height:40px;line-height:40px;opacity:.5}.lib-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.library-error{flex-shrink:0}.library-error mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-grid{grid-template-columns:repeat(5,1fr);max-height:50vh;overflow-y:auto}.library-grid::-webkit-scrollbar{width:8px}.library-grid::-webkit-scrollbar-track{background:#f1f1f1}.library-grid::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:10px;border:2px solid #F1F1F1}.library-grid-item{aspect-ratio:1/1;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.library-grid-item:hover{transform:translateY(-4px) scale(1.02);box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}.library-grid-item.selected{border-color:var(--cc-sf-accent-color, #3B82F6)}.library-grid-item.selected .library-grid-img{opacity:.7}.library-grid-item:hover .library-overlay-hover{opacity:1}.library-grid-img{object-fit:cover}.library-overlay-hover{inset:0;opacity:0;transition:opacity .15s ease}.library-check{top:6px;right:6px;width:22px;height:22px;box-shadow:0 1px 4px #00000026}.library-check mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.library-modal-footer{flex-shrink:0}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary{background-color:var(--cc-sf-accent-color, #3B82F6)!important;border-color:var(--cc-sf-accent-color, #3B82F6)!important;color:#fff!important;font-weight:600;padding-left:32px;padding-right:32px}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:hover{background-color:var(--cc-sf-btn-primary-hover-bg, #2563EB)!important}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:disabled{background-color:#93c5fd!important;cursor:not-allowed}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline{font-weight:600;padding-left:24px;padding-right:24px;border-color:#d1d5db;color:#374151}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline:hover{background-color:#f9fafb}.location-subtitle{line-height:1.5}.loc-tab-btn ::ng-deep button{width:100%}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning){background-color:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #000000)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning):hover{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.loc-venue-item{transition:box-shadow .15s ease,border-color .15s ease}.loc-venue-item:hover{box-shadow:0 2px 8px #0000000f;border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.loc-venue-search-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-venue-text{white-space:nowrap;text-overflow:ellipsis}.loc-action-btn{transition:background .15s ease,color .15s ease;flex-shrink:0}.loc-action-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.loc-action-btn.loc-delete-btn{color:var(--loc-delete-color, #E53E3E)}.loc-action-btn.loc-delete-btn:hover{background:var(--cc-sf-error-bg, #FEF2F2)}.loc-action-btn.loc-edit-btn{color:var(--cc-sf-hint-color, #9CA3AF)}.loc-action-btn.loc-edit-btn:hover{color:var(--cc-sf-input-focus-border, #3B82F6);background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)}.loc-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1}.loc-suggestions-panel{top:calc(100% + 4px);left:0;right:0;z-index:300;box-shadow:0 8px 24px #0000001a;animation:mu-fade-in .15s ease;max-height:260px;overflow-y:auto}.loc-suggestion-item{transition:background .12s ease}.loc-suggestion-item:hover,.loc-suggestion-item:focus{background:var(--loc-suggestion-hover-bg, #F0F9FF)}.loc-suggestion-item:not(:last-child){border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)}.loc-suggestion-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-suggestion-text{white-space:nowrap;text-overflow:ellipsis}.loc-add-btn{transition:opacity .15s ease}.loc-add-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.loc-add-btn:hover{opacity:.8}.loc-map-container{box-shadow:0 2px 10px #0000000f}.loc-tba-panel{min-height:120px}.loc-tba-icon{width:40px;height:40px;line-height:40px;opacity:.6}.loc-tba-text{line-height:1.6;max-width:360px}.radio-label{display:flex!important}.radio-label .option-content{padding-left:10px}\n"] }]
|
|
2391
|
+
args: [{ selector: 'lib-form-field', standalone: false, template: "<div class=\"form-field mb-16px\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\r\n\r\n <!-- \u2550\u2550 ROW Layout \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRow\" class=\"form-row grid-row\">\r\n <ng-container *ngFor=\"let child of config.children\">\r\n <div class=\"row-field\" [style.gridColumn]=\"'span ' + getChildColSpan(child)\" *ngIf=\"child.isEnabled !== false\">\r\n <lib-form-field [config]=\"child\" [controller]=\"controller\" [formGroup]=\"formGroup\" [allowMulti]=\"allowMulti\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- \u2550\u2550 GROUP \u2014 allowMulti (repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig?.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\"\r\n [class.multi-save-active]=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n\r\n <!-- Multi-Save: header row with label + top-right Add button -->\r\n <div class=\"multi-save-header d-flex justify-content-between align-items-center mb-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig?.label\">{{ config.sectionConfig!.label }}</h3>\r\n <lib-button [variant]=\"'outline'\" [icon]=\"{type: 'material', value: 'add'}\" (click)=\"addGroupInstance()\"\r\n class=\"btn-add-multi\">\r\n {{ addMultiLabel }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- Standard heading (non-multiSave) -->\r\n <h3 class=\"group-label\"\r\n *ngIf=\"config.sectionConfig?.label && !config.sectionConfig?.multiSaveConfig?.active\">{{\r\n config.sectionConfig!.label }}</h3>\r\n\r\n <!-- \u2500\u2500 Standard (non-multiSave) repeater: accordion instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <ng-container *ngIf=\"!config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-accordion-instance\"\r\n [class.is-expanded]=\"isGroupExpanded(i)\">\r\n\r\n <!-- Accordion header -->\r\n <div class=\"group-accordion-header\" (click)=\"toggleGroupAccordion(i)\"\r\n role=\"button\" [attr.aria-expanded]=\"isGroupExpanded(i)\">\r\n <div class=\"accordion-header-left d-flex align-items-center gap-10\">\r\n <span class=\"instance-badge\">{{ i + 1 }}</span>\r\n <span class=\"instance-title\">{{ config.sectionConfig!.label }} #{{ i + 1 }}</span>\r\n </div>\r\n <div class=\"accordion-header-right d-flex align-items-center gap-6\">\r\n <button type=\"button\" class=\"accordion-remove-btn\"\r\n *ngIf=\"instanceList.length > 1\"\r\n (click)=\"$event.stopPropagation(); removeGroupInstance(i)\"\r\n aria-label=\"Remove\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n <mat-icon class=\"accordion-chevron\">\r\n {{ isGroupExpanded(i) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <!-- Accordion body (always mounted so form controls survive collapse) -->\r\n <div class=\"group-accordion-body\" [hidden]=\"!isGroupExpanded(i)\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\">\r\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"true\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Full-width dashed Add button -->\r\n <button type=\"button\" class=\"btn-add-group\" (click)=\"addGroupInstance()\">\r\n <mat-icon>add</mat-icon> {{ addLabel }} {{ config.sectionConfig!.label }}\r\n </button>\r\n </ng-container>\r\n\r\n <!-- \u2500\u2500 MultiSave: card instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <ng-container *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-instance position-relative mb-16 overflow-hidden\"\r\n [class.is-editing]=\"instance.isEditing\"\r\n [class.is-card]=\"!instance.isEditing\">\r\n\r\n <!-- Edit / new form view -->\r\n <div [hidden]=\"!instance.isEditing\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\">\r\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"true\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Save / Cancel -->\r\n <div class=\"group-footer d-flex justify-content-end align-items-center gap-16 p-0-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"multiSaveError\">{{ multiSaveError }}</span>\r\n <div class=\"footer-actions d-flex gap-12\">\r\n <lib-button [variant]=\"'outline'\" (click)=\"cancelGroupInstance(i)\">Cancel</lib-button>\r\n <lib-button [variant]=\"'primary'\" (click)=\"saveGroupInstance(i)\">Save</lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Card view (saved state) -->\r\n <ng-container *ngIf=\"!instance.isEditing\">\r\n <div class=\"card-view d-flex justify-content-between align-items-center p-18px-24px\"\r\n [class.is-expanded]=\"instance.isExpanded\">\r\n <div class=\"card-content flex-1 d-flex flex-column gap-4 overflow-hidden\">\r\n <span class=\"card-title font-weight-600 overflow-hidden fs-1rem c-111827\">{{\r\n instance.fg.get(config.sectionConfig!.multiSaveConfig!.summaryField || '')?.value\r\n || '\u2014' }}</span>\r\n </div>\r\n <div class=\"card-actions d-flex align-items-center gap-16 ml-20\">\r\n <mat-icon class=\"icon-delete\" (click)=\"removeGroupInstance(i, true)\">delete_outline</mat-icon>\r\n <mat-icon class=\"icon-edit\" (click)=\"editGroupInstance(i)\">edit_outline</mat-icon>\r\n <mat-icon class=\"icon-expand\" (click)=\"toggleExpandGroupInstance(i)\">\r\n {{ instance.isExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- \u2550\u2550 GROUP \u2014 single (non-repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig && !config.sectionConfig.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig.label\">{{ config.sectionConfig.label }}</h3>\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\" *ngIf=\"field.isEnabled !== false\">\r\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"groupFormGroup\" [allowMulti]=\"false\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n\r\n <!-- \u2550\u2550 Text Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isTextField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <textarea *ngIf=\"config.subType === 'LONG'\" class=\"field-input textarea\" [placeholder]=\"config.placeholder || ''\"\r\n [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\" rows=\"4\">\r\n </textarea>\r\n\r\n <!-- Password input with show/hide toggle -->\r\n <div *ngIf=\"config.subType === 'PASSWORD'\" class=\"password-wrapper position-relative d-flex align-items-center\">\r\n <input [type]=\"showPassword ? 'text' : 'password'\" class=\"field-input password-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <button type=\"button\"\r\n class=\"password-toggle position-absolute cursor-pointer d-flex align-items-center justify-content-center b-none border-none c-6B7280 p-0-25rem\"\r\n (click)=\"showPassword = !showPassword\" tabindex=\"-1\"\r\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\r\n <mat-icon class=\"eye-icon\">{{ showPassword ? 'visibility' : 'visibility_off' }}</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input *ngIf=\"config.subType !== 'LONG' && config.subType !== 'PASSWORD'\"\r\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\" class=\"field-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\"\r\n [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Number Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isNumberField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input type=\"number\" class=\"field-input\" [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\"\r\n [min]=\"config.numberConfig?.min ?? null\" [max]=\"config.numberConfig?.max ?? null\"\r\n [step]=\"config.numberConfig?.step || 1\" [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Date Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isDateField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input matInput [matDatepicker]=\"datePicker\" class=\"field-input date-input has-icon-right\"\r\n [formControlName]=\"config.name!\" [min]=\"config.dateConfig?.minDate\" [max]=\"config.dateConfig?.maxDate\"\r\n [class.is-invalid]=\"errorMessage\" [placeholder]=\"config.placeholder || ''\" [readonly]=\"config.readonly\"\r\n (click)=\"!config.readonly && datePicker.open()\">\r\n <div class=\"date-icon-wrapper position-absolute d-flex align-items-center justify-content-center\"\r\n *ngIf=\"!config.readonly\">\r\n <mat-datepicker-toggle matSuffix [for]=\"datePicker\"></mat-datepicker-toggle>\r\n </div>\r\n <mat-datepicker #datePicker></mat-datepicker>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Time Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isTimeField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input type=\"time\" class=\"field-input time-input\" [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\" [readonly]=\"!!config.readonly\">\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Autocomplete \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isAutocomplete\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Hidden real control (stores the code value) -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <div class=\"autocomplete-wrapper position-relative d-flex align-items-center w-100\"\r\n [class.is-invalid]=\"errorMessage\" [class.readonly]=\"config.readonly\">\r\n <!-- Search icon -->\r\n <mat-icon class=\"ac-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n\r\n <input class=\"field-input ac-input\" [formControl]=\"autocompleteInputCtrl\" [matAutocomplete]=\"auto\"\r\n [placeholder]=\"config.placeholder || 'Search\u2026'\" [readonly]=\"!!config.readonly\" [class.is-invalid]=\"errorMessage\"\r\n (blur)=\"onAutocompleteClear()\" autocomplete=\"off\">\r\n\r\n <!-- Clear button -->\r\n <button type=\"button\"\r\n class=\"ac-clear-btn position-absolute d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none c-9CA3AF p-0-2rem\"\r\n *ngIf=\"autocompleteInputCtrl.value && !config.readonly\"\r\n (click)=\"autocompleteInputCtrl.setValue(''); updateValue(null)\" tabindex=\"-1\" aria-label=\"Clear\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n\r\n <mat-autocomplete #auto=\"matAutocomplete\" [panelWidth]=\"'auto'\">\r\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.label\"\r\n (onSelectionChange)=\"onAutocompleteSelected(option)\">\r\n <span class=\"ac-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Dynamic display fields (image / email / phone / text) -->\r\n <div class=\"ac-display-fields d-flex flex-wrap gap-6 mt-2\" *ngIf=\"option['displayMeta']?.length\">\r\n <ng-container *ngFor=\"let field of option['displayMeta']\">\r\n\r\n <!-- Image avatar -->\r\n <span *ngIf=\"field.type === 'image' && field.value\" class=\"ac-df-item ac-df-image\">\r\n <img [src]=\"field.value\" [alt]=\"field.label || 'image'\" class=\"ac-df-avatar\">\r\n </span>\r\n\r\n <!-- Email -->\r\n <span *ngIf=\"field.type === 'email' && field.value\" class=\"ac-df-item ac-df-chip\">\r\n <mat-icon class=\"ac-df-icon\">mail_outline</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Phone -->\r\n <span *ngIf=\"field.type === 'phone' && field.value\" class=\"ac-df-item ac-df-chip\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\">phone</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Custom / Icon-based / Generic Text -->\r\n <span *ngIf=\"field.type !== 'image' && field.type !== 'email' && field.type !== 'phone' && field.value\" \r\n class=\"ac-df-item\" [class.ac-df-chip]=\"!!field.icon\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\" *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n </ng-container>\r\n </div>\r\n </mat-option>\r\n <mat-option *ngIf=\"filteredOptions.length === 0\" disabled class=\"ac-no-results fs-0-8125rem c-6B7280\">\r\n No results found\r\n </mat-option>\r\n </mat-autocomplete>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Dropdown \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isDropdown\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <select *ngIf=\"config.subType === 'SINGLE'\" class=\"field-input\" [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\">\r\n <option [ngValue]=\"null\" disabled selected>{{ config.placeholder || 'Select' }}</option>\r\n <option *ngFor=\"let option of config.optionConfig?.optionList\" [value]=\"option.code\">\r\n {{ option.label }}\r\n </option>\r\n </select>\r\n\r\n <!-- MULTIPLE SELECT: custom panel with checkboxes -->\r\n <div *ngIf=\"config.subType === 'MULTIPLE'\" class=\"multi-select-wrapper\"\r\n [class.is-invalid]=\"errorMessage\">\r\n\r\n <div class=\"field-input multi-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\r\n [class.ms-open]=\"isMultiDropdownOpen\"\r\n (click)=\"toggleMultiDropdown($event)\">\r\n <span *ngIf=\"multiSelectedCount > 0\" class=\"multi-select-value fs-0-9rem\">\r\n {{ multiSelectedCount }} selected\r\n </span>\r\n <span *ngIf=\"multiSelectedCount === 0\" class=\"multi-select-placeholder\">\r\n {{ config.placeholder || selectPlaceholderLabel }}\r\n </span>\r\n <mat-icon class=\"multi-select-arrow\">\r\n {{ isMultiDropdownOpen ? expandLessLabel : expandMoreLabel }}\r\n </mat-icon>\r\n </div>\r\n\r\n <div class=\"multi-select-panel\" *ngIf=\"isMultiDropdownOpen\"\r\n (click)=\"$event.stopPropagation()\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\r\n class=\"multi-select-option d-flex align-items-center gap-8 cursor-pointer\">\r\n <input type=\"checkbox\"\r\n [checked]=\"isChecked(option.code)\"\r\n [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\r\n </label>\r\n <div *ngIf=\"!config.optionConfig?.optionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noOptionsAvailableLabel }}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Radio \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRadio\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"radio-group\" [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\" class=\"radio-label\"\r\n [class.card-item]=\"config.subType === 'CARD'\"\r\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"radio\" [formControlName]=\"config.name!\" [value]=\"option.code\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Checkbox \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label && config.subType === 'LIST'\"\r\n class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\r\n <label class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span>{{ config.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <div *ngIf=\"config.subType === 'LIST' || config.subType === 'CARD'\" class=\"checkbox-group d-flex flex-column gap-8\"\r\n [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\r\n class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\"\r\n [class.card-item]=\"config.subType === 'CARD'\" [class.selected]=\"isChecked(option.code)\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Chip \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isChip\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"chip-group d-flex flex-wrap gap-8\" [class.is-invalid]=\"errorMessage\">\r\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\r\n class=\"chip-label cursor-pointer p-6px-14px b-ffffff c-374151 b-1px-solid-D1D5DB br-20px fs-0-875rem\"\r\n [class.selected]=\"isChecked(option.code)\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\" hidden>\r\n <span>{{ option.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Switch \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isSwitch\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label class=\"switch-container d-flex justify-content-between align-items-center cursor-pointer\">\r\n <span class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label }}</span>\r\n <div class=\"switch position-relative\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span class=\"slider position-absolute cursor-pointer\"></span>\r\n </div>\r\n </label>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Rich Text \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRichText\" class=\"field-wrapper component-rich-text d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rich-text-container\" [class.is-invalid]=\"errorMessage\">\r\n <quill-editor [formControlName]=\"config.name!\" class=\"rich-text-editor d-block w-100\"\r\n [placeholder]=\"config.richTextConfig?.placeholder || config.placeholder || ''\"\r\n [styles]=\"{height: config.richTextConfig?.height || '200px'}\">\r\n </quill-editor>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Rating \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isRating\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rating-group d-flex gap-4\" [class.is-invalid]=\"errorMessage\">\r\n <span *ngFor=\"let star of getStarArray()\" class=\"star d-inline-flex align-items-center cursor-pointer\"\r\n [class.filled]=\"isStarFilled(star)\" [class.half]=\"isStarHalf(star)\" (click)=\"onRatingChange(star, $event)\">\r\n <mat-icon>{{ isStarFilled(star) || isStarHalf(star) ? 'star' : 'star_border' }}</mat-icon>\r\n </span>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Generated Field (read-only) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isGenerated\" class=\"field-wrapper d-flex flex-column gap-6\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label\r\n }}</label>\r\n <div class=\"generated-value fs-0-875rem p-0-625rem-0-875rem b-F3F4F6 b-1px-solid-E5E7EB br-8px c-6B7280\">{{ value ||\r\n '-' }}</div>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint\">{{ config.hint }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 File Upload \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isFileUpload\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Drop Zone -->\r\n <div\r\n class=\"upload-drop-zone d-flex flex-column align-items-center justify-content-center gap-8 cursor-pointer text-center p-32px-24px us-none\"\r\n [class.drag-over]=\"isDragOver\" [class.has-files]=\"value?.length\" [class.is-invalid]=\"errorMessage\"\r\n (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onFileDrop($event)\"\r\n (click)=\"fileInput.click()\">\r\n\r\n <!-- Icon with accent-colour pill background -->\r\n <div class=\"upload-icon-wrap mb-4\">\r\n <div class=\"dropzone-icon-pill d-flex align-items-center justify-content-center\">\r\n <mat-icon class=\"upload-cloud-icon\">cloud_upload</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <p class=\"upload-main-text font-weight-600 m-0 fs-0-9rem\">Drag & drop files here</p>\r\n <p class=\"upload-sub-text m-0 fs-0-8rem c-64748B\">or <span class=\"upload-link\">Browse files</span></p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"config.attachmentConfig?.acceptLabel\">\r\n Supported: <span class=\"upload-formats font-weight-500\">{{ config.attachmentConfig!.acceptLabel }}</span>\r\n </p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"!config.attachmentConfig?.acceptLabel && config.hint\">\r\n {{ config.hint }}\r\n </p>\r\n <span class=\"upload-size-badge fs-0-72rem\" *ngIf=\"config.attachmentConfig?.maxSizeMB\">\r\n Max {{ config.attachmentConfig!.maxSizeMB }}MB\r\n </span>\r\n\r\n <!-- Hidden native file input -->\r\n <input #fileInput type=\"file\" hidden [attr.multiple]=\"config.attachmentConfig?.multiple ? true : null\"\r\n [attr.accept]=\"config.attachmentConfig?.accept || null\" (change)=\"onFileSelected($event)\">\r\n </div>\r\n\r\n <!-- Uploaded file list -->\r\n <div class=\"uploaded-list d-flex flex-column gap-8 mt-10\" *ngIf=\"value?.length\">\r\n <div *ngFor=\"let f of value; let i = index\"\r\n class=\"uploaded-item d-flex align-items-center gap-10 p-10px-14px br-8px\"\r\n [class.uploading]=\"f.isUploading\">\r\n\r\n <!-- Uploading spinner -->\r\n <ng-container *ngIf=\"f.isUploading; else fileReady\">\r\n <div class=\"upload-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size uploading-label fs-0-72rem\">Uploading...</span>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Normal state once upload is done -->\r\n <ng-template #fileReady>\r\n <mat-icon class=\"file-type-icon\">{{ getFileIcon(f.type) }}</mat-icon>\r\n <img *ngIf=\"f.type?.startsWith('image') && f.dataUrl\" [src]=\"f.dataUrl\" class=\"file-thumb rounded-4\"\r\n alt=\"preview\">\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size fs-0-72rem\">{{ formatFileSize(f.size) }}</span>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Compact icon-only remove button -->\r\n <button type=\"button\" class=\"file-remove-btn d-flex align-items-center justify-content-center rounded-50\"\r\n [disabled]=\"f.isUploading\" (click)=\"!f.isUploading && removeUploadedFile(i)\"\r\n aria-label=\"Remove file\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Validation / file errors -->\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"fileUploadError\">{{ fileUploadError }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage && !fileUploadError\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\"\r\n *ngIf=\"config.hint && !errorMessage && !fileUploadError && !config.attachmentConfig?.acceptLabel\">\r\n {{ config.hint }}\r\n </span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Media Upload (Type 2) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isMediaUpload\" class=\"field-wrapper media-upload-wrapper d-flex flex-column gap-6 p-0 border-none b-none\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Hidden file input lives outside *ngIf \u2014 triggered via ViewChild -->\r\n <input #mediaDeviceInput type=\"file\" hidden multiple accept=\"image/*\" (change)=\"onMediaFileSelected($event)\">\r\n\r\n <!-- Two-column layout -->\r\n <div class=\"mu-layout d-grid align-items-start\">\r\n\r\n <!-- \u2500\u2500 LEFT PANEL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"mu-left d-flex flex-column gap-20\">\r\n\r\n <!-- Header: title + max-items badge -->\r\n <div class=\"mu-header d-flex align-items-start flex-wrap gap-10\">\r\n <h3 class=\"mu-title m-0 c-111827 fs-1-35rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </h3>\r\n <span\r\n class=\"mu-badge d-inline-flex align-items-center rounded-20 color-white font-weight-600 fs-0-72rem p-4px-12px b-111827\"\r\n *ngIf=\"config.attachmentConfig?.maxFiles\">\r\n {{ controller.labels['LBL_MEDIA_MAX_PREFIX'] || 'Maximum' }}\r\n {{ config.attachmentConfig?.maxFiles }}\r\n {{ controller.labels['LBL_MEDIA_MAX_SUFFIX'] || 'Image & Videos' }}\r\n </span>\r\n </div>\r\n\r\n <!-- Description -->\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\" *ngIf=\"config.attachmentConfig?.description\">\r\n {{ config.attachmentConfig?.description }}\r\n </p>\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\"\r\n *ngIf=\"!config.attachmentConfig?.description && controller.labels['LBL_MEDIA_DESC']\">\r\n {{ controller.labels['LBL_MEDIA_DESC'] }}\r\n </p>\r\n\r\n <!-- Feature bullet list -->\r\n <ul class=\"mu-features m-0 p-0 d-flex flex-column gap-8 ls-none\"\r\n *ngIf=\"config.attachmentConfig?.features?.length || controller.labels['LBL_MEDIA_FEATURE_1']\">\r\n <ng-container *ngIf=\"config.attachmentConfig?.features?.length\">\r\n <li *ngFor=\"let f of config.attachmentConfig?.features\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ f }}\r\n </li>\r\n </ng-container>\r\n <ng-container *ngIf=\"!config.attachmentConfig?.features?.length\">\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_1']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_1'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_2']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_2'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_3']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_3'] }}\r\n </li>\r\n </ng-container>\r\n </ul>\r\n\r\n <!-- Backdrop to close dropdown on outside click -->\r\n <div class=\"media-menu-backdrop\" *ngIf=\"showMediaMenu\"\r\n (click)=\"$event.stopPropagation(); showMediaMenu = false\"></div>\r\n\r\n <!-- Add Media button + dropdown -->\r\n <div class=\"media-add-container position-relative\" (click)=\"showMediaMenu = !showMediaMenu\">\r\n <lib-button id=\"btn-add-media-{{ config.name }}\" [variant]=\"'warning'\"\r\n [icon]=\"{type: 'material', value: 'add_photo_alternate'}\">\r\n {{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}\r\n <mat-icon class=\"menu-chevron fs-18px\">add</mat-icon>\r\n </lib-button>\r\n\r\n <div class=\"media-dropdown position-absolute rounded-12 overflow-hidden b-ffffff b-1px-solid-E5E7EB\"\r\n *ngIf=\"showMediaMenu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\r\n <!-- Video -->\r\n <button id=\"btn-media-video-{{ config.name }}\" type=\"button\"\r\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\r\n (click)=\"onMediaMenuVideo(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\n class=\"media-drop-icon media-drop-icon--video d-flex align-items-center justify-content-center rounded-8\"><mat-icon>videocam</mat-icon></span>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_VIDEO'] || 'Video' }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_VIDEO_DESC'] || 'Add\r\n YouTube URL'\r\n }}</span>\r\n </span>\r\n </button>\r\n <!-- Device -->\r\n <button id=\"btn-media-device-{{ config.name }}\" type=\"button\"\r\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\r\n (click)=\"onMediaMenuDevice(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\n class=\"media-drop-icon media-drop-icon--device d-flex align-items-center justify-content-center rounded-8\"><mat-icon>upload</mat-icon></span>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_DEVICE'] || 'Upload from device'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_DEVICE_DESC'] ||\r\n 'Select images from your\r\n computer' }}</span>\r\n </span>\r\n </button>\r\n <!-- Library -->\r\n <button id=\"btn-media-library-{{ config.name }}\" type=\"button\"\r\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\r\n (click)=\"onMediaMenuLibrary(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\n class=\"media-drop-icon media-drop-icon--library d-flex align-items-center justify-content-center rounded-8\"><mat-icon>photo_library</mat-icon></span>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_LIBRARY'] || 'Upload from library'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_LIBRARY_DESC'] ||\r\n 'Choose from default\r\n images' }}</span>\r\n </span>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- YouTube URL Input (inline below button) -->\r\n <div class=\"youtube-input-panel d-flex flex-column gap-8 p-16 rounded-10 b-FFFAF1 b-1px-solid-E5E7EB\"\r\n *ngIf=\"showYoutubeInput\">\r\n <label class=\"youtube-panel-label d-flex align-items-center gap-6 font-weight-600 fs-0-875rem c-111827\">\r\n {{ controller.labels['LBL_YOUTUBE_URL'] || 'Video URL' }}\r\n </label>\r\n <div class=\"youtube-input-row d-flex gap-8\">\r\n <input id=\"input-youtube-url-{{ config.name }}\" type=\"url\" class=\"field-input youtube-url-input\"\r\n [(ngModel)]=\"youtubeUrlInput\" [ngModelOptions]=\"{standalone: true}\"\r\n [placeholder]=\"controller.labels['PH_YOUTUBE_URL'] || 'Video URL'\" (keyup.enter)=\"addYoutubeMedia()\">\r\n <lib-button id=\"btn-add-youtube-{{ config.name }}\" [variant]=\"'secondary'\" (click)=\"addYoutubeMedia()\">\r\n {{ controller.labels['LBL_ADD'] || 'Add' }}\r\n </lib-button>\r\n </div>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"youtubeUrlError\">{{ youtubeUrlError }}</span>\r\n </div>\r\n\r\n <div\r\n class=\"media-upload-status d-flex align-items-center gap-8 mt-4 color-error rounded-8 font-weight-500 p-10px-14px b-FEF2F2 fs-0-85rem\"\r\n *ngIf=\"mediaUploadError\">\r\n <mat-icon class=\"status-icon fs-18px\">error_outline</mat-icon>\r\n <span>{{ mediaUploadError }}</span>\r\n </div>\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n </div>\r\n <!-- end left panel -->\r\n\r\n <!-- \u2500\u2500 RIGHT PANEL (carousel) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"mu-right d-flex flex-column gap-12\">\r\n\r\n <!-- Carousel (when items exist) -->\r\n <div class=\"media-carousel-section d-flex flex-column gap-12\" *ngIf=\"mediaItems.length\">\r\n <div\r\n class=\"media-carousel-main position-relative w-100 overflow-hidden d-flex align-items-center justify-content-center br-12px b-0F172A\">\r\n <button id=\"btn-carousel-prev-{{ config.name }}\" type=\"button\"\r\n class=\"carousel-nav carousel-nav--prev position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\r\n (click)=\"mediaCarouselPrev()\" [disabled]=\"mediaCarouselIndex === 0\" aria-label=\"Previous\">\r\n <mat-icon>chevron_left</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-viewer position-absolute d-flex align-items-center justify-content-center\"\r\n *ngFor=\"let item of mediaItems; let i = index\" [hidden]=\"i !== mediaCarouselIndex\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"carousel-uploading d-flex flex-column align-items-center gap-12 c-94A3B8 fs-0-85rem\">\r\n <div class=\"carousel-spinner rounded-50 b-3px-solid-rgba-255-255-255-0-15\"></div>\r\n <span>{{ controller.labels['LBL_UPLOADING'] || 'Uploading\u2026' }}</span>\r\n </div>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'youtube'\">\r\n <iframe class=\"carousel-iframe w-100 h-100 br-12px\" [src]=\"item.url | trustedUrl\" frameborder=\"0\"\r\n allowfullscreen\r\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\">\r\n </iframe>\r\n </ng-container>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'image'\">\r\n <img class=\"carousel-image w-100 h-100 br-12px\" [src]=\"item.url\" alt=\"Media\">\r\n </ng-container>\r\n <button id=\"btn-remove-media-{{ config.name }}-{{ i }}\" type=\"button\"\r\n class=\"carousel-remove-btn position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-0-0-0-0-55\"\r\n [disabled]=\"item.isUploading\" (click)=\"removeMediaItem(i)\" aria-label=\"Remove\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <button id=\"btn-carousel-next-{{ config.name }}\" type=\"button\"\r\n class=\"carousel-nav carousel-nav--next position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\r\n (click)=\"mediaCarouselNext()\" [disabled]=\"mediaCarouselIndex === mediaItems.length - 1\" aria-label=\"Next\">\r\n <mat-icon>chevron_right</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-dots position-absolute d-flex gap-6\">\r\n <span *ngFor=\"let item of mediaItems; let i = index\"\r\n class=\"carousel-dot rounded-50 cursor-pointer b-rgba-255-255-255-0-45\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\"></span>\r\n </div>\r\n </div>\r\n\r\n <!-- Thumbnail strip -->\r\n <div class=\"media-thumbnail-strip d-flex flex-wrap gap-8 pb-4px\">\r\n <div *ngFor=\"let item of mediaThumbnails; let i = index\"\r\n class=\"media-thumb rounded-8 overflow-hidden cursor-pointer d-flex align-items-center justify-content-center b-2px-solid-transparent b-E2E8F0\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"thumb-uploading d-flex align-items-center justify-content-center w-100 h-100\">\r\n <div class=\"thumb-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && item.thumbnailUrl\"\r\n [src]=\"item.thumbnailUrl\" class=\"thumb-img w-100 h-100\" alt=\"Video thumbnail\">\r\n <div *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && !item.thumbnailUrl\"\r\n class=\"thumb-yt-placeholder d-flex align-items-center justify-content-center w-100 h-100 b-1E293B c-EF4444\">\r\n <mat-icon>play_circle</mat-icon>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'image' && item.url\" [src]=\"item.url\"\r\n class=\"thumb-img w-100 h-100\" alt=\"Image thumbnail\">\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty right-side placeholder -->\r\n <div\r\n class=\"mu-right-empty d-flex flex-column align-items-center justify-content-center gap-10 h-100 text-center p-24 br-12px b-FFFAF1 c-94A3B8 b-2px-dashed-CBD5E1\"\r\n *ngIf=\"!mediaItems.length\" (click)=\"onMediaMenuDevice()\">\r\n <mat-icon class=\"mu-right-empty-icon fs-52px\">perm_media</mat-icon>\r\n <p>{{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}</p>\r\n </div>\r\n\r\n </div>\r\n <!-- end right panel -->\r\n\r\n </div><!-- end mu-layout -->\r\n </div>\r\n\r\n\r\n <!-- \u2550\u2550 Library Image Picker Modal \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <!-- Wrapper is always in DOM (hidden) so @ViewChild can move it to body -->\r\n <div #libraryModal class=\"media-library-portal-host\" [class.is-open]=\"showLibraryModal\">\r\n\r\n <!-- Backdrop -->\r\n <div class=\"media-library-overlay\" (click)=\"closeLibraryModal()\"></div>\r\n\r\n <!-- Modal card -->\r\n <div class=\"media-library-modal d-flex flex-column overflow-hidden b-ffffff br-16px\"\r\n role=\"dialog\" aria-modal=\"true\">\r\n <div class=\"library-modal-header d-flex align-items-start justify-content-between p-24px-28px bb-1px-solid-E5E7EB\">\r\n <div class=\"header-left d-flex flex-column gap-8\">\r\n <h3 class=\"library-modal-title m-0 color-dark fs-1-25rem\">\r\n {{ controller.labels['LBL_ADD_IMAGES'] || 'Add Images' }}\r\n </h3>\r\n <p class=\"library-modal-subtitle m-0 color-gray fs-0-85rem\">\r\n {{ controller.labels['LBL_LIBRARY_MODAL_DESC'] || 'Select images from your library.' }}\r\n </p>\r\n </div>\r\n <button id=\"btn-close-library-{{ config.name }}\" type=\"button\"\r\n class=\"library-close-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 border-none b-none c-9CA3AF\"\r\n (click)=\"closeLibraryModal()\" aria-label=\"Close\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Loading -->\r\n <div class=\"library-loading\" *ngIf=\"libraryLoading\">\r\n <div class=\"lib-spinner rounded-50 b-3px-solid-E2E8F0\"></div>\r\n <span>{{ controller.labels['LBL_LOADING'] || 'Loading\u2026' }}</span>\r\n </div>\r\n\r\n <!-- Error -->\r\n <div class=\"library-error d-flex align-items-center gap-8 color-error b-FEF2F2 fs-0-875rem p-16px-24px\"\r\n *ngIf=\"libraryError && !libraryLoading\">\r\n <mat-icon>error_outline</mat-icon>\r\n {{ libraryError }}\r\n </div>\r\n\r\n <!-- Image grid -->\r\n <div class=\"library-grid d-grid gap-16 flex-1 p-28px b-F9FAFB\" *ngIf=\"!libraryLoading && libraryImages.length\">\r\n <div *ngFor=\"let img of libraryImages; let li = index\" id=\"lib-img-{{ config.name }}-{{ li }}\"\r\n class=\"library-grid-item position-relative rounded-12 overflow-hidden cursor-pointer bg-white b-3px-solid-transparent\"\r\n [class.selected]=\"isLibraryItemSelected(img)\" (click)=\"toggleLibraryItem(img)\">\r\n <img [src]=\"getLibraryItemUrl(img)\" class=\"library-grid-img w-100 h-100 d-block\" alt=\"Library image\">\r\n <div\r\n class=\"library-check position-absolute bg-white rounded-50 d-flex align-items-center justify-content-center c-3B82F6\"\r\n *ngIf=\"isLibraryItemSelected(img)\">\r\n <mat-icon>check_circle</mat-icon>\r\n </div>\r\n <div class=\"library-overlay-hover position-absolute b-rgba-59-130-246-0-12\"></div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty library -->\r\n <div\r\n class=\"library-empty d-flex flex-column align-items-center justify-content-center gap-12 flex-1 c-9CA3AF fs-0-875rem p-48px-24px\"\r\n *ngIf=\"!libraryLoading && !libraryError && libraryImages.length === 0\">\r\n <mat-icon>image_not_supported</mat-icon>\r\n <span>{{ controller.labels['LBL_LIBRARY_EMPTY'] || 'No images found in library.' }}</span>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"library-modal-footer d-flex align-items-center justify-content-end bg-white p-20px-28px bt-1px-solid-E5E7EB\">\r\n <div class=\"library-footer-actions d-flex gap-12\">\r\n <lib-button id=\"btn-library-cancel-{{ config.name }}\" [variant]=\"'outline'\" (click)=\"closeLibraryModal()\">\r\n {{ controller.labels['LBL_CANCEL'] || 'Cancel' }}\r\n </lib-button>\r\n <lib-button id=\"btn-library-confirm-{{ config.name }}\" [variant]=\"'primary'\"\r\n [disabled]=\"librarySelectedIds.size === 0\" (click)=\"confirmLibrarySelection()\">\r\n {{ controller.labels['LBL_CONTINUE'] || 'Continue' }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n\r\n\r\n <!-- \u2550\u2550 Location Field \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\r\n <div *ngIf=\"isLocation\" class=\"field-wrapper location-field-wrapper d-flex flex-column gap-6 gap-12\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Field label -->\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n <p class=\"location-subtitle m-0 c-6B7280 fs-0-8125rem\" *ngIf=\"config.hint\">{{ config.hint }}</p>\r\n\r\n <!-- Three-tab bar -->\r\n <div class=\"location-tabs d-flex gap-12 mb-24\">\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'VENUE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('VENUE')\">\r\n {{ controller.labels['LBL_LOC_VENUE'] || 'Venue' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'ONLINE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('ONLINE')\">\r\n {{ controller.labels['LBL_LOC_ONLINE'] || 'Online Event' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'TBA' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('TBA')\">\r\n {{ controller.labels['LBL_LOC_TBA'] || 'To be Announced' }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- VENUE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'VENUE'\" class=\"loc-panel loc-venue-panel d-flex flex-column gap-12\">\r\n\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ADDRESS'] || 'Location address' }}\r\n </p>\r\n\r\n <!-- Added venue rows -->\r\n <div class=\"loc-venue-list d-flex flex-column gap-8\" *ngIf=\"locationVenues.length > 0\">\r\n <div *ngFor=\"let venue of locationVenues; let i = index\"\r\n class=\"loc-venue-item d-flex align-items-center gap-10 p-10px-14px br-7px b-ffffff b-1px-solid-D1D5DB\">\r\n <mat-icon class=\"loc-venue-search-icon fs-18px c-9CA3AF\">search</mat-icon>\r\n <span class=\"loc-venue-text flex-1 overflow-hidden fs-0-875rem c-111827\">{{ venue.address || venue.name ||\r\n venue.description }}</span>\r\n <button type=\"button\"\r\n class=\"loc-action-btn loc-delete-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none p-4px\"\r\n (click)=\"removeLocationVenue(i)\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Location count badge -->\r\n <p class=\"loc-count-text m-0 font-weight-600 fs-0-8125rem c-3B82F6\"\r\n *ngIf=\"locationVenues.length > 0 && config.locationConfig?.allowMulti\">\r\n {{ locationVenues.length }} {{ controller.labels['LBL_LOC_COUNT_SUFFIX'] || 'Locations Added!' }}\r\n </p>\r\n\r\n <!-- Search input (hide when max reached) -->\r\n <div class=\"loc-search-container position-relative\" *ngIf=\"!locationMaxReached\">\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n <input\r\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\r\n [placeholder]=\"config.locationConfig?.venuePlaceholder || (controller.labels['PH_LOC_VENUE'] || 'Type to search venue...')\"\r\n [value]=\"locationSearchText\" (input)=\"handleLocationSearchInput($event)\" (blur)=\"hideLocationSuggestions()\"\r\n autocomplete=\"off\" [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n <!-- Suggestions dropdown -->\r\n <div class=\"loc-suggestions-panel position-absolute overflow-hidden br-8px b-ffffff b-1px-solid-D1D5DB\"\r\n *ngIf=\"locationShowSuggestions && locationSuggestions.length\">\r\n <div *ngFor=\"let sug of locationSuggestions\"\r\n class=\"loc-suggestion-item d-flex align-items-center gap-10 cursor-pointer p-10px-14px\"\r\n (mousedown)=\"onLocationSuggestionSelect(sug)\">\r\n <mat-icon class=\"loc-suggestion-icon fs-18px c-E53E3E\">place</mat-icon>\r\n <span class=\"loc-suggestion-text overflow-hidden fs-0-875rem c-374151\">{{ sug.description }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Add another button -->\r\n <button type=\"button\"\r\n class=\"loc-add-btn d-inline-flex align-items-center gap-6 cursor-pointer font-weight-600 p-0 b-none border-none fs-0-875rem c-1A56DB\"\r\n *ngIf=\"locationVenues.length > 0 && !locationMaxReached && config.locationConfig?.allowMulti\">\r\n <mat-icon>add_circle_outline</mat-icon>\r\n <span>{{ controller.labels['LBL_LOC_ADD_ANOTHER'] || 'Add another Location' }}</span>\r\n </button>\r\n\r\n <!-- Map -->\r\n <div class=\"loc-map-container overflow-hidden br-10px b-1px-solid-E5E7EB\"\r\n *ngIf=\"config.locationConfig?.showMap !== false\">\r\n <ng-container *ngIf=\"config.locationConfig?.googleMapsApiKey; else simpleEmbed\">\r\n <div [id]=\"'loc-map-' + config.name\" class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\"></div>\r\n </ng-container>\r\n <ng-template #simpleEmbed>\r\n <iframe class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\" [src]=\"getLocationMapEmbedUrl() | trustedUrl\"\r\n frameborder=\"0\" allowfullscreen loading=\"lazy\">\r\n </iframe>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- Map hint -->\r\n <p class=\"loc-map-hint m-0 text-center fs-0-78rem c-6B7280\">\r\n {{ controller.labels['LBL_LOC_MAP_HINT'] || 'Type the venue address. A map will appear to assist you.' }}\r\n </p>\r\n </div>\r\n\r\n <!-- ONLINE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'ONLINE'\" class=\"loc-panel loc-online-panel d-flex flex-column gap-12\">\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ONLINE_URL'] || 'Event URL' }}\r\n </p>\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">link</mat-icon>\r\n <input\r\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\r\n type=\"url\"\r\n [placeholder]=\"config.locationConfig?.onlinePlaceholder || (controller.labels['PH_LOC_ONLINE'] || 'https://zoom.us/j/...')\"\r\n [value]=\"locationOnlineUrl\" (input)=\"onLocationUrlChange($any($event.target).value)\"\r\n [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n </div>\r\n\r\n <!-- TBA TAB -->\r\n <div *ngIf=\"locationActiveTab === 'TBA'\"\r\n class=\"loc-panel loc-tba-panel d-flex flex-column gap-12 justify-content-center\">\r\n <div\r\n class=\"loc-tba-content d-flex flex-column align-items-center justify-content-center text-center gap-12 p-32px-24px b-F9FAFB b-1px-dashed-D1D5DB br-10px\">\r\n <mat-icon class=\"loc-tba-icon fs-40px c-9CA3AF\">schedule</mat-icon>\r\n <p class=\"loc-tba-text m-0 c-6B7280 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_TBA_DESC'] || \"This event's location is yet to be announced. Check back later\r\n for updates.\" }}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <!-- Hidden real form control -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n</div>", styles: [".d-flex{display:flex}.d-inline-flex{display:inline-flex}.d-grid{display:grid}.d-block{display:block}.d-none{display:none}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.align-items-center{align-items:center}.align-items-start{align-items:flex-start}.align-items-end{align-items:flex-end}.justify-content-center{justify-content:center}.justify-content-between{justify-content:space-between}.justify-content-start{justify-content:flex-start}.justify-content-end{justify-content:flex-end}.grid-cols-12{grid-template-columns:repeat(12,1fr)}.w-100{width:100%}.h-100{height:100%}.position-relative{position:relative}.position-absolute{position:absolute}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-poppins{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)}.font-weight-400{font-weight:400}.font-weight-500{font-weight:500}.font-weight-600{font-weight:600}.text-13{font-size:13px}.text-14{font-size:14px}.text-16{font-size:16px}.color-white{color:#fff}.color-dark{color:#111827}.color-gray{color:#6b7280}.color-error{color:var(--cc-sf-error-text-color, #DC2626)}.bg-white{background-color:#fff}.bg-transparent{background-color:transparent}.m-0{margin:0}.mt-4{margin-top:4px}.mt-8{margin-top:8px}.mt-10{margin-top:10px}.mt-12{margin-top:12px}.mt-16{margin-top:16px}.mt-20{margin-top:20px}.mt-24{margin-top:24px}.mb-0{margin-bottom:0}.mb-4{margin-bottom:4px}.mb-8{margin-bottom:8px}.mb-10{margin-bottom:10px}.mb-12{margin-bottom:12px}.mb-16{margin-bottom:16px}.mb-20{margin-bottom:20px}.mb-24{margin-bottom:24px}.ml-16{margin-left:16px}.ml-20{margin-left:20px}.p-0{padding:0}.p-16{padding:16px}.p-20{padding:20px}.p-24{padding:24px}.pt-20{padding-top:20px}.pb-10{padding-bottom:10px}.gap-4{gap:4px}.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-16{gap:16px}.gap-20{gap:20px}.rounded-4{border-radius:4px}.rounded-6{border-radius:6px}.rounded-8{border-radius:8px}.rounded-10{border-radius:10px}.rounded-12{border-radius:12px}.rounded-20{border-radius:20px}.rounded-24{border-radius:24px}.rounded-50{border-radius:50%}.cursor-pointer{cursor:pointer}.overflow-hidden{overflow:hidden}.resize-vertical{resize:vertical}.box-sizing-border{box-sizing:border-box}.border-none{border:none!important}.mb-16px{margin-bottom:var(--cc-sf-grid-gap, 16px)!important}.c-DC2626{color:var(--cc-sf-label-required-color, #DC2626)!important}.ml-0-125rem{margin-left:.125rem!important}.fs-0-875rem{font-size:.875rem!important}.c-111827{color:var(--cc-sf-label-color, #111827)!important}.br-7px{border-radius:var(--cc-sf-input-radius, 7px)!important}.c-6B7280{color:var(--cc-sf-hint-color, #6B7280)!important}.fs-0-75rem{font-size:var(--cc-sf-error-text-size, .75rem)!important}.b-none{background:none!important}.p-32px-24px{padding:32px 24px!important}.us-none{-webkit-user-select:none!important;user-select:none!important}.c-1E293B{color:var(--cc-sf-label-color, #1E293B)!important}.c-3B82F6{color:var(--cc-sf-chip-selected-bg, #3B82F6)!important}.fs-0-78rem{font-size:.78rem!important}.p-10px-14px{padding:10px 14px!important}.fs-0-85rem{font-size:.85rem!important}.fs-0-72rem{font-size:.72rem!important}.c-94A3B8{color:#94a3b8!important}.p-4px{padding:4px!important}.br-8px{border-radius:var(--cc-sf-input-radius, 8px)!important}.bc-F3F4F6{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.br-none{border-right:none!important}.bl-none{border-left:none!important}.pe-none{pointer-events:none!important}.fs-1-1rem{font-size:1.1rem!important}.c-9CA3AF{color:var(--cc-sf-hint-color, #9CA3AF)!important}.pl-2-4rem{padding-left:2.4rem!important}.fs-0-8125rem{font-size:.8125rem!important}.ls-none{list-style:none!important}.br-12px{border-radius:var(--mu-carousel-radius, 12px)!important}.b-FFFAF1{background:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.fs-18px{font-size:18px!important}.b-FEF2F2{background:var(--cc-sf-error-bg, #FEF2F2)!important}.bc-DC2626{border-color:var(--cc-sf-error-border, #DC2626)!important}.c-202124{color:var(--cc-sf-label-color, #202124)!important}.fs-18px{font-size:var(--cc-sf-label-size, 18px)!important}.mb-0-5rem{margin-bottom:.5rem!important}.pt-0-625rem{padding-top:var(--cc-sf-input-padding-y, .625rem)!important}.pb-0-625rem{padding-bottom:var(--cc-sf-input-padding-y, .625rem)!important}.pl-16px{padding-left:var(--cc-sf-input-padding-x, 16px)!important}.pr-16px{padding-right:var(--cc-sf-input-padding-x, 16px)!important}.bc-ffffff{background-color:var(--cc-sf-section-bg, #ffffff)!important}.b-1px-solid-D1D5DB{border:1px solid var(--cc-sf-input-border, #D1D5DB)!important}.fs-0-75rem{font-size:.75rem!important}.c-1F2937{color:var(--cc-sf-section-label-color, #1F2937)!important}.p-6px-14px{padding:var(--cc-sf-chip-padding, 6px 14px)!important}.b-ffffff{background:var(--loc-suggestion-bg, #ffffff)!important}.c-374151{color:var(--cc-sf-label-color, #374151)!important}.br-20px{border-radius:var(--cc-sf-chip-radius, 20px)!important}.fs-0-875rem{font-size:var(--cc-sf-btn-font-size, .875rem)!important}.bc-D1D5DB{background-color:var(--cc-sf-switch-track-off, #D1D5DB)!important}.pr-2-75rem{padding-right:2.75rem!important}.p-0-25rem{padding:.25rem!important}.p-0-625rem-0-875rem{padding:var(--cc-sf-generated-padding, .625rem .875rem)!important}.b-F3F4F6{background:var(--cc-sf-generated-bg, #F3F4F6)!important}.b-1px-solid-E5E7EB{border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.br-8px{border-radius:var(--cc-sf-uploaded-item-radius, 8px)!important}.c-6B7280{color:var(--ms-desc-color, #6B7280)!important}.mb-20px{margin-bottom:var(--cc-sf-section-gap, 20px)!important}.br-10px{border-radius:var(--cc-sf-input-radius, 10px)!important}.p-20px{padding:var(--cc-sf-section-padding, 20px)!important}.fs-1rem{font-size:1rem!important}.m-0-0-16px-0{margin:0 0 16px!important}.bb-2px-solid-E5E7EB{border-bottom:var(--cc-sf-section-label-border, 2px solid #E5E7EB)!important}.p-16px{padding:var(--cc-sf-instance-padding, 16px)!important}.b-F9FAFB{background:var(--loc-tba-bg, #F9FAFB)!important}.bb-1px-dashed-D1D5DB{border-bottom:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)!important}.c-4B5563{color:var(--cc-sf-instance-num-color, #4B5563)!important}.fs-0-8125rem{font-size:var(--cc-sf-hint-size, .8125rem)!important}.pb-0{padding-bottom:0!important}.p-18px-24px{padding:18px 24px!important}.c-111827{color:var(--ms-title-color, #111827)!important}.bt-1px-solid-E5E7EB{border-top:1px solid #E5E7EB!important}.p-4px-10px{padding:4px 10px!important}.b-FFF5F5{background:var(--cc-sf-btn-remove-bg, #FFF5F5)!important}.c-E53E3E{color:var(--loc-delete-color, #E53E3E)!important}.b-1px-solid-FED7D7{border:var(--cc-sf-btn-remove-border, 1px solid #FED7D7)!important}.br-4px{border-radius:var(--cc-sf-btn-remove-radius, 4px)!important}.p-8px-16px{padding:8px 16px!important}.b-transparent{background:var(--cc-sf-btn-add-bg, transparent)!important}.c-3B82F6{color:var(--cc-sf-input-focus-border, #3B82F6)!important}.b-1px-dashed-CBD5E1{border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1)!important}.br-6px{border-radius:var(--cc-sf-btn-add-radius, 6px)!important}.b-1-5px-dashed-CBD5E1{border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1)!important}.br-12px{border-radius:var(--cc-sf-dropzone-radius, 12px)!important}.bc-FFFAF1{background-color:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.c-94A3B8{color:var(--cc-sf-uploaded-remove-color, #94A3B8)!important}.fs-0-9rem{font-size:var(--cc-sf-input-font-size, .9rem)!important}.c-64748B{color:var(--cc-sf-dropzone-hint-color, #64748B)!important}.b-1px-solid-E2E8F0{border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0)!important}.b-2px-solid-E2E8F0{border:2px solid #E2E8F0!important}.pr-3-5rem{padding-right:3.5rem!important}.p-0-0-875rem{padding:0 .875rem!important}.bc-FFFFFF{background-color:var(--cc-sf-input-bg, #FFFFFF)!important}.b-1-5px-solid-D1D5DB{border:var(--cc-sf-input-border, 1.5px solid #D1D5DB)!important}.mb-0-75rem{margin-bottom:.75rem!important}.mt-6px{margin-top:6px!important}.pr-2-4rem{padding-right:2.4rem!important}.p-0-2rem{padding:.2rem!important}.fs-1-35rem{font-size:1.35rem!important}.p-4px-12px{padding:4px 12px!important}.b-111827{background:var(--cc-sf-label-color, #111827)!important}.b-2px-dashed-CBD5E1{border:2px dashed var(--cc-sf-dropzone-border, #CBD5E1)!important}.fs-52px{font-size:52px!important}.p-12px-16px{padding:12px 16px!important}.bb-1px-solid-F3F4F6{border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)!important}.b-0F172A{background:var(--mu-carousel-bg, #0F172A)!important}.b-3px-solid-rgba-255-255-255-0-15{border:3px solid rgba(255,255,255,.15)!important}.b-rgba-255-255-255-0-85{background:#ffffffd9!important}.b-rgba-0-0-0-0-55{background:#0000008c!important}.b-rgba-255-255-255-0-45{background:#ffffff73!important}.pb-4px{padding-bottom:4px!important}.b-2px-solid-transparent{border:2px solid transparent!important}.b-E2E8F0{background:var(--mu-thumb-bg, #E2E8F0)!important}.b-1E293B{background:#1e293b!important}.c-EF4444{color:#ef4444!important}.b-rgba-0-0-0-0-5{background:#00000080!important}.br-16px{border-radius:var(--mu-modal-radius, 16px)!important}.p-24px-28px{padding:24px 28px!important}.bb-1px-solid-E5E7EB{border-bottom:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.fs-1-25rem{font-size:1.25rem!important}.p-48px-24px{padding:48px 24px!important}.b-3px-solid-E2E8F0{border:3px solid #E2E8F0!important}.p-16px-24px{padding:16px 24px!important}.p-28px{padding:28px!important}.b-3px-solid-transparent{border:3px solid transparent!important}.b-rgba-59-130-246-0-12{background:#3b82f61f!important}.p-20px-28px{padding:20px 28px!important}.c-1A56DB{color:var(--loc-add-color, #1A56DB)!important}.b-1px-dashed-D1D5DB{border:1px dashed var(--cc-sf-input-disabled-border, #D1D5DB)!important}.fs-40px{font-size:40px!important}.c-9CA3AF{color:var(--loc-tba-icon-color, #9CA3AF)!important}.form-field{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)!important}:host{--cc-sf-input-border: #D1D5DB;--cc-sf-input-bg: #ffffff;--cc-sf-input-radius: 9px;--cc-sf-input-height: 44px;--cc-sf-label-color: #111827;--cc-sf-hint-color: #9CA3AF;--cc-sf-error-border: #EF4444;--cc-sf-error-bg: #FFF5F5;--cc-sf-accent-color: #6366F1;--cc-sf-input-focus-border: #6366F1;--cc-sf-input-hover-border: #A5B4FC;--cc-sf-input-placeholder: #C4C9D4;--cc-sf-input-disabled-bg: #F8F9FB;--cc-sf-input-disabled-border: #E5E7EB;--cc-sf-switch-track-on: #6366F1;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-thumb: #ffffff;--cc-sf-selected-color: #6366F1}.form-row{gap:var(--cc-sf-grid-gap, 16px)}.form-row.horizontal{display:flex;flex-direction:row}.form-row.horizontal>*{flex:1}.form-row:not(.horizontal){flex-direction:column}.form-row.grid-row{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.form-row.grid-row{grid-template-columns:1fr}.form-row.grid-row .row-field{grid-column:span 12!important}}.field-label{font-size:.75rem;font-weight:600;line-height:1;letter-spacing:.04em;text-transform:uppercase;color:#6b7280}.field-input,input.matInput,.mat-mdc-input-element{display:block;width:100%;height:var(--cc-sf-input-height)!important;padding:0 14px!important;font-family:inherit;font-size:.9rem;color:var(--cc-sf-label-color);background-color:var(--cc-sf-input-bg)!important;border:1.5px solid var(--cc-sf-input-border)!important;border-radius:var(--cc-sf-input-radius)!important;box-sizing:border-box;box-shadow:0 1px 2px #0000000a!important;transition:all .2s cubic-bezier(.4,0,.2,1)}.field-input::placeholder,input.matInput::placeholder,.mat-mdc-input-element::placeholder{font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder)}.field-input{opacity:var(--cc-sf-input-opacity, 1);line-height:var(--cc-sf-input-line-height, 1.5);transition:var(--cc-sf-input-transition, all .2s ease)}.field-input::placeholder{font-weight:var(--cc-sf-placeholder-weight, 400);font-size:var(--cc-sf-placeholder-size, 14px);line-height:var(--cc-sf-placeholder-line-height, 100%);color:var(--cc-sf-input-placeholder)}.field-input:hover:not(:disabled):not([readonly]){border-color:var(--cc-sf-input-hover-border)!important;box-shadow:0 1px 4px #6366f114!important}.field-input:focus{outline:none;border-color:var(--cc-sf-input-focus-border)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important;background-color:#fefeff!important}.field-input:disabled,.field-input[readonly]{background-color:var(--cc-sf-input-disabled-bg)!important;color:#9ca3af!important;cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important;box-shadow:none!important}.field-input.is-invalid{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)!important}.field-input.is-invalid:focus{box-shadow:0 0 0 3px #ef44441f,0 1px 4px #ef44441a!important}.field-input.textarea{resize:vertical;min-height:100px;height:auto;padding:12px 16px!important}input[type=time].time-input{cursor:pointer}input[type=time].time-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:.7;filter:invert(30%)}input[type=time].time-input::-webkit-calendar-picker-indicator:hover{opacity:1}select.field-input{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E\");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;cursor:pointer}select.field-input:disabled{cursor:not-allowed}.multi-select-wrapper{position:relative}.multi-select-wrapper .multi-select-trigger{min-height:var(--cc-sf-input-height, 44px);height:auto;-webkit-user-select:none;user-select:none}.multi-select-wrapper .multi-select-trigger.ms-open{border-color:var(--cc-sf-input-focus-border, #6366F1)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important}.multi-select-wrapper .multi-select-trigger .multi-select-value{flex:1;color:var(--cc-sf-label-color, #111827);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.multi-select-wrapper .multi-select-trigger .multi-select-placeholder{flex:1;font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder, #C4C9D4)}.multi-select-wrapper .multi-select-trigger .multi-select-arrow{font-size:20px;width:20px;height:20px;line-height:20px;color:#6b7280;flex-shrink:0}.multi-select-wrapper .multi-select-panel{position:absolute;top:calc(100% + 4px);left:0;right:0;background:#fff;border:1.5px solid var(--cc-sf-input-border, #D1D5DB);border-radius:var(--cc-sf-input-radius, 9px);box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f;z-index:1050;max-height:240px;overflow-y:auto}.multi-select-wrapper .multi-select-panel::-webkit-scrollbar{width:4px}.multi-select-wrapper .multi-select-panel::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:2px}.multi-select-wrapper .multi-select-option{padding:8px 12px;border-bottom:1px solid #F3F4F6;transition:background .1s}.multi-select-wrapper .multi-select-option:last-of-type{border-bottom:none}.multi-select-wrapper .multi-select-option:hover{background:#f9fafb}.multi-select-wrapper .multi-select-option input[type=checkbox]{flex-shrink:0;cursor:pointer;accent-color:var(--cc-sf-selected-color, #6366F1);width:15px;height:15px}.multi-select-wrapper .multi-select-empty{padding:12px;text-align:center}.multi-select-wrapper.is-invalid .multi-select-trigger{border-color:var(--cc-sf-error-border, #EF4444)!important;background-color:var(--cc-sf-error-bg, #FFF5F5)!important}.char-count-hint{font-style:normal;line-height:100%;letter-spacing:.02em;margin-top:.4rem}.radio-group.layout-column,.checkbox-group.layout-column{display:grid!important;grid-template-columns:repeat(12,1fr);gap:16px;width:100%}.radio-group.layout-row,.checkbox-group.layout-row{flex-direction:column!important;gap:12px;width:100%}.radio-label input,.checkbox-label input{cursor:pointer;accent-color:var(--cc-sf-chip-selected-bg, #F05A28)}.radio-label.card-item,.checkbox-label.card-item{display:flex!important;flex-direction:row-reverse!important;justify-content:space-between!important;align-items:center!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB);border-radius:12px;padding:16px 20px;box-sizing:border-box;transition:all .2s ease;background:var(--cc-sf-input-bg, #ffffff);margin-bottom:0}.radio-label.card-item input,.checkbox-label.card-item input{margin-left:16px}.radio-label.card-item.selected,.checkbox-label.card-item.selected{border-color:var(--cc-sf-selected-color);background-color:#f05a280d}.radio-label.card-item .option-content .label-text,.checkbox-label.card-item .option-content .label-text,.checkbox-single .checkbox-label{font-weight:var(--cc-sf-label-weight, 500)}.chip-label{transition:var(--cc-sf-input-transition, all .2s ease)}.chip-label:hover{background:var(--cc-sf-chip-hover-bg, #F3F4F6)}.chip-label.selected{background:var(--cc-sf-selected-color);color:var(--cc-sf-chip-selected-color, #ffffff);border-color:var(--cc-sf-selected-color)}.switch{width:50px;height:24px;display:inline-block}.switch input{opacity:0;width:0;height:0;position:absolute}.switch input:checked+.slider{background-color:var(--cc-sf-switch-track-on)!important}.switch input:checked+.slider:before{transform:translate(26px)}.switch .slider{inset:0;transition:.4s;background-color:var(--cc-sf-switch-track-off);border-radius:24px}.switch .slider:before{position:absolute;content:\"\";height:18px;width:18px;left:3px;bottom:3px;background-color:var(--cc-sf-switch-thumb);transition:.4s;border-radius:50%}.rating-group .star{transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star mat-icon{font-size:var(--cc-sf-star-size, 28px);width:var(--cc-sf-star-size, 28px);height:var(--cc-sf-star-size, 28px);line-height:var(--cc-sf-star-size, 28px);color:var(--cc-sf-star-empty, #D1D5DB);transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star.filled mat-icon,.rating-group .star.half mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.rating-group .star:hover mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.password-wrapper .password-toggle{right:.625rem;top:50%;transform:translateY(-50%);line-height:1;transition:color var(--cc-sf-input-transition, .2s ease)}.password-wrapper .password-toggle mat-icon.eye-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;line-height:1.125rem}.password-wrapper .password-toggle:hover{color:var(--cc-sf-label-color, #374151)}.password-wrapper .password-toggle:focus{outline:none}.group-section-wrapper .group-label{font-size:var(--cc-sf-section-label-size, 1rem);font-weight:var(--cc-sf-section-label-weight, 600);color:var(--cc-sf-section-label-color, #1F2937);margin:0 0 16px;padding-left:12px;padding-top:2px;padding-bottom:2px;border-left:var(--cc-sf-section-header-accent-width, 4px) solid var(--cc-sf-section-header-accent-color, #3B82F6);line-height:1.4}.group-section-wrapper .group-fields.sf-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.group-section-wrapper .group-fields.sf-grid{grid-template-columns:1fr}.group-section-wrapper .group-fields.sf-grid .sf-col{grid-column:span 12!important}}.group-section-wrapper .group-accordion-instance{border:var(--cc-sf-instance-border, 1px solid #E5E7EB);border-radius:var(--cc-sf-section-border-radius-inner, 8px);margin-bottom:8px;overflow:hidden;transition:border-color .2s ease}.group-section-wrapper .group-accordion-instance:last-of-type{margin-bottom:0}.group-section-wrapper .group-accordion-instance .group-accordion-header{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;background:var(--cc-sf-repeater-accordion-header-bg, #F9FAFB);cursor:pointer;-webkit-user-select:none;user-select:none;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header:hover{background:var(--cc-sf-repeater-accordion-active-bg, #EFF6FF)}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-badge{width:22px;height:22px;border-radius:50%;background:var(--cc-sf-repeater-badge-bg, #E5E7EB);color:var(--cc-sf-repeater-badge-color, #374151);font-size:.75rem;font-weight:600;display:flex;align-items:center;justify-content:center;flex-shrink:0}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-title{font-size:var(--cc-sf-instance-num-size, .8125rem);font-weight:600;color:var(--cc-sf-repeater-accordion-header-color, #1F2937)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn{background:none;border:none;cursor:pointer;color:var(--cc-sf-btn-remove-color, #E53E3E);padding:4px;border-radius:4px;line-height:1;display:flex;align-items:center;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-chevron{font-size:1.25rem;width:1.25rem;height:1.25rem;line-height:1.25rem;color:var(--cc-sf-instance-num-color, #4B5563)}.group-section-wrapper .group-accordion-instance .group-accordion-body{padding:var(--cc-sf-instance-padding, 16px);background:var(--cc-sf-instance-bg, #F9FAFB);border-top:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)}.group-section-wrapper .btn-add-group{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:10px 20px;margin-top:12px;background:var(--cc-sf-btn-add-bg, transparent);color:var(--cc-sf-btn-add-color, #3B82F6);border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1);border-radius:var(--cc-sf-btn-add-radius, 6px);cursor:pointer;font-family:inherit;font-size:var(--cc-sf-btn-font-size, .875rem);font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.group-section-wrapper .btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.group-section-wrapper .group-instance:last-child{margin-bottom:0}.group-section-wrapper.multi-save-active{border:none;box-shadow:none;padding:0;background:transparent}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button{color:var(--ms-btn-add-color, #3B82F6);font-weight:600;font-size:.875rem;padding:8px 12px}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button:hover{color:var(--ms-btn-add-hover, #2563EB);background-color:var(--cc-sf-btn-add-hover-bg, #EFF6FF)}.group-section-wrapper.multi-save-active .group-instance.is-card{cursor:pointer;transition:all .2s ease-in-out}.group-section-wrapper.multi-save-active .group-instance.is-card:hover{box-shadow:var(--ms-card-shadow-hover, 0 8px 24px rgba(0, 0, 0, .08));border-color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-title{white-space:nowrap;text-overflow:ellipsis}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-desc{line-height:1.4;display:-webkit-box;-webkit-line-clamp:1;line-clamp:1;-webkit-box-orient:vertical}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view.is-expanded .card-content .card-desc{-webkit-line-clamp:unset;line-clamp:unset}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon{font-size:22px;width:22px;height:22px;color:var(--cc-sf-hint-color, #9CA3AF);transition:color .2s}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-delete:hover{color:var(--cc-sf-error-border, #DC2626)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-edit:hover{color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-expand{color:var(--cc-sf-input-disabled-border, #E5E7EB)}.btn-remove{transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-remove mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.btn-remove:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.btn-add-group{font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.upload-drop-zone{background-color:var(--cc-sf-dropzone-bg, #F8FAFC);border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1);border-radius:var(--cc-sf-dropzone-radius, 12px);transition:background-color .2s ease,border-color .2s ease}.upload-drop-zone:hover{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-hover-border, #93C5FD)}.upload-drop-zone.drag-over{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-over-border, #3B82F6);box-shadow:var(--cc-sf-dropzone-over-shadow, 0 0 0 4px rgba(59, 130, 246, .12))}.upload-drop-zone.is-invalid{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.upload-icon-wrap .dropzone-icon-pill{width:52px;height:52px;border-radius:50%;background:var(--cc-sf-dropzone-icon-bg, rgba(59, 130, 246, .1))}.upload-icon-wrap mat-icon.upload-cloud-icon{font-size:28px;width:28px;height:28px;line-height:28px;color:var(--cc-sf-accent-color, #3B82F6)}.upload-main-text{color:var(--cc-sf-label-color, #1E293B)}.upload-sub-text{color:var(--cc-sf-hint-color, #64748B)}.upload-link{color:var(--cc-sf-dropzone-link-color, #3B82F6);font-weight:500}.upload-formats{color:var(--cc-sf-dropzone-link-color, #3B82F6)}.upload-size-badge{display:inline-block;padding:2px 8px;border-radius:20px;background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-hint-color, #6B7280);font-weight:500}.uploaded-item{background:var(--cc-sf-uploaded-item-bg, #ffffff);border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0);border-radius:var(--cc-sf-uploaded-item-radius, 8px);transition:box-shadow .15s ease}.uploaded-item:hover{box-shadow:0 2px 6px #0000000f}.uploaded-item mat-icon.file-type-icon{font-size:20px;width:20px;height:20px;line-height:20px;flex-shrink:0;color:var(--cc-sf-hint-color, #64748B)}.uploaded-item .file-thumb{width:36px;height:36px;object-fit:cover;flex-shrink:0}.uploaded-item .file-info{min-width:0;gap:2px}.uploaded-item .file-info .file-name{white-space:nowrap;text-overflow:ellipsis}.uploaded-item .file-remove-btn{flex-shrink:0;width:32px;height:32px;background:none;border:none;cursor:pointer;color:var(--cc-sf-uploaded-remove-color, #94A3B8);padding:0;display:flex;align-items:center;justify-content:center;transition:color .15s ease,background .15s ease}.uploaded-item .file-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.uploaded-item .file-remove-btn:hover:not(:disabled){color:var(--cc-sf-uploaded-remove-hover-color, #DC2626);background:var(--cc-sf-uploaded-remove-hover-bg, #FEF2F2)}.uploaded-item .file-remove-btn:disabled{opacity:.4;cursor:not-allowed}.uploaded-item.uploading{background:var(--cc-sf-uploaded-uploading-bg, #F8FAFC);border-color:var(--cc-sf-uploaded-uploading-border, #CBD5E1);opacity:.85}.upload-spinner{width:20px;height:20px;flex-shrink:0;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}@keyframes cc-spin{to{transform:rotate(360deg)}}.uploading-label{font-style:italic}.input-group{align-items:stretch}.input-group .field-input{flex:1;width:auto}.input-prefix+.input-group .field-input{border-top-left-radius:0;border-bottom-left-radius:0}.input-group .field-input:has(+.input-suffix){border-top-right-radius:0;border-bottom-right-radius:0}.input-group .field-input.has-icon-right{padding-right:3rem}.input-group.readonly .field-input{cursor:default}.input-prefix,.input-suffix{display:flex!important;align-items:center;white-space:nowrap;padding:0 14px;background-color:var(--cc-sf-input-disabled-bg);border:1px solid var(--cc-sf-input-border);font-size:.875rem;color:var(--cc-sf-hint-color);-webkit-user-select:none;user-select:none}.input-prefix{border-right:none;border-top-left-radius:var(--cc-sf-input-radius, 8px);border-bottom-left-radius:var(--cc-sf-input-radius, 8px)}.input-suffix{border-left:none;border-top-right-radius:var(--cc-sf-input-radius, 8px);border-bottom-right-radius:var(--cc-sf-input-radius, 8px)}.readonly-icons{right:.875rem;top:50%;transform:translateY(-50%)}.readonly-icons mat-icon.lock-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem;opacity:.5;color:var(--cc-sf-hint-color, #6B7280)}.date-icon-wrapper{right:.5rem;top:50%;transform:translateY(-50%);pointer-events:auto}.date-icon-wrapper .mat-icon-button{width:32px;height:32px;line-height:32px}.subfields-group-wrapper .subfields-row{transition:all .2s ease}.subfields-group-wrapper .subfields-row.is-invalid .subfield-item ::ng-deep .field-input{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.subfields-group-wrapper .subfields-row .subfield-item{min-width:0}.subfields-group-wrapper .subfields-row .subfield-item ::ng-deep .field-label{font-size:.75rem!important;margin-bottom:4px!important;font-weight:500!important;color:var(--cc-sf-hint-color, #6B7280)!important}.subfields-group-wrapper .subfields-row .subfield-separator{font-weight:700}.autocomplete-wrapper .ac-input{padding-left:40px!important}.autocomplete-wrapper .ac-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1;transition:color var(--cc-sf-input-transition, .2s ease)}.autocomplete-wrapper .ac-clear-btn{right:.6rem;transition:color .15s ease,background .15s ease}.autocomplete-wrapper .ac-clear-btn mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.autocomplete-wrapper .ac-clear-btn:hover{color:var(--cc-sf-label-color, #374151);background:var(--cc-sf-input-disabled-bg, #F3F4F6)}.autocomplete-wrapper .ac-clear-btn:focus{outline:none}.autocomplete-wrapper:focus-within .ac-search-icon{color:var(--cc-sf-accent-color, #3B82F6)}.autocomplete-wrapper.is-invalid .ac-input{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)}.autocomplete-wrapper.readonly .ac-input{background-color:var(--cc-sf-input-disabled-bg);color:var(--cc-sf-input-disabled-color, #6B7280);cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important}.ac-no-results{font-style:italic}::ng-deep .mat-mdc-autocomplete-panel,::ng-deep .mat-autocomplete-panel{background:var(--cc-sf-input-bg, #ffffff)!important;border-radius:var(--cc-sf-input-radius, 9px)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important;box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f!important;padding:4px 0!important;min-width:200px}::ng-deep .mat-mdc-autocomplete-panel mat-option,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option,::ng-deep .mat-mdc-autocomplete-panel .mat-option,::ng-deep .mat-autocomplete-panel mat-option,::ng-deep .mat-autocomplete-panel .mat-mdc-option,::ng-deep .mat-autocomplete-panel .mat-option{background:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #111827)!important;font-size:.875rem!important;padding:10px 16px!important;min-height:40px!important;line-height:1.4!important;display:flex!important;flex-direction:column!important;align-items:flex-start!important;transition:background var(--cc-sf-input-transition, .2s ease)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]){background:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-option.mdc-list-item--selected{background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)!important;color:var(--cc-sf-selected-color, #6366F1)!important;font-weight:600!important}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-option-label{font-weight:500;color:var(--cc-sf-label-color, #111827);font-size:.875rem;display:block}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-option .ac-display-fields{align-items:center;line-height:1}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-item{display:inline-flex;align-items:center;font-size:.72rem;color:var(--cc-sf-hint-color, #6B7280);gap:3px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-chip{background:var(--cc-sf-input-disabled-bg, #F3F4F6);border-radius:4px;padding:2px 6px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-text{color:var(--cc-sf-hint-color, #6B7280)}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--cc-sf-input-border, #D1D5DB);vertical-align:middle}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-label{font-weight:600;color:var(--cc-sf-hint-color, #9CA3AF);margin-right:2px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-icon{font-size:11px;width:11px;height:11px;line-height:11px;color:var(--cc-sf-hint-color, #9CA3AF);flex-shrink:0}.mu-layout{grid-template-columns:1fr 1fr;gap:32px}@media(max-width:768px){.mu-layout{grid-template-columns:1fr}}.mu-title{font-weight:700;line-height:1.3}.mu-badge{white-space:nowrap;flex-shrink:0}.mu-description{line-height:1.6}.mu-feature-item .mu-check{width:16px;height:16px;line-height:16px;flex-shrink:0}.mu-right{min-height:260px}.mu-right-empty{min-height:250px;max-width:400px;box-shadow:0 2px 10px #0000000d;transition:box-shadow .2s ease}.mu-right-empty:hover{cursor:pointer;box-shadow:0 4px 16px #0000001a}.mu-right-empty .mu-right-empty-icon{width:52px;height:52px;line-height:52px;opacity:.3}.mu-right-empty p{margin:0;font-size:.85rem}.media-add-container{display:inline-block}.media-add-container ::ng-deep button{display:flex;align-items:center;gap:6px}.media-add-container ::ng-deep button .menu-chevron{width:18px;height:18px;line-height:18px;transition:transform .2s ease}.media-dropdown{top:calc(100% + 6px);left:0;z-index:200;min-width:240px;box-shadow:var(--mu-dropdown-shadow, 0 8px 32px rgba(0, 0, 0, .12));animation:mu-fade-in .15s ease}@keyframes mu-fade-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.media-dropdown-item{transition:background .15s ease}.media-dropdown-item:last-child{border-bottom:none}.media-dropdown-item:hover{background:var(--cc-sf-dropzone-hover-bg, #F0F9FF)}.media-drop-icon{width:36px;height:36px;flex-shrink:0}.media-drop-icon mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.media-drop-icon--video{background:var(--mu-icon-video-bg, #FFF0F0);color:var(--mu-icon-video-color, #EF4444)}.media-drop-icon--device{background:var(--mu-icon-device-bg, #EFF6FF);color:var(--mu-icon-device-color, #3B82F6)}.media-drop-icon--library{background:var(--mu-icon-library-bg, #F0FDF4);color:var(--mu-icon-library-color, #22C55E)}.media-drop-text{gap:2px}.youtube-input-panel{animation:mu-fade-in .18s ease}.youtube-panel-label mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:var(--mu-icon-video-color, #EF4444)}.youtube-input-row{align-items:stretch}.media-menu-backdrop{position:fixed;inset:0;z-index:100}.media-upload-status{animation:mu-fade-in .2s ease}.media-upload-status .status-icon{width:18px;height:18px;line-height:18px}.media-carousel-main{max-width:400px;height:var(--mu-carousel-height, 250px)}.carousel-viewer{inset:0}.carousel-viewer .carousel-image{object-fit:cover}.carousel-viewer .carousel-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.carousel-nav{top:50%;transform:translateY(-50%);z-index:10;width:40px;height:40px;box-shadow:0 2px 8px #0003;transition:background .2s ease,opacity .2s ease;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.carousel-nav mat-icon{font-size:22px;width:22px;height:22px;line-height:22px;color:#1e293b}.carousel-nav:hover:not(:disabled){background:#fff}.carousel-nav:disabled{opacity:.3;cursor:not-allowed}.carousel-nav--prev{left:12px}.carousel-nav--next{right:12px}.carousel-remove-btn{top:10px;right:10px;z-index:10;width:32px;height:32px;transition:background .2s ease}.carousel-remove-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:#fff}.carousel-remove-btn:hover:not(:disabled){background:#dc2626d9}.carousel-remove-btn:disabled{opacity:.4;cursor:not-allowed}.carousel-dots{bottom:10px;left:50%;transform:translate(-50%);z-index:10}.carousel-dot{width:8px;height:8px;transition:background .2s ease,transform .2s ease}.carousel-dot.active{background:#fff;transform:scale(1.3)}.media-thumbnail-strip{max-width:400px;overflow-x:auto}.media-thumbnail-strip::-webkit-scrollbar{height:4px}.media-thumbnail-strip::-webkit-scrollbar-thumb{background:var(--cc-sf-input-disabled-border, #D1D5DB);border-radius:2px}.media-thumb{flex-shrink:0;width:72px;height:52px;transition:border-color .2s ease,transform .15s ease}.media-thumb.active{border-color:var(--mu-thumb-active-border, var(--cc-sf-accent-color, #3B82F6));transform:scale(1.04)}.media-thumb:hover:not(.active){border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.media-thumb .thumb-img{object-fit:cover}.media-thumb .thumb-yt-placeholder mat-icon{font-size:28px;width:28px;height:28px;line-height:28px}.media-thumb .thumb-uploading .thumb-spinner{width:20px;height:20px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.media-library-portal-host{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;visibility:hidden;opacity:0;pointer-events:none;transition:opacity .2s ease,visibility .2s ease}.media-library-portal-host.is-open{visibility:visible;opacity:1;pointer-events:auto}.media-library-portal-host.is-open .media-library-modal{transform:scale(1) translateY(0)}.media-library-overlay{position:absolute;inset:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.media-library-modal{position:relative;z-index:10000;width:90vw;max-width:900px;max-height:90vh;background:#fff;border-radius:16px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transform:scale(.95) translateY(10px);transition:transform .3s cubic-bezier(.175,.885,.32,1.275)}.library-modal-header{flex-shrink:0}.library-modal-title{font-weight:700}.library-modal-subtitle{line-height:1.5;max-width:600px}.library-close-btn{width:32px;height:32px;transition:background .15s ease,color .15s ease}.library-close-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-close-btn:hover{background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-label-color, #374151)}.library-loading mat-icon,.library-empty mat-icon{font-size:40px;width:40px;height:40px;line-height:40px;opacity:.5}.lib-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.library-error{flex-shrink:0}.library-error mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-grid{grid-template-columns:repeat(5,1fr);max-height:50vh;overflow-y:auto}.library-grid::-webkit-scrollbar{width:8px}.library-grid::-webkit-scrollbar-track{background:#f1f1f1}.library-grid::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:10px;border:2px solid #F1F1F1}.library-grid-item{aspect-ratio:1/1;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.library-grid-item:hover{transform:translateY(-4px) scale(1.02);box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}.library-grid-item.selected{border-color:var(--cc-sf-accent-color, #3B82F6)}.library-grid-item.selected .library-grid-img{opacity:.7}.library-grid-item:hover .library-overlay-hover{opacity:1}.library-grid-img{object-fit:cover}.library-overlay-hover{inset:0;opacity:0;transition:opacity .15s ease}.library-check{top:6px;right:6px;width:22px;height:22px;box-shadow:0 1px 4px #00000026}.library-check mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.library-modal-footer{flex-shrink:0}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary{background-color:var(--cc-sf-accent-color, #3B82F6)!important;border-color:var(--cc-sf-accent-color, #3B82F6)!important;color:#fff!important;font-weight:600;padding-left:32px;padding-right:32px}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:hover{background-color:var(--cc-sf-btn-primary-hover-bg, #2563EB)!important}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:disabled{background-color:#93c5fd!important;cursor:not-allowed}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline{font-weight:600;padding-left:24px;padding-right:24px;border-color:#d1d5db;color:#374151}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline:hover{background-color:#f9fafb}.location-subtitle{line-height:1.5}.loc-tab-btn ::ng-deep button{width:100%}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning){background-color:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #000000)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning):hover{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.loc-venue-item{transition:box-shadow .15s ease,border-color .15s ease}.loc-venue-item:hover{box-shadow:0 2px 8px #0000000f;border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.loc-venue-search-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-venue-text{white-space:nowrap;text-overflow:ellipsis}.loc-action-btn{transition:background .15s ease,color .15s ease;flex-shrink:0}.loc-action-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.loc-action-btn.loc-delete-btn{color:var(--loc-delete-color, #E53E3E)}.loc-action-btn.loc-delete-btn:hover{background:var(--cc-sf-error-bg, #FEF2F2)}.loc-action-btn.loc-edit-btn{color:var(--cc-sf-hint-color, #9CA3AF)}.loc-action-btn.loc-edit-btn:hover{color:var(--cc-sf-input-focus-border, #3B82F6);background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)}.loc-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1}.loc-suggestions-panel{top:calc(100% + 4px);left:0;right:0;z-index:300;box-shadow:0 8px 24px #0000001a;animation:mu-fade-in .15s ease;max-height:260px;overflow-y:auto}.loc-suggestion-item{transition:background .12s ease}.loc-suggestion-item:hover,.loc-suggestion-item:focus{background:var(--loc-suggestion-hover-bg, #F0F9FF)}.loc-suggestion-item:not(:last-child){border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)}.loc-suggestion-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-suggestion-text{white-space:nowrap;text-overflow:ellipsis}.loc-add-btn{transition:opacity .15s ease}.loc-add-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.loc-add-btn:hover{opacity:.8}.loc-map-container{box-shadow:0 2px 10px #0000000f}.loc-tba-panel{min-height:120px}.loc-tba-icon{width:40px;height:40px;line-height:40px;opacity:.6}.loc-tba-text{line-height:1.6;max-width:360px}.radio-label{display:flex!important}.radio-label .option-content{padding-left:10px}\n"] }]
|
|
2349
2392
|
}], ctorParameters: () => [{ type: i1$3.FormBuilder }, { type: ExpressionService }, { type: i3.HttpClient }], propDecorators: { config: [{
|
|
2350
2393
|
type: Input
|
|
2351
2394
|
}], controller: [{
|
|
@@ -2360,6 +2403,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
2360
2403
|
}], libraryModalRef: [{
|
|
2361
2404
|
type: ViewChild,
|
|
2362
2405
|
args: ['libraryModal']
|
|
2406
|
+
}], onDocumentClick: [{
|
|
2407
|
+
type: HostListener,
|
|
2408
|
+
args: ['document:click']
|
|
2409
|
+
}], onEscapeKey: [{
|
|
2410
|
+
type: HostListener,
|
|
2411
|
+
args: ['document:keydown.escape']
|
|
2363
2412
|
}] } });
|
|
2364
2413
|
|
|
2365
2414
|
class FormSectionComponent {
|
|
@@ -3877,17 +3926,14 @@ class DropdownComponent {
|
|
|
3877
3926
|
this.errorMessage = this.labels.errorMessage || this.errorMessage;
|
|
3878
3927
|
}
|
|
3879
3928
|
}
|
|
3880
|
-
get
|
|
3881
|
-
return
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
}
|
|
3889
|
-
get clearAriaLabel() {
|
|
3890
|
-
return this.labels?.clearAriaLabel || 'Clear selection';
|
|
3929
|
+
get resolvedLabels() {
|
|
3930
|
+
return {
|
|
3931
|
+
requiredMarker: this.labels?.requiredMarker || '*',
|
|
3932
|
+
searchPlaceholder: this.labels?.searchPlaceholder || 'Search...',
|
|
3933
|
+
selectedSuffix: this.labels?.selectedSuffix || 'selected',
|
|
3934
|
+
clearAriaLabel: this.labels?.clearAriaLabel || 'Clear selection',
|
|
3935
|
+
noResultsFound: this.labels?.noResultsFound || 'No results found',
|
|
3936
|
+
};
|
|
3891
3937
|
}
|
|
3892
3938
|
writeValue(value) {
|
|
3893
3939
|
this.value = value;
|
|
@@ -3990,7 +4036,7 @@ class DropdownComponent {
|
|
|
3990
4036
|
getSelectedLabel() {
|
|
3991
4037
|
if (this.multiple) {
|
|
3992
4038
|
const count = Array.isArray(this.value) ? this.value.length : 0;
|
|
3993
|
-
return count > 0 ? `${count} ${this.selectedSuffix}` : '';
|
|
4039
|
+
return count > 0 ? `${count} ${this.resolvedLabels.selectedSuffix}` : '';
|
|
3994
4040
|
}
|
|
3995
4041
|
if (!this.value)
|
|
3996
4042
|
return '';
|
|
@@ -4084,7 +4130,7 @@ class DropdownComponent {
|
|
|
4084
4130
|
useExisting: forwardRef(() => DropdownComponent),
|
|
4085
4131
|
multi: true
|
|
4086
4132
|
}
|
|
4087
|
-
], viewQueries: [{ propertyName: "viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cc-dropdown-wrapper\" [ngStyle]=\"wrapperStyles\" libClickOutside (libClickOutside)=\"close()\">\r\n <!-- Label -->\r\n <label *ngIf=\"label\" class=\"cc-dropdown-label\" [ngStyle]=\"labelStyles\">\r\n {{ label }}\r\n <span *ngIf=\"required\" class=\"cc-required\">{{ requiredMarker }}</span>\r\n </label>\r\n\r\n <!-- Trigger -->\r\n <div class=\"cc-dropdown-trigger\" [class.disabled]=\"disabled\" [class.open]=\"isOpen\" [class.error]=\"errorMessage\"\r\n (click)=\"toggle()\" tabindex=\"0\" (keydown)=\"handleKeyboardEvent($event)\" [ngStyle]=\"fieldStyles\">\r\n\r\n <div class=\"cc-selected-value\" *ngIf=\"hasValue()\">\r\n {{ multiple ? value.length + ' ' + selectedSuffix : getSelectedLabel() }}\r\n </div>\r\n <div class=\"cc-placeholder\" *ngIf=\"!hasValue()\">{{ placeholder }}</div>\r\n\r\n <div class=\"cc-actions\">\r\n <span *ngIf=\"clearable && hasValue() && !disabled\" class=\"cc-clear-btn\" (click)=\"clearSelection($event)\"\r\n [attr.aria-label]=\"clearAriaLabel\">\r\n <span class=\"material-icons\">close</span>\r\n </span>\r\n <span class=\"cc-arrow material-icons\">expand_more</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Dropdown Menu -->\r\n <div class=\"cc-dropdown-menu\" *ngIf=\"isOpen\">\r\n <!-- Search -->\r\n <div class=\"cc-search-container\" *ngIf=\"searchable\">\r\n <input type=\"text\" #searchInput [placeholder]=\"searchPlaceholder\"\r\n (input)=\"onSearch($any($event.target).value)\" (click)=\"$event.stopPropagation()\"\r\n class=\"cc-dropdown-search-input\" />\r\n </div>\r\n\r\n <!-- Options Virtual Scroll -->\r\n <cdk-virtual-scroll-viewport itemSize=\"40\" class=\"cc-virtual-scroll\"\r\n [style.height.px]=\"filteredOptions.length * 40 > 240 ? 240 : (filteredOptions.length * 40 || 40)\">\r\n\r\n <div *cdkVirtualFor=\"let option of filteredOptions; let i = index\" class=\"cc-option\"\r\n [class.selected]=\"isSelected(option)\" [class.focused]=\"i === focusedIndex\"\r\n [class.disabled]=\"option.disabled\" (click)=\"selectOption(option)\">\r\n\r\n <!-- Multi-select checkbox visual -->\r\n <span class=\"cc-multi-check\" *ngIf=\"multiple\">\r\n <span class=\"material-icons\" *ngIf=\"isSelected(option)\">check_box</span>\r\n <span class=\"material-icons\" *ngIf=\"!isSelected(option)\">check_box_outline_blank</span>\r\n </span>\r\n\r\n <!-- Icon -->\r\n <span class=\"cc-option-icon-wrapper\" *ngIf=\"option.icon\">\r\n <span class=\"material-icons\" *ngIf=\"getIconType(option.icon) === 'material'\">{{\r\n getIconValue(option.icon) }}</span>\r\n <i *ngIf=\"getIconType(option.icon) === 'fontawesome'\" [class]=\"getIconValue(option.icon)\"></i>\r\n <img *ngIf=\"getIconType(option.icon) === 'img'\" [src]=\"getIconValue(option.icon)\"\r\n class=\"cc-option-img\" alt=\"icon\" />\r\n </span>\r\n\r\n <span class=\"cc-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Single-select check visual -->\r\n <span class=\"cc-single-check\" *ngIf=\"!multiple && isSelected(option)\">\r\n <span class=\"material-icons\">check</span>\r\n </span>\r\n </div>\r\n\r\n <div *ngIf=\"filteredOptions.length === 0\" class=\"cc-no-results\">{{
|
|
4133
|
+
], viewQueries: [{ propertyName: "viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cc-dropdown-wrapper\" [ngStyle]=\"wrapperStyles\" libClickOutside (libClickOutside)=\"close()\">\r\n <!-- Label -->\r\n <label *ngIf=\"label\" class=\"cc-dropdown-label\" [ngStyle]=\"labelStyles\">\r\n {{ label }}\r\n <span *ngIf=\"required\" class=\"cc-required\">{{ resolvedLabels.requiredMarker }}</span>\r\n </label>\r\n\r\n <!-- Trigger -->\r\n <div class=\"cc-dropdown-trigger\" [class.disabled]=\"disabled\" [class.open]=\"isOpen\" [class.error]=\"errorMessage\"\r\n (click)=\"toggle()\" tabindex=\"0\" (keydown)=\"handleKeyboardEvent($event)\" [ngStyle]=\"fieldStyles\">\r\n\r\n <div class=\"cc-selected-value\" *ngIf=\"hasValue()\">\r\n {{ multiple ? value.length + ' ' + resolvedLabels.selectedSuffix : getSelectedLabel() }}\r\n </div>\r\n <div class=\"cc-placeholder\" *ngIf=\"!hasValue()\">{{ placeholder }}</div>\r\n\r\n <div class=\"cc-actions\">\r\n <span *ngIf=\"clearable && hasValue() && !disabled\" class=\"cc-clear-btn\" (click)=\"clearSelection($event)\"\r\n [attr.aria-label]=\"resolvedLabels.clearAriaLabel\">\r\n <span class=\"material-icons\">close</span>\r\n </span>\r\n <span class=\"cc-arrow material-icons\">expand_more</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Dropdown Menu -->\r\n <div class=\"cc-dropdown-menu\" *ngIf=\"isOpen\">\r\n <!-- Search -->\r\n <div class=\"cc-search-container\" *ngIf=\"searchable\">\r\n <input type=\"text\" #searchInput [placeholder]=\"resolvedLabels.searchPlaceholder\"\r\n (input)=\"onSearch($any($event.target).value)\" (click)=\"$event.stopPropagation()\"\r\n class=\"cc-dropdown-search-input\" />\r\n </div>\r\n\r\n <!-- Options Virtual Scroll -->\r\n <cdk-virtual-scroll-viewport itemSize=\"40\" class=\"cc-virtual-scroll\"\r\n [style.height.px]=\"filteredOptions.length * 40 > 240 ? 240 : (filteredOptions.length * 40 || 40)\">\r\n\r\n <div *cdkVirtualFor=\"let option of filteredOptions; let i = index\" class=\"cc-option\"\r\n [class.selected]=\"isSelected(option)\" [class.focused]=\"i === focusedIndex\"\r\n [class.disabled]=\"option.disabled\" (click)=\"selectOption(option)\">\r\n\r\n <!-- Multi-select checkbox visual -->\r\n <span class=\"cc-multi-check\" *ngIf=\"multiple\">\r\n <span class=\"material-icons\" *ngIf=\"isSelected(option)\">check_box</span>\r\n <span class=\"material-icons\" *ngIf=\"!isSelected(option)\">check_box_outline_blank</span>\r\n </span>\r\n\r\n <!-- Icon -->\r\n <span class=\"cc-option-icon-wrapper\" *ngIf=\"option.icon\">\r\n <span class=\"material-icons\" *ngIf=\"getIconType(option.icon) === 'material'\">{{\r\n getIconValue(option.icon) }}</span>\r\n <i *ngIf=\"getIconType(option.icon) === 'fontawesome'\" [class]=\"getIconValue(option.icon)\"></i>\r\n <img *ngIf=\"getIconType(option.icon) === 'img'\" [src]=\"getIconValue(option.icon)\"\r\n class=\"cc-option-img\" alt=\"icon\" />\r\n </span>\r\n\r\n <span class=\"cc-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Single-select check visual -->\r\n <span class=\"cc-single-check\" *ngIf=\"!multiple && isSelected(option)\">\r\n <span class=\"material-icons\">check</span>\r\n </span>\r\n </div>\r\n\r\n <div *ngIf=\"filteredOptions.length === 0\" class=\"cc-no-results\">{{ resolvedLabels.noResultsFound }}</div>\r\n </cdk-virtual-scroll-viewport>\r\n </div>\r\n\r\n <!-- Error Message -->\r\n <div class=\"cc-error-message\" *ngIf=\"errorMessage\">{{ errorMessage }}</div>\r\n</div>", styles: [".cc-dropdown-wrapper{display:flex;flex-direction:column;width:100%;position:relative;font-family:var(--cc-dropdown-font-family, inherit);gap:var(--cc-dropdown-label-gap, .5rem)}.cc-dropdown-label{font-size:var(--cc-dropdown-font-size, .875rem);font-weight:var(--cc-dropdown-font-weight, 500);color:var(--cc-dropdown-label-color, #202124);margin:0;line-height:1.4}.cc-required{color:var(--cc-dropdown-required-color, #D93025);margin-left:.25rem}.cc-dropdown-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;min-height:var(--cc-dropdown-height, 2.5rem);padding:var(--cc-dropdown-padding, .5rem .75rem);background-color:var(--cc-dropdown-bg, #FEFEFE);border:var(--cc-dropdown-border-width, 1px) solid var(--cc-dropdown-border-color, #BDC1C6);border-radius:var(--cc-dropdown-border-radius, .4375rem);cursor:pointer;-webkit-user-select:none;user-select:none;box-sizing:border-box;transition:all .2s}.cc-dropdown-trigger:focus{outline:none;border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-dropdown-trigger.open{border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-dropdown-trigger.error{border-color:var(--cc-dropdown-error-color, #D93025)}.cc-dropdown-trigger.disabled{background-color:var(--cc-dropdown-disabled-bg, #F1F3F4);cursor:not-allowed;color:var(--cc-dropdown-disabled-color, #80868B)}.cc-dropdown-trigger.disabled .cc-arrow,.cc-dropdown-trigger.disabled .cc-clear-btn{color:var(--cc-dropdown-disabled-color, #80868B)}.cc-selected-value{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--cc-dropdown-color, #202124);font-size:var(--cc-dropdown-font-size, .875rem);font-weight:var(--cc-dropdown-font-weight, 400)}.cc-placeholder{flex:1;color:var(--cc-dropdown-placeholder-color, #80868B);font-size:var(--cc-dropdown-font-size, .875rem)}.cc-actions{display:flex;align-items:center;gap:.5rem}.cc-arrow{color:var(--cc-dropdown-arrow-color, #5F6368);transition:transform .2s;font-size:1.25rem}.cc-dropdown-trigger.open .cc-arrow{transform:rotate(180deg)}.cc-clear-btn{display:flex;align-items:center;justify-content:center;color:var(--cc-dropdown-arrow-color, #5F6368);cursor:pointer}.cc-clear-btn:hover{color:#202124}.cc-clear-btn .material-icons{font-size:1.125rem}.cc-dropdown-menu{position:absolute;top:100%;left:0;width:100%;background-color:#fff;border:1px solid #BDC1C6;border-radius:0 0 4px 4px;box-shadow:0 2px 4px #0000001a;z-index:1000;margin-top:2px;overflow:hidden;animation:fadeIn .1s ease-out}@keyframes fadeIn{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.cc-search-container{padding:.5rem;border-bottom:1px solid var(--cc-dropdown-divider-color, #E0E0E0)}.cc-dropdown-search-input{width:100%;padding:.5rem;border:1px solid #BDC1C6;border-radius:4px;outline:none;box-sizing:border-box;font-size:.875rem}.cc-dropdown-search-input:focus{border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-virtual-scroll{width:100%;max-height:240px}.cc-option{display:flex;align-items:center;padding:0 .75rem;height:40px;cursor:pointer;color:var(--cc-dropdown-color, #202124);font-size:var(--cc-dropdown-font-size, .875rem);transition:background-color .1s;box-sizing:border-box}.cc-option:hover,.cc-option.focused{background-color:#f1f3f4}.cc-option.selected{background-color:#e8f0fe;color:#1a73e8}.cc-option.disabled{opacity:.5;cursor:not-allowed;background-color:transparent}.cc-multi-check{margin-right:.5rem;color:var(--cc-dropdown-arrow-color, #5F6368);display:flex;align-items:center}.cc-multi-check .material-icons{font-size:1.25rem}.cc-option.selected .cc-multi-check{color:#1a73e8}.cc-option-icon-wrapper{margin-right:.5rem;display:flex;align-items:center}.cc-option-icon-wrapper .material-icons{font-size:1.25rem}.cc-option-img{width:1.25rem;height:1.25rem;object-fit:contain}.cc-option-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cc-single-check{margin-left:.5rem;color:#1a73e8;display:flex;align-items:center}.cc-single-check .material-icons{font-size:1.125rem}.cc-no-results{padding:.75rem;color:#80868b;text-align:center;font-size:.875rem}.cc-error-message{font-size:.75rem;color:var(--cc-dropdown-error-color, #D93025);margin-top:.25rem}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2$1.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i2$1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i2$1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[libClickOutside]", outputs: ["libClickOutside"] }] });
|
|
4088
4134
|
}
|
|
4089
4135
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DropdownComponent, decorators: [{
|
|
4090
4136
|
type: Component,
|
|
@@ -4094,7 +4140,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
4094
4140
|
useExisting: forwardRef(() => DropdownComponent),
|
|
4095
4141
|
multi: true
|
|
4096
4142
|
}
|
|
4097
|
-
], template: "<div class=\"cc-dropdown-wrapper\" [ngStyle]=\"wrapperStyles\" libClickOutside (libClickOutside)=\"close()\">\r\n <!-- Label -->\r\n <label *ngIf=\"label\" class=\"cc-dropdown-label\" [ngStyle]=\"labelStyles\">\r\n {{ label }}\r\n <span *ngIf=\"required\" class=\"cc-required\">{{ requiredMarker }}</span>\r\n </label>\r\n\r\n <!-- Trigger -->\r\n <div class=\"cc-dropdown-trigger\" [class.disabled]=\"disabled\" [class.open]=\"isOpen\" [class.error]=\"errorMessage\"\r\n (click)=\"toggle()\" tabindex=\"0\" (keydown)=\"handleKeyboardEvent($event)\" [ngStyle]=\"fieldStyles\">\r\n\r\n <div class=\"cc-selected-value\" *ngIf=\"hasValue()\">\r\n {{ multiple ? value.length + ' ' + selectedSuffix : getSelectedLabel() }}\r\n </div>\r\n <div class=\"cc-placeholder\" *ngIf=\"!hasValue()\">{{ placeholder }}</div>\r\n\r\n <div class=\"cc-actions\">\r\n <span *ngIf=\"clearable && hasValue() && !disabled\" class=\"cc-clear-btn\" (click)=\"clearSelection($event)\"\r\n [attr.aria-label]=\"clearAriaLabel\">\r\n <span class=\"material-icons\">close</span>\r\n </span>\r\n <span class=\"cc-arrow material-icons\">expand_more</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Dropdown Menu -->\r\n <div class=\"cc-dropdown-menu\" *ngIf=\"isOpen\">\r\n <!-- Search -->\r\n <div class=\"cc-search-container\" *ngIf=\"searchable\">\r\n <input type=\"text\" #searchInput [placeholder]=\"searchPlaceholder\"\r\n (input)=\"onSearch($any($event.target).value)\" (click)=\"$event.stopPropagation()\"\r\n class=\"cc-dropdown-search-input\" />\r\n </div>\r\n\r\n <!-- Options Virtual Scroll -->\r\n <cdk-virtual-scroll-viewport itemSize=\"40\" class=\"cc-virtual-scroll\"\r\n [style.height.px]=\"filteredOptions.length * 40 > 240 ? 240 : (filteredOptions.length * 40 || 40)\">\r\n\r\n <div *cdkVirtualFor=\"let option of filteredOptions; let i = index\" class=\"cc-option\"\r\n [class.selected]=\"isSelected(option)\" [class.focused]=\"i === focusedIndex\"\r\n [class.disabled]=\"option.disabled\" (click)=\"selectOption(option)\">\r\n\r\n <!-- Multi-select checkbox visual -->\r\n <span class=\"cc-multi-check\" *ngIf=\"multiple\">\r\n <span class=\"material-icons\" *ngIf=\"isSelected(option)\">check_box</span>\r\n <span class=\"material-icons\" *ngIf=\"!isSelected(option)\">check_box_outline_blank</span>\r\n </span>\r\n\r\n <!-- Icon -->\r\n <span class=\"cc-option-icon-wrapper\" *ngIf=\"option.icon\">\r\n <span class=\"material-icons\" *ngIf=\"getIconType(option.icon) === 'material'\">{{\r\n getIconValue(option.icon) }}</span>\r\n <i *ngIf=\"getIconType(option.icon) === 'fontawesome'\" [class]=\"getIconValue(option.icon)\"></i>\r\n <img *ngIf=\"getIconType(option.icon) === 'img'\" [src]=\"getIconValue(option.icon)\"\r\n class=\"cc-option-img\" alt=\"icon\" />\r\n </span>\r\n\r\n <span class=\"cc-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Single-select check visual -->\r\n <span class=\"cc-single-check\" *ngIf=\"!multiple && isSelected(option)\">\r\n <span class=\"material-icons\">check</span>\r\n </span>\r\n </div>\r\n\r\n <div *ngIf=\"filteredOptions.length === 0\" class=\"cc-no-results\">{{
|
|
4143
|
+
], template: "<div class=\"cc-dropdown-wrapper\" [ngStyle]=\"wrapperStyles\" libClickOutside (libClickOutside)=\"close()\">\r\n <!-- Label -->\r\n <label *ngIf=\"label\" class=\"cc-dropdown-label\" [ngStyle]=\"labelStyles\">\r\n {{ label }}\r\n <span *ngIf=\"required\" class=\"cc-required\">{{ resolvedLabels.requiredMarker }}</span>\r\n </label>\r\n\r\n <!-- Trigger -->\r\n <div class=\"cc-dropdown-trigger\" [class.disabled]=\"disabled\" [class.open]=\"isOpen\" [class.error]=\"errorMessage\"\r\n (click)=\"toggle()\" tabindex=\"0\" (keydown)=\"handleKeyboardEvent($event)\" [ngStyle]=\"fieldStyles\">\r\n\r\n <div class=\"cc-selected-value\" *ngIf=\"hasValue()\">\r\n {{ multiple ? value.length + ' ' + resolvedLabels.selectedSuffix : getSelectedLabel() }}\r\n </div>\r\n <div class=\"cc-placeholder\" *ngIf=\"!hasValue()\">{{ placeholder }}</div>\r\n\r\n <div class=\"cc-actions\">\r\n <span *ngIf=\"clearable && hasValue() && !disabled\" class=\"cc-clear-btn\" (click)=\"clearSelection($event)\"\r\n [attr.aria-label]=\"resolvedLabels.clearAriaLabel\">\r\n <span class=\"material-icons\">close</span>\r\n </span>\r\n <span class=\"cc-arrow material-icons\">expand_more</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Dropdown Menu -->\r\n <div class=\"cc-dropdown-menu\" *ngIf=\"isOpen\">\r\n <!-- Search -->\r\n <div class=\"cc-search-container\" *ngIf=\"searchable\">\r\n <input type=\"text\" #searchInput [placeholder]=\"resolvedLabels.searchPlaceholder\"\r\n (input)=\"onSearch($any($event.target).value)\" (click)=\"$event.stopPropagation()\"\r\n class=\"cc-dropdown-search-input\" />\r\n </div>\r\n\r\n <!-- Options Virtual Scroll -->\r\n <cdk-virtual-scroll-viewport itemSize=\"40\" class=\"cc-virtual-scroll\"\r\n [style.height.px]=\"filteredOptions.length * 40 > 240 ? 240 : (filteredOptions.length * 40 || 40)\">\r\n\r\n <div *cdkVirtualFor=\"let option of filteredOptions; let i = index\" class=\"cc-option\"\r\n [class.selected]=\"isSelected(option)\" [class.focused]=\"i === focusedIndex\"\r\n [class.disabled]=\"option.disabled\" (click)=\"selectOption(option)\">\r\n\r\n <!-- Multi-select checkbox visual -->\r\n <span class=\"cc-multi-check\" *ngIf=\"multiple\">\r\n <span class=\"material-icons\" *ngIf=\"isSelected(option)\">check_box</span>\r\n <span class=\"material-icons\" *ngIf=\"!isSelected(option)\">check_box_outline_blank</span>\r\n </span>\r\n\r\n <!-- Icon -->\r\n <span class=\"cc-option-icon-wrapper\" *ngIf=\"option.icon\">\r\n <span class=\"material-icons\" *ngIf=\"getIconType(option.icon) === 'material'\">{{\r\n getIconValue(option.icon) }}</span>\r\n <i *ngIf=\"getIconType(option.icon) === 'fontawesome'\" [class]=\"getIconValue(option.icon)\"></i>\r\n <img *ngIf=\"getIconType(option.icon) === 'img'\" [src]=\"getIconValue(option.icon)\"\r\n class=\"cc-option-img\" alt=\"icon\" />\r\n </span>\r\n\r\n <span class=\"cc-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Single-select check visual -->\r\n <span class=\"cc-single-check\" *ngIf=\"!multiple && isSelected(option)\">\r\n <span class=\"material-icons\">check</span>\r\n </span>\r\n </div>\r\n\r\n <div *ngIf=\"filteredOptions.length === 0\" class=\"cc-no-results\">{{ resolvedLabels.noResultsFound }}</div>\r\n </cdk-virtual-scroll-viewport>\r\n </div>\r\n\r\n <!-- Error Message -->\r\n <div class=\"cc-error-message\" *ngIf=\"errorMessage\">{{ errorMessage }}</div>\r\n</div>", styles: [".cc-dropdown-wrapper{display:flex;flex-direction:column;width:100%;position:relative;font-family:var(--cc-dropdown-font-family, inherit);gap:var(--cc-dropdown-label-gap, .5rem)}.cc-dropdown-label{font-size:var(--cc-dropdown-font-size, .875rem);font-weight:var(--cc-dropdown-font-weight, 500);color:var(--cc-dropdown-label-color, #202124);margin:0;line-height:1.4}.cc-required{color:var(--cc-dropdown-required-color, #D93025);margin-left:.25rem}.cc-dropdown-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;min-height:var(--cc-dropdown-height, 2.5rem);padding:var(--cc-dropdown-padding, .5rem .75rem);background-color:var(--cc-dropdown-bg, #FEFEFE);border:var(--cc-dropdown-border-width, 1px) solid var(--cc-dropdown-border-color, #BDC1C6);border-radius:var(--cc-dropdown-border-radius, .4375rem);cursor:pointer;-webkit-user-select:none;user-select:none;box-sizing:border-box;transition:all .2s}.cc-dropdown-trigger:focus{outline:none;border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-dropdown-trigger.open{border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-dropdown-trigger.error{border-color:var(--cc-dropdown-error-color, #D93025)}.cc-dropdown-trigger.disabled{background-color:var(--cc-dropdown-disabled-bg, #F1F3F4);cursor:not-allowed;color:var(--cc-dropdown-disabled-color, #80868B)}.cc-dropdown-trigger.disabled .cc-arrow,.cc-dropdown-trigger.disabled .cc-clear-btn{color:var(--cc-dropdown-disabled-color, #80868B)}.cc-selected-value{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--cc-dropdown-color, #202124);font-size:var(--cc-dropdown-font-size, .875rem);font-weight:var(--cc-dropdown-font-weight, 400)}.cc-placeholder{flex:1;color:var(--cc-dropdown-placeholder-color, #80868B);font-size:var(--cc-dropdown-font-size, .875rem)}.cc-actions{display:flex;align-items:center;gap:.5rem}.cc-arrow{color:var(--cc-dropdown-arrow-color, #5F6368);transition:transform .2s;font-size:1.25rem}.cc-dropdown-trigger.open .cc-arrow{transform:rotate(180deg)}.cc-clear-btn{display:flex;align-items:center;justify-content:center;color:var(--cc-dropdown-arrow-color, #5F6368);cursor:pointer}.cc-clear-btn:hover{color:#202124}.cc-clear-btn .material-icons{font-size:1.125rem}.cc-dropdown-menu{position:absolute;top:100%;left:0;width:100%;background-color:#fff;border:1px solid #BDC1C6;border-radius:0 0 4px 4px;box-shadow:0 2px 4px #0000001a;z-index:1000;margin-top:2px;overflow:hidden;animation:fadeIn .1s ease-out}@keyframes fadeIn{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.cc-search-container{padding:.5rem;border-bottom:1px solid var(--cc-dropdown-divider-color, #E0E0E0)}.cc-dropdown-search-input{width:100%;padding:.5rem;border:1px solid #BDC1C6;border-radius:4px;outline:none;box-sizing:border-box;font-size:.875rem}.cc-dropdown-search-input:focus{border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-virtual-scroll{width:100%;max-height:240px}.cc-option{display:flex;align-items:center;padding:0 .75rem;height:40px;cursor:pointer;color:var(--cc-dropdown-color, #202124);font-size:var(--cc-dropdown-font-size, .875rem);transition:background-color .1s;box-sizing:border-box}.cc-option:hover,.cc-option.focused{background-color:#f1f3f4}.cc-option.selected{background-color:#e8f0fe;color:#1a73e8}.cc-option.disabled{opacity:.5;cursor:not-allowed;background-color:transparent}.cc-multi-check{margin-right:.5rem;color:var(--cc-dropdown-arrow-color, #5F6368);display:flex;align-items:center}.cc-multi-check .material-icons{font-size:1.25rem}.cc-option.selected .cc-multi-check{color:#1a73e8}.cc-option-icon-wrapper{margin-right:.5rem;display:flex;align-items:center}.cc-option-icon-wrapper .material-icons{font-size:1.25rem}.cc-option-img{width:1.25rem;height:1.25rem;object-fit:contain}.cc-option-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cc-single-check{margin-left:.5rem;color:#1a73e8;display:flex;align-items:center}.cc-single-check .material-icons{font-size:1.125rem}.cc-no-results{padding:.75rem;color:#80868b;text-align:center;font-size:.875rem}.cc-error-message{font-size:.75rem;color:var(--cc-dropdown-error-color, #D93025);margin-top:.25rem}\n"] }]
|
|
4098
4144
|
}], propDecorators: { config: [{
|
|
4099
4145
|
type: Input
|
|
4100
4146
|
}], labels: [{
|
|
@@ -9254,7 +9300,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
9254
9300
|
}] });
|
|
9255
9301
|
|
|
9256
9302
|
const DEFAULT_ITEMS_PER_PAGE = 10;
|
|
9257
|
-
const DEFAULT_PAGE_SIZE_OPTIONS = [10, 20, 50];
|
|
9303
|
+
const DEFAULT_PAGE_SIZE_OPTIONS = [5, 10, 20, 50, 100];
|
|
9258
9304
|
const PAGINATION_THEME_DEFAULT = 'theme-1';
|
|
9259
9305
|
const PAGINATION_THEME_DARK = 'theme-2';
|
|
9260
9306
|
// Nav
|
|
@@ -9279,7 +9325,7 @@ class SideNavComponent {
|
|
|
9279
9325
|
/** Whether to hide icons when the side nav is expanded */
|
|
9280
9326
|
hideIconsWhenExpanded = false;
|
|
9281
9327
|
/** Whether to show tooltips on nav items */
|
|
9282
|
-
showTooltips =
|
|
9328
|
+
showTooltips = false;
|
|
9283
9329
|
/** Position of the tooltip */
|
|
9284
9330
|
tooltipPosition = DEFAULT_SIDE_NAV_TOOLTIP_POSITION;
|
|
9285
9331
|
/** Optional dictionary for label translation */
|
|
@@ -9372,14 +9418,30 @@ class SideNavComponent {
|
|
|
9372
9418
|
styles['--cc-side-nav-active-color'] = this.styleConfig.activeColor;
|
|
9373
9419
|
if (this.styleConfig.activeHoverBg)
|
|
9374
9420
|
styles['--cc-side-nav-active-hover-bg'] = this.styleConfig.activeHoverBg;
|
|
9421
|
+
if (this.styleConfig.toggleBg)
|
|
9422
|
+
styles['--cc-side-nav-toggle-bg'] = this.styleConfig.toggleBg;
|
|
9423
|
+
if (this.styleConfig.toggleBorderColor)
|
|
9424
|
+
styles['--cc-side-nav-toggle-border-color'] = this.styleConfig.toggleBorderColor;
|
|
9425
|
+
if (this.styleConfig.tooltipBg)
|
|
9426
|
+
styles['--cc-side-nav-tooltip-bg'] = this.styleConfig.tooltipBg;
|
|
9427
|
+
if (this.styleConfig.tooltipColor)
|
|
9428
|
+
styles['--cc-side-nav-tooltip-color'] = this.styleConfig.tooltipColor;
|
|
9429
|
+
if (this.styleConfig.tooltipFontWeight)
|
|
9430
|
+
styles['--cc-side-nav-tooltip-font-weight'] = this.styleConfig.tooltipFontWeight;
|
|
9431
|
+
if (this.styleConfig.tooltipLetterSpacing)
|
|
9432
|
+
styles['--cc-side-nav-tooltip-letter-spacing'] = this.styleConfig.tooltipLetterSpacing;
|
|
9433
|
+
if (this.styleConfig.tooltipOffset)
|
|
9434
|
+
styles['--cc-side-nav-tooltip-offset'] = this.styleConfig.tooltipOffset;
|
|
9435
|
+
if (this.styleConfig.tooltipShadow)
|
|
9436
|
+
styles['--cc-side-nav-tooltip-shadow'] = this.styleConfig.tooltipShadow;
|
|
9375
9437
|
return styles;
|
|
9376
9438
|
}
|
|
9377
9439
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SideNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9378
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SideNavComponent, isStandalone: false, selector: "lib-side-nav", inputs: { sections: "sections", userRoles: "userRoles", activeId: "activeId", styleConfig: "styleConfig", collapsed: "collapsed", width: "width", collapsedWidth: "collapsedWidth", showCollapseToggle: "showCollapseToggle", hideIconsWhenExpanded: "hideIconsWhenExpanded", showTooltips: "showTooltips", tooltipPosition: "tooltipPosition", labels: "labels" }, outputs: { itemClicked: "itemClicked", collapsedChange: "collapsedChange" }, host: { properties: { "class.cc-side-nav-host-collapsed": "this.isHostCollapsed" } }, usesOnChanges: true, ngImport: i0, template: "<nav class=\"cc-side-nav\" [class.collapsed]=\"collapsed\" role=\"navigation\" [ngStyle]=\"customStyles\">\r\n\r\n <!-- Fallback standalone toggle if no sections exist -->\r\n <div class=\"cc-side-nav-toggle-wrap standalone\" *ngIf=\"filteredSections.length === 0 && showCollapseToggle\">\r\n <button class=\"cc-side-nav-toggle\" (click)=\"toggleCollapse()\" type=\"button\"\r\n [attr.aria-label]=\"collapsed ? 'Expand navigation' : 'Collapse navigation'\">\r\n <span class=\"material-icons cc-side-nav-toggle-icon\">\r\n {{ collapsed ? 'menu_open' : 'menu' }}\r\n </span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"cc-side-nav-section\" *ngFor=\"let section of filteredSections; let i = index\">\r\n\r\n <div class=\"cc-side-nav-heading-row\" *ngIf=\"section.heading || (i === 0 && showCollapseToggle)\">\r\n <h3 class=\"cc-side-nav-heading\" *ngIf=\"section.heading && !collapsed\">\r\n {{ section.heading }}\r\n </h3>\r\n\r\n <div class=\"cc-side-nav-toggle-wrap\" *ngIf=\"i === 0 && showCollapseToggle\"\r\n [class.no-heading]=\"!section.heading || collapsed\">\r\n <button class=\"cc-side-nav-toggle\" (click)=\"toggleCollapse()\" type=\"button\"\r\n [attr.aria-label]=\"collapsed ? 'Expand navigation' : 'Collapse navigation'\">\r\n <span class=\"material-icons cc-side-nav-toggle-icon\">\r\n {{ collapsed ? 'menu_open' : 'menu' }}\r\n </span>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <ul class=\"cc-side-nav-list\">\r\n <li *ngFor=\"let item of section.items\" class=\"cc-side-nav-list-item\">\r\n <a (click)=\"onItemClick(item, $event)\" class=\"cc-side-nav-link\" [class.active]=\"item.id === activeId\"\r\n [class.disabled]=\"item.disabled\" [matTooltip]=\"item.tooltip || item.label\"\r\n [matTooltipDisabled]=\"!showTooltips\" [matTooltipPosition]=\"tooltipPosition\"\r\n [matTooltipClass]=\"'cc-side-nav-tooltip'\" [attr.aria-current]=\"(item.id === activeId) ? 'page' : null\"\r\n [attr.aria-disabled]=\"item.disabled ? true : null\">\r\n <span *ngIf=\"item.icon && (collapsed || !hideIconsWhenExpanded)\" class=\"cc-side-nav-icon\">\r\n\r\n <span *ngIf=\"!item.icon.includes('fa') && !item.icon.includes('bi')\" class=\"material-icons\">\r\n {{ item.icon }}\r\n </span>\r\n\r\n <i *ngIf=\"item.icon.includes('fa') || item.icon.includes('bi')\" [class]=\"item.icon\"></i>\r\n\r\n </span>\r\n <span class=\"cc-side-nav-label\" *ngIf=\"!collapsed\">{{ item.label }}</span>\r\n <span class=\"cc-side-nav-arrow material-icons\"\r\n *ngIf=\"!collapsed && (item.id === activeId) && item.showArrow !== false\">chevron_right</span>\r\n </a>\r\n </li>\r\n </ul>\r\n\r\n </div>\r\n\r\n</nav>", styles: [":host{display:block;height:100%;overflow-y:auto;overflow-x:hidden;width:var(--cc-side-nav-width, 220px);transition:width .25s ease;--cc-side-nav-bg: rgba(249, 200, 14, .0509803922);--cc-side-nav-width: 220px;--cc-side-nav-collapsed-width: 56px;--cc-side-nav-gap-sections: 24px;--cc-side-nav-padding: 16px;--cc-side-nav-font-family: Poppins, sans-serif;--cc-side-nav-heading-font-weight: 500;--cc-side-nav-heading-font-size: 16px;--cc-side-nav-heading-color: #3C4043;--cc-side-nav-item-gap: 4px;--cc-side-nav-item-padding: 12px 16px;--cc-side-nav-item-border-radius: 8px;--cc-side-nav-item-font-weight: 400;--cc-side-nav-item-font-size: 14px;--cc-side-nav-item-color: #5F6368;--cc-side-nav-item-hover-bg: rgba(230, 62, 48, .05);--cc-side-nav-item-hover-color: #3C4043;--cc-side-nav-active-bg: #E63E30;--cc-side-nav-active-color: #FFFFFF;--cc-side-nav-active-font-weight: 500;--cc-side-nav-active-hover-bg: #D4382B;--cc-side-nav-disabled-opacity: .5;--cc-side-nav-tooltip-bg: rgba(0, 0, 0, .8);--cc-side-nav-tooltip-color: #FFFFFF;--cc-side-nav-tooltip-padding: 8px 12px;--cc-side-nav-tooltip-border-radius: 6px;--cc-side-nav-tooltip-font-size: 12px}:host.cc-side-nav-host-collapsed{width:var(--cc-side-nav-collapsed-width, 56px)}:host .cc-side-nav-heading-row{display:flex;align-items:center;justify-content:space-between;min-height:28px}:host .cc-side-nav-toggle{background:transparent;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:var(--cc-side-nav-item-color, #5F6368);transition:color .2s ease,background .2s ease;border-radius:6px}:host .cc-side-nav-toggle:hover{background:#0000000d}:host .cc-side-nav-toggle .material-icons{font-size:20px}:host::-webkit-scrollbar{width:3px}:host::-webkit-scrollbar-track{background:transparent}:host::-webkit-scrollbar-thumb{background:#00000026;border-radius:2px}:host::-webkit-scrollbar-thumb:hover{background:#0000004d}.cc-side-nav{display:flex;flex-direction:column;gap:var(--cc-side-nav-gap-sections, 24px);padding:var(--cc-side-nav-padding, 16px);width:var(--cc-side-nav-width, 220px);min-width:var(--cc-side-nav-width, 220px);box-sizing:border-box;background-color:var(--cc-side-nav-bg, rgba(249, 200, 14, .0509803922));min-height:100%;transition:width .25s ease,min-width .25s ease,padding .25s ease;overflow:hidden}.cc-side-nav.collapsed{width:var(--cc-side-nav-collapsed-width, 56px);min-width:var(--cc-side-nav-collapsed-width, 56px);padding:var(--cc-side-nav-padding, 16px) 8px}.cc-side-nav.collapsed .cc-side-nav-link{justify-content:center;gap:0;padding:10px}.cc-side-nav.collapsed .cc-side-nav-icon{font-size:20px}.cc-side-nav-section{display:flex;flex-direction:column;gap:12px}.cc-side-nav-heading{margin:0;padding:0 16px;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif);font-weight:var(--cc-side-nav-heading-font-weight, 500);font-style:normal;font-size:var(--cc-side-nav-heading-font-size, 16px);text-transform:uppercase;color:var(--cc-side-nav-heading-color, #3C4043);flex:1}.cc-side-nav-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--cc-side-nav-item-gap, 4px)}.cc-side-nav-list-item{margin:0;padding:0}.cc-side-nav-link{display:flex;align-items:center;gap:12px;padding:var(--cc-side-nav-item-padding, 12px 16px);text-decoration:none;cursor:pointer;border-radius:var(--cc-side-nav-item-border-radius, 8px);transition:background-color .2s ease,opacity .2s ease;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif);font-weight:var(--cc-side-nav-item-font-weight, 400);font-style:normal;font-size:var(--cc-side-nav-item-font-size, 14px);color:var(--cc-side-nav-item-color, #5F6368)}.cc-side-nav-link:hover:not(.disabled){background-color:var(--cc-side-nav-item-hover-bg, rgba(230, 62, 48, .05));color:var(--cc-side-nav-item-hover-color, #3C4043)}.cc-side-nav-link.active{background:var(--cc-side-nav-active-bg, #E63E30);opacity:1;border-radius:var(--cc-side-nav-item-border-radius, 8px);font-weight:var(--cc-side-nav-active-font-weight, 500);color:var(--cc-side-nav-active-color, #FFFFFF)}.cc-side-nav-link.active:hover{background-color:var(--cc-side-nav-active-hover-bg, #D4382B)}.cc-side-nav-link.disabled{cursor:not-allowed;opacity:var(--cc-side-nav-disabled-opacity, .5)}.cc-side-nav-icon{display:flex;align-items:center;justify-content:center;font-size:16px}.cc-side-nav-header{display:flex;justify-content:flex-end;padding:8px 16px}.cc-side-nav-label{flex:1;white-space:nowrap;overflow:hidden}.collapsed .cc-side-nav-label{display:none}.cc-side-nav-arrow{margin-left:auto;font-size:18px;opacity:.7}.cc-side-nav-link.active .cc-side-nav-arrow{color:var(--cc-side-nav-active-color, #FFFFFF);opacity:1}::ng-deep .cc-side-nav-tooltip{background-color:var(--cc-side-nav-tooltip-bg, rgba(0, 0, 0, .8))!important;color:var(--cc-side-nav-tooltip-color, #FFFFFF)!important;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif)!important;font-size:var(--cc-side-nav-tooltip-font-size, 12px)!important;padding:var(--cc-side-nav-tooltip-padding, 8px 12px)!important;border-radius:var(--cc-side-nav-tooltip-border-radius, 6px)!important;box-shadow:0 4px 12px #00000026;margin-left:8px!important}.cc-side-nav-toggle-wrap{display:flex;align-items:center;justify-content:flex-end}.cc-side-nav-toggle-wrap.standalone{padding-bottom:8px}.cc-side-nav-toggle-wrap.no-heading{flex:1}.collapsed .cc-side-nav-toggle-wrap{justify-content:center;width:100%}.cc-side-nav-toggle{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;border-radius:50%;background-color:transparent;cursor:pointer;color:var(--cc-side-nav-item-color, #5F6368);transition:background-color .2s ease,color .2s ease}.cc-side-nav-toggle:hover{background-color:var(--cc-side-nav-item-hover-bg, rgba(0, 0, 0, .06));color:var(--cc-side-nav-heading-color, #3C4043)}.cc-side-nav-toggle-icon{font-size:18px;line-height:1;display:block;transition:transform .25s ease}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
|
|
9440
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SideNavComponent, isStandalone: false, selector: "lib-side-nav", inputs: { sections: "sections", userRoles: "userRoles", activeId: "activeId", styleConfig: "styleConfig", collapsed: "collapsed", width: "width", collapsedWidth: "collapsedWidth", showCollapseToggle: "showCollapseToggle", hideIconsWhenExpanded: "hideIconsWhenExpanded", showTooltips: "showTooltips", tooltipPosition: "tooltipPosition", labels: "labels" }, outputs: { itemClicked: "itemClicked", collapsedChange: "collapsedChange" }, host: { properties: { "class.cc-side-nav-host-collapsed": "this.isHostCollapsed" } }, usesOnChanges: true, ngImport: i0, template: "<nav class=\"cc-side-nav\" [class.collapsed]=\"collapsed\" role=\"navigation\" [ngStyle]=\"customStyles\">\n\n <div class=\"cc-side-nav-section\" *ngFor=\"let section of filteredSections; let i = index\">\n\n <div class=\"cc-side-nav-heading-row\" *ngIf=\"section.heading && !collapsed\">\n <h3 class=\"cc-side-nav-heading\">{{ section.heading }}</h3>\n </div>\n\n <ul class=\"cc-side-nav-list\">\n <li *ngFor=\"let item of section.items\" class=\"cc-side-nav-list-item\">\n <a (click)=\"onItemClick(item, $event)\" class=\"cc-side-nav-link\" [class.active]=\"item.id === activeId\"\n [class.disabled]=\"item.disabled\" [matTooltip]=\"item.tooltip || item.label\"\n [matTooltipDisabled]=\"collapsed ? false : !showTooltips\" [matTooltipPosition]=\"tooltipPosition\"\n [matTooltipClass]=\"'cc-side-nav-tooltip'\" [attr.aria-current]=\"(item.id === activeId) ? 'page' : null\"\n [attr.aria-disabled]=\"item.disabled ? true : null\">\n <span *ngIf=\"item.icon && (collapsed || !hideIconsWhenExpanded)\" class=\"cc-side-nav-icon\">\n <span *ngIf=\"!item.icon.includes('fa') && !item.icon.includes('bi')\" class=\"material-icons\">\n {{ item.icon }}\n </span>\n <i *ngIf=\"item.icon.includes('fa') || item.icon.includes('bi')\" [class]=\"item.icon\"></i>\n </span>\n <!-- Label: always in DOM, CSS transitions handle fade on collapse -->\n <span class=\"cc-side-nav-label\">{{ item.label }}</span>\n <!-- Arrow: rendered for active items regardless of collapse state; CSS hides it -->\n <span class=\"cc-side-nav-arrow material-icons\"\n *ngIf=\"(item.id === activeId) && item.showArrow !== false\">chevron_right</span>\n </a>\n </li>\n </ul>\n\n </div>\n\n</nav>\n\n<!--\n Toggle pill \u2014 always straddles the right edge of :host (right: -12px).\n Hidden via opacity/scale until :host is hovered; always visible when collapsed.\n Single chevron SVG rotates 180\u00B0 via CSS \u2014 no icon swap needed.\n-->\n<button *ngIf=\"showCollapseToggle\"\n class=\"cc-sn-toggle\"\n (click)=\"toggleCollapse()\"\n type=\"button\"\n [attr.aria-label]=\"collapsed ? 'Expand navigation' : 'Collapse navigation'\">\n <svg class=\"cc-sn-chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\">\n <path d=\"M10 3L5 8L10 13\"\n stroke=\"currentColor\"\n stroke-width=\"1.75\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"/>\n </svg>\n</button>\n", styles: [":host{display:block;height:100%;overflow:visible;position:relative;width:var(--cc-side-nav-width, 220px);transition:width .3s cubic-bezier(.4,0,.2,1);--cc-side-nav-bg: #ffffff;--cc-side-nav-border-color: rgba(0, 0, 0, .08);--cc-side-nav-width: 220px;--cc-side-nav-collapsed-width: 56px;--cc-side-nav-gap-sections: 24px;--cc-side-nav-padding: 16px;--cc-side-nav-font-family: Poppins, sans-serif;--cc-side-nav-heading-font-weight: 500;--cc-side-nav-heading-font-size: 16px;--cc-side-nav-heading-color: #3C4043;--cc-side-nav-item-gap: 4px;--cc-side-nav-item-padding: 12px 16px;--cc-side-nav-item-border-radius: 8px;--cc-side-nav-item-font-weight: 400;--cc-side-nav-item-font-size: 14px;--cc-side-nav-item-color: #5F6368;--cc-side-nav-item-hover-bg: rgba(30, 30, 45, .08);--cc-side-nav-item-hover-color: #1e1e2d;--cc-side-nav-active-bg: #1e1e2d;--cc-side-nav-active-color: #ffffff;--cc-side-nav-active-font-weight: 500;--cc-side-nav-active-hover-bg: #2a2a3d;--cc-side-nav-disabled-opacity: .5;--cc-side-nav-toggle-bg: #ffffff;--cc-side-nav-toggle-border-color: rgba(0, 0, 0, .1);--cc-side-nav-tooltip-bg: #1e1e2d;--cc-side-nav-tooltip-color: #FFFFFF;--cc-side-nav-tooltip-padding: 8px 14px;--cc-side-nav-tooltip-border-radius: 6px;--cc-side-nav-tooltip-font-size: 12px;--cc-side-nav-tooltip-font-weight: 500;--cc-side-nav-tooltip-letter-spacing: .2px;--cc-side-nav-tooltip-offset: 20px;--cc-side-nav-tooltip-shadow: 0 4px 12px rgba(0, 0, 0, .35)}:host.cc-side-nav-host-collapsed{width:var(--cc-side-nav-collapsed-width, 56px)}:host:after{content:\"\";position:absolute;top:10%;right:0;width:2px;height:80%;background:var(--cc-side-nav-active-bg, #1e1e2d);border-radius:2px;opacity:0;transition:opacity .25s ease;pointer-events:none;z-index:4}:host:hover:after{opacity:.18}:host.cc-side-nav-host-collapsed:after{display:none}.cc-sn-toggle{position:absolute;bottom:20px;right:-12px;width:24px;height:24px;border-radius:50%;border:1.5px solid var(--cc-side-nav-toggle-border-color, rgba(0, 0, 0, .1));background:var(--cc-side-nav-toggle-bg, #ffffff);cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:20;padding:0;color:var(--cc-side-nav-item-color, #5F6368);box-shadow:0 1px 4px #0000001a,0 0 0 1px #0000000a;opacity:0;transform:scale(.65);transition:opacity .2s ease,transform .25s cubic-bezier(.34,1.56,.64,1),background .15s ease,color .15s ease,border-color .15s ease,box-shadow .2s ease}.cc-sn-toggle:focus-visible{outline:2px solid var(--cc-side-nav-active-bg, #E63E30);outline-offset:2px}.cc-sn-toggle .cc-sn-chevron{width:12px;height:12px;flex-shrink:0;display:block;transition:transform .3s cubic-bezier(.4,0,.2,1)}:host:hover .cc-sn-toggle{opacity:1;transform:scale(1)}:host:hover .cc-sn-toggle:hover,:host.cc-side-nav-host-collapsed .cc-sn-toggle:hover{background:var(--cc-side-nav-active-bg, #E63E30);color:var(--cc-side-nav-active-color, #ffffff);border-color:transparent;box-shadow:0 4px 14px #0000001a,0 1px 4px #0000001a;transform:scale(1.12)}:host.cc-side-nav-host-collapsed .cc-sn-toggle{opacity:1;transform:scale(1);box-shadow:0 2px 8px #00000021,0 0 0 1.5px #00000014}:host.cc-side-nav-host-collapsed .cc-sn-chevron{transform:rotate(180deg)}.cc-side-nav{display:flex;flex-direction:column;gap:var(--cc-side-nav-gap-sections, 24px);padding:var(--cc-side-nav-padding, 16px);width:var(--cc-side-nav-width, 220px);min-width:var(--cc-side-nav-width, 220px);box-sizing:border-box;background-color:var(--cc-side-nav-bg, #ffffff);border-right:1px solid var(--cc-side-nav-border-color, rgba(0, 0, 0, .08));height:100%;overflow-y:auto;overflow-x:hidden;transition:width .3s cubic-bezier(.4,0,.2,1),min-width .3s cubic-bezier(.4,0,.2,1),padding .3s cubic-bezier(.4,0,.2,1)}.cc-side-nav::-webkit-scrollbar{width:3px}.cc-side-nav::-webkit-scrollbar-track{background:transparent}.cc-side-nav::-webkit-scrollbar-thumb{background:#00000026;border-radius:2px}.cc-side-nav::-webkit-scrollbar-thumb:hover{background:#0000004d}.cc-side-nav.collapsed{width:var(--cc-side-nav-collapsed-width, 56px);min-width:var(--cc-side-nav-collapsed-width, 56px);padding:var(--cc-side-nav-padding, 16px) 8px}.cc-side-nav.collapsed .cc-side-nav-link{justify-content:center;gap:0;padding:10px}.cc-side-nav.collapsed .cc-side-nav-icon{font-size:20px}.cc-side-nav-section{display:flex;flex-direction:column;gap:12px}.cc-side-nav-heading-row{display:flex;align-items:center;justify-content:space-between;min-height:28px}.cc-side-nav-heading{margin:0;padding:0 16px;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif);font-weight:var(--cc-side-nav-heading-font-weight, 500);font-style:normal;font-size:var(--cc-side-nav-heading-font-size, 16px);text-transform:uppercase;color:var(--cc-side-nav-heading-color, #3C4043);flex:1}.cc-side-nav-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--cc-side-nav-item-gap, 4px)}.cc-side-nav-list-item{margin:0;padding:0}.cc-side-nav-link{display:flex;align-items:center;gap:12px;padding:var(--cc-side-nav-item-padding, 12px 16px);text-decoration:none;cursor:pointer;border-radius:var(--cc-side-nav-item-border-radius, 8px);transition:background-color .2s ease,opacity .2s ease;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif);font-weight:var(--cc-side-nav-item-font-weight, 400);font-style:normal;font-size:var(--cc-side-nav-item-font-size, 14px);color:var(--cc-side-nav-item-color, #5F6368)}.cc-side-nav-link:hover:not(.disabled){background-color:var(--cc-side-nav-item-hover-bg, rgba(30, 30, 45, .08));color:var(--cc-side-nav-item-hover-color, #1e1e2d)}.cc-side-nav-link.active{background:var(--cc-side-nav-active-bg, #1e1e2d);opacity:1;border-radius:var(--cc-side-nav-item-border-radius, 8px);font-weight:var(--cc-side-nav-active-font-weight, 500);color:var(--cc-side-nav-active-color, #ffffff)}.cc-side-nav-link.active:hover{background-color:var(--cc-side-nav-active-hover-bg, #2a2a3d);color:var(--cc-side-nav-active-color, #ffffff)}.cc-side-nav-link.disabled{cursor:not-allowed;opacity:var(--cc-side-nav-disabled-opacity, .5)}.cc-side-nav-icon{display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0}.cc-side-nav-label{flex:1;white-space:nowrap;overflow:hidden;max-width:200px;opacity:1;transition:opacity .15s ease,max-width .3s cubic-bezier(.4,0,.2,1)}.collapsed .cc-side-nav-label{opacity:0;max-width:0}.cc-side-nav-arrow{margin-left:auto;font-size:18px;opacity:.7;overflow:hidden;max-width:30px;flex-shrink:0;white-space:nowrap;transition:opacity .15s ease,max-width .3s cubic-bezier(.4,0,.2,1)}.collapsed .cc-side-nav-arrow{opacity:0;max-width:0}.cc-side-nav-link.active .cc-side-nav-arrow{color:var(--cc-side-nav-active-color, #FFFFFF);opacity:1}::ng-deep .mat-mdc-tooltip.cc-side-nav-tooltip{margin-left:var(--cc-side-nav-tooltip-offset, 20px)!important;overflow:visible!important}::ng-deep .mat-mdc-tooltip.cc-side-nav-tooltip .mdc-tooltip__surface{background-color:var(--cc-side-nav-tooltip-bg, #1e1e2d)!important;color:var(--cc-side-nav-tooltip-color, #FFFFFF)!important;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif)!important;font-size:var(--cc-side-nav-tooltip-font-size, 12px)!important;font-weight:var(--cc-side-nav-tooltip-font-weight, 500)!important;padding:var(--cc-side-nav-tooltip-padding, 8px 14px)!important;border-radius:var(--cc-side-nav-tooltip-border-radius, 6px)!important;box-shadow:var(--cc-side-nav-tooltip-shadow, 0 4px 12px rgba(0, 0, 0, .35))!important;letter-spacing:var(--cc-side-nav-tooltip-letter-spacing, .2px)!important;position:relative!important;overflow:visible!important}::ng-deep .mat-mdc-tooltip.cc-side-nav-tooltip .mdc-tooltip__surface:before{content:\"\";position:absolute;left:-6px;top:50%;transform:translateY(-50%);width:0;height:0;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:6px solid var(--cc-side-nav-tooltip-bg, #1e1e2d)}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
|
|
9379
9441
|
}
|
|
9380
9442
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SideNavComponent, decorators: [{
|
|
9381
9443
|
type: Component,
|
|
9382
|
-
args: [{ selector: 'lib-side-nav', standalone: false, template: "<nav class=\"cc-side-nav\" [class.collapsed]=\"collapsed\" role=\"navigation\" [ngStyle]=\"customStyles\">\r\n\r\n <!-- Fallback standalone toggle if no sections exist -->\r\n <div class=\"cc-side-nav-toggle-wrap standalone\" *ngIf=\"filteredSections.length === 0 && showCollapseToggle\">\r\n <button class=\"cc-side-nav-toggle\" (click)=\"toggleCollapse()\" type=\"button\"\r\n [attr.aria-label]=\"collapsed ? 'Expand navigation' : 'Collapse navigation'\">\r\n <span class=\"material-icons cc-side-nav-toggle-icon\">\r\n {{ collapsed ? 'menu_open' : 'menu' }}\r\n </span>\r\n </button>\r\n </div>\r\n\r\n <div class=\"cc-side-nav-section\" *ngFor=\"let section of filteredSections; let i = index\">\r\n\r\n <div class=\"cc-side-nav-heading-row\" *ngIf=\"section.heading || (i === 0 && showCollapseToggle)\">\r\n <h3 class=\"cc-side-nav-heading\" *ngIf=\"section.heading && !collapsed\">\r\n {{ section.heading }}\r\n </h3>\r\n\r\n <div class=\"cc-side-nav-toggle-wrap\" *ngIf=\"i === 0 && showCollapseToggle\"\r\n [class.no-heading]=\"!section.heading || collapsed\">\r\n <button class=\"cc-side-nav-toggle\" (click)=\"toggleCollapse()\" type=\"button\"\r\n [attr.aria-label]=\"collapsed ? 'Expand navigation' : 'Collapse navigation'\">\r\n <span class=\"material-icons cc-side-nav-toggle-icon\">\r\n {{ collapsed ? 'menu_open' : 'menu' }}\r\n </span>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <ul class=\"cc-side-nav-list\">\r\n <li *ngFor=\"let item of section.items\" class=\"cc-side-nav-list-item\">\r\n <a (click)=\"onItemClick(item, $event)\" class=\"cc-side-nav-link\" [class.active]=\"item.id === activeId\"\r\n [class.disabled]=\"item.disabled\" [matTooltip]=\"item.tooltip || item.label\"\r\n [matTooltipDisabled]=\"!showTooltips\" [matTooltipPosition]=\"tooltipPosition\"\r\n [matTooltipClass]=\"'cc-side-nav-tooltip'\" [attr.aria-current]=\"(item.id === activeId) ? 'page' : null\"\r\n [attr.aria-disabled]=\"item.disabled ? true : null\">\r\n <span *ngIf=\"item.icon && (collapsed || !hideIconsWhenExpanded)\" class=\"cc-side-nav-icon\">\r\n\r\n <span *ngIf=\"!item.icon.includes('fa') && !item.icon.includes('bi')\" class=\"material-icons\">\r\n {{ item.icon }}\r\n </span>\r\n\r\n <i *ngIf=\"item.icon.includes('fa') || item.icon.includes('bi')\" [class]=\"item.icon\"></i>\r\n\r\n </span>\r\n <span class=\"cc-side-nav-label\" *ngIf=\"!collapsed\">{{ item.label }}</span>\r\n <span class=\"cc-side-nav-arrow material-icons\"\r\n *ngIf=\"!collapsed && (item.id === activeId) && item.showArrow !== false\">chevron_right</span>\r\n </a>\r\n </li>\r\n </ul>\r\n\r\n </div>\r\n\r\n</nav>", styles: [":host{display:block;height:100%;overflow-y:auto;overflow-x:hidden;width:var(--cc-side-nav-width, 220px);transition:width .25s ease;--cc-side-nav-bg: rgba(249, 200, 14, .0509803922);--cc-side-nav-width: 220px;--cc-side-nav-collapsed-width: 56px;--cc-side-nav-gap-sections: 24px;--cc-side-nav-padding: 16px;--cc-side-nav-font-family: Poppins, sans-serif;--cc-side-nav-heading-font-weight: 500;--cc-side-nav-heading-font-size: 16px;--cc-side-nav-heading-color: #3C4043;--cc-side-nav-item-gap: 4px;--cc-side-nav-item-padding: 12px 16px;--cc-side-nav-item-border-radius: 8px;--cc-side-nav-item-font-weight: 400;--cc-side-nav-item-font-size: 14px;--cc-side-nav-item-color: #5F6368;--cc-side-nav-item-hover-bg: rgba(230, 62, 48, .05);--cc-side-nav-item-hover-color: #3C4043;--cc-side-nav-active-bg: #E63E30;--cc-side-nav-active-color: #FFFFFF;--cc-side-nav-active-font-weight: 500;--cc-side-nav-active-hover-bg: #D4382B;--cc-side-nav-disabled-opacity: .5;--cc-side-nav-tooltip-bg: rgba(0, 0, 0, .8);--cc-side-nav-tooltip-color: #FFFFFF;--cc-side-nav-tooltip-padding: 8px 12px;--cc-side-nav-tooltip-border-radius: 6px;--cc-side-nav-tooltip-font-size: 12px}:host.cc-side-nav-host-collapsed{width:var(--cc-side-nav-collapsed-width, 56px)}:host .cc-side-nav-heading-row{display:flex;align-items:center;justify-content:space-between;min-height:28px}:host .cc-side-nav-toggle{background:transparent;border:none;cursor:pointer;padding:4px;display:flex;align-items:center;justify-content:center;color:var(--cc-side-nav-item-color, #5F6368);transition:color .2s ease,background .2s ease;border-radius:6px}:host .cc-side-nav-toggle:hover{background:#0000000d}:host .cc-side-nav-toggle .material-icons{font-size:20px}:host::-webkit-scrollbar{width:3px}:host::-webkit-scrollbar-track{background:transparent}:host::-webkit-scrollbar-thumb{background:#00000026;border-radius:2px}:host::-webkit-scrollbar-thumb:hover{background:#0000004d}.cc-side-nav{display:flex;flex-direction:column;gap:var(--cc-side-nav-gap-sections, 24px);padding:var(--cc-side-nav-padding, 16px);width:var(--cc-side-nav-width, 220px);min-width:var(--cc-side-nav-width, 220px);box-sizing:border-box;background-color:var(--cc-side-nav-bg, rgba(249, 200, 14, .0509803922));min-height:100%;transition:width .25s ease,min-width .25s ease,padding .25s ease;overflow:hidden}.cc-side-nav.collapsed{width:var(--cc-side-nav-collapsed-width, 56px);min-width:var(--cc-side-nav-collapsed-width, 56px);padding:var(--cc-side-nav-padding, 16px) 8px}.cc-side-nav.collapsed .cc-side-nav-link{justify-content:center;gap:0;padding:10px}.cc-side-nav.collapsed .cc-side-nav-icon{font-size:20px}.cc-side-nav-section{display:flex;flex-direction:column;gap:12px}.cc-side-nav-heading{margin:0;padding:0 16px;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif);font-weight:var(--cc-side-nav-heading-font-weight, 500);font-style:normal;font-size:var(--cc-side-nav-heading-font-size, 16px);text-transform:uppercase;color:var(--cc-side-nav-heading-color, #3C4043);flex:1}.cc-side-nav-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--cc-side-nav-item-gap, 4px)}.cc-side-nav-list-item{margin:0;padding:0}.cc-side-nav-link{display:flex;align-items:center;gap:12px;padding:var(--cc-side-nav-item-padding, 12px 16px);text-decoration:none;cursor:pointer;border-radius:var(--cc-side-nav-item-border-radius, 8px);transition:background-color .2s ease,opacity .2s ease;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif);font-weight:var(--cc-side-nav-item-font-weight, 400);font-style:normal;font-size:var(--cc-side-nav-item-font-size, 14px);color:var(--cc-side-nav-item-color, #5F6368)}.cc-side-nav-link:hover:not(.disabled){background-color:var(--cc-side-nav-item-hover-bg, rgba(230, 62, 48, .05));color:var(--cc-side-nav-item-hover-color, #3C4043)}.cc-side-nav-link.active{background:var(--cc-side-nav-active-bg, #E63E30);opacity:1;border-radius:var(--cc-side-nav-item-border-radius, 8px);font-weight:var(--cc-side-nav-active-font-weight, 500);color:var(--cc-side-nav-active-color, #FFFFFF)}.cc-side-nav-link.active:hover{background-color:var(--cc-side-nav-active-hover-bg, #D4382B)}.cc-side-nav-link.disabled{cursor:not-allowed;opacity:var(--cc-side-nav-disabled-opacity, .5)}.cc-side-nav-icon{display:flex;align-items:center;justify-content:center;font-size:16px}.cc-side-nav-header{display:flex;justify-content:flex-end;padding:8px 16px}.cc-side-nav-label{flex:1;white-space:nowrap;overflow:hidden}.collapsed .cc-side-nav-label{display:none}.cc-side-nav-arrow{margin-left:auto;font-size:18px;opacity:.7}.cc-side-nav-link.active .cc-side-nav-arrow{color:var(--cc-side-nav-active-color, #FFFFFF);opacity:1}::ng-deep .cc-side-nav-tooltip{background-color:var(--cc-side-nav-tooltip-bg, rgba(0, 0, 0, .8))!important;color:var(--cc-side-nav-tooltip-color, #FFFFFF)!important;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif)!important;font-size:var(--cc-side-nav-tooltip-font-size, 12px)!important;padding:var(--cc-side-nav-tooltip-padding, 8px 12px)!important;border-radius:var(--cc-side-nav-tooltip-border-radius, 6px)!important;box-shadow:0 4px 12px #00000026;margin-left:8px!important}.cc-side-nav-toggle-wrap{display:flex;align-items:center;justify-content:flex-end}.cc-side-nav-toggle-wrap.standalone{padding-bottom:8px}.cc-side-nav-toggle-wrap.no-heading{flex:1}.collapsed .cc-side-nav-toggle-wrap{justify-content:center;width:100%}.cc-side-nav-toggle{display:flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;border-radius:50%;background-color:transparent;cursor:pointer;color:var(--cc-side-nav-item-color, #5F6368);transition:background-color .2s ease,color .2s ease}.cc-side-nav-toggle:hover{background-color:var(--cc-side-nav-item-hover-bg, rgba(0, 0, 0, .06));color:var(--cc-side-nav-heading-color, #3C4043)}.cc-side-nav-toggle-icon{font-size:18px;line-height:1;display:block;transition:transform .25s ease}\n"] }]
|
|
9444
|
+
args: [{ selector: 'lib-side-nav', standalone: false, template: "<nav class=\"cc-side-nav\" [class.collapsed]=\"collapsed\" role=\"navigation\" [ngStyle]=\"customStyles\">\n\n <div class=\"cc-side-nav-section\" *ngFor=\"let section of filteredSections; let i = index\">\n\n <div class=\"cc-side-nav-heading-row\" *ngIf=\"section.heading && !collapsed\">\n <h3 class=\"cc-side-nav-heading\">{{ section.heading }}</h3>\n </div>\n\n <ul class=\"cc-side-nav-list\">\n <li *ngFor=\"let item of section.items\" class=\"cc-side-nav-list-item\">\n <a (click)=\"onItemClick(item, $event)\" class=\"cc-side-nav-link\" [class.active]=\"item.id === activeId\"\n [class.disabled]=\"item.disabled\" [matTooltip]=\"item.tooltip || item.label\"\n [matTooltipDisabled]=\"collapsed ? false : !showTooltips\" [matTooltipPosition]=\"tooltipPosition\"\n [matTooltipClass]=\"'cc-side-nav-tooltip'\" [attr.aria-current]=\"(item.id === activeId) ? 'page' : null\"\n [attr.aria-disabled]=\"item.disabled ? true : null\">\n <span *ngIf=\"item.icon && (collapsed || !hideIconsWhenExpanded)\" class=\"cc-side-nav-icon\">\n <span *ngIf=\"!item.icon.includes('fa') && !item.icon.includes('bi')\" class=\"material-icons\">\n {{ item.icon }}\n </span>\n <i *ngIf=\"item.icon.includes('fa') || item.icon.includes('bi')\" [class]=\"item.icon\"></i>\n </span>\n <!-- Label: always in DOM, CSS transitions handle fade on collapse -->\n <span class=\"cc-side-nav-label\">{{ item.label }}</span>\n <!-- Arrow: rendered for active items regardless of collapse state; CSS hides it -->\n <span class=\"cc-side-nav-arrow material-icons\"\n *ngIf=\"(item.id === activeId) && item.showArrow !== false\">chevron_right</span>\n </a>\n </li>\n </ul>\n\n </div>\n\n</nav>\n\n<!--\n Toggle pill \u2014 always straddles the right edge of :host (right: -12px).\n Hidden via opacity/scale until :host is hovered; always visible when collapsed.\n Single chevron SVG rotates 180\u00B0 via CSS \u2014 no icon swap needed.\n-->\n<button *ngIf=\"showCollapseToggle\"\n class=\"cc-sn-toggle\"\n (click)=\"toggleCollapse()\"\n type=\"button\"\n [attr.aria-label]=\"collapsed ? 'Expand navigation' : 'Collapse navigation'\">\n <svg class=\"cc-sn-chevron\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n aria-hidden=\"true\">\n <path d=\"M10 3L5 8L10 13\"\n stroke=\"currentColor\"\n stroke-width=\"1.75\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"/>\n </svg>\n</button>\n", styles: [":host{display:block;height:100%;overflow:visible;position:relative;width:var(--cc-side-nav-width, 220px);transition:width .3s cubic-bezier(.4,0,.2,1);--cc-side-nav-bg: #ffffff;--cc-side-nav-border-color: rgba(0, 0, 0, .08);--cc-side-nav-width: 220px;--cc-side-nav-collapsed-width: 56px;--cc-side-nav-gap-sections: 24px;--cc-side-nav-padding: 16px;--cc-side-nav-font-family: Poppins, sans-serif;--cc-side-nav-heading-font-weight: 500;--cc-side-nav-heading-font-size: 16px;--cc-side-nav-heading-color: #3C4043;--cc-side-nav-item-gap: 4px;--cc-side-nav-item-padding: 12px 16px;--cc-side-nav-item-border-radius: 8px;--cc-side-nav-item-font-weight: 400;--cc-side-nav-item-font-size: 14px;--cc-side-nav-item-color: #5F6368;--cc-side-nav-item-hover-bg: rgba(30, 30, 45, .08);--cc-side-nav-item-hover-color: #1e1e2d;--cc-side-nav-active-bg: #1e1e2d;--cc-side-nav-active-color: #ffffff;--cc-side-nav-active-font-weight: 500;--cc-side-nav-active-hover-bg: #2a2a3d;--cc-side-nav-disabled-opacity: .5;--cc-side-nav-toggle-bg: #ffffff;--cc-side-nav-toggle-border-color: rgba(0, 0, 0, .1);--cc-side-nav-tooltip-bg: #1e1e2d;--cc-side-nav-tooltip-color: #FFFFFF;--cc-side-nav-tooltip-padding: 8px 14px;--cc-side-nav-tooltip-border-radius: 6px;--cc-side-nav-tooltip-font-size: 12px;--cc-side-nav-tooltip-font-weight: 500;--cc-side-nav-tooltip-letter-spacing: .2px;--cc-side-nav-tooltip-offset: 20px;--cc-side-nav-tooltip-shadow: 0 4px 12px rgba(0, 0, 0, .35)}:host.cc-side-nav-host-collapsed{width:var(--cc-side-nav-collapsed-width, 56px)}:host:after{content:\"\";position:absolute;top:10%;right:0;width:2px;height:80%;background:var(--cc-side-nav-active-bg, #1e1e2d);border-radius:2px;opacity:0;transition:opacity .25s ease;pointer-events:none;z-index:4}:host:hover:after{opacity:.18}:host.cc-side-nav-host-collapsed:after{display:none}.cc-sn-toggle{position:absolute;bottom:20px;right:-12px;width:24px;height:24px;border-radius:50%;border:1.5px solid var(--cc-side-nav-toggle-border-color, rgba(0, 0, 0, .1));background:var(--cc-side-nav-toggle-bg, #ffffff);cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:20;padding:0;color:var(--cc-side-nav-item-color, #5F6368);box-shadow:0 1px 4px #0000001a,0 0 0 1px #0000000a;opacity:0;transform:scale(.65);transition:opacity .2s ease,transform .25s cubic-bezier(.34,1.56,.64,1),background .15s ease,color .15s ease,border-color .15s ease,box-shadow .2s ease}.cc-sn-toggle:focus-visible{outline:2px solid var(--cc-side-nav-active-bg, #E63E30);outline-offset:2px}.cc-sn-toggle .cc-sn-chevron{width:12px;height:12px;flex-shrink:0;display:block;transition:transform .3s cubic-bezier(.4,0,.2,1)}:host:hover .cc-sn-toggle{opacity:1;transform:scale(1)}:host:hover .cc-sn-toggle:hover,:host.cc-side-nav-host-collapsed .cc-sn-toggle:hover{background:var(--cc-side-nav-active-bg, #E63E30);color:var(--cc-side-nav-active-color, #ffffff);border-color:transparent;box-shadow:0 4px 14px #0000001a,0 1px 4px #0000001a;transform:scale(1.12)}:host.cc-side-nav-host-collapsed .cc-sn-toggle{opacity:1;transform:scale(1);box-shadow:0 2px 8px #00000021,0 0 0 1.5px #00000014}:host.cc-side-nav-host-collapsed .cc-sn-chevron{transform:rotate(180deg)}.cc-side-nav{display:flex;flex-direction:column;gap:var(--cc-side-nav-gap-sections, 24px);padding:var(--cc-side-nav-padding, 16px);width:var(--cc-side-nav-width, 220px);min-width:var(--cc-side-nav-width, 220px);box-sizing:border-box;background-color:var(--cc-side-nav-bg, #ffffff);border-right:1px solid var(--cc-side-nav-border-color, rgba(0, 0, 0, .08));height:100%;overflow-y:auto;overflow-x:hidden;transition:width .3s cubic-bezier(.4,0,.2,1),min-width .3s cubic-bezier(.4,0,.2,1),padding .3s cubic-bezier(.4,0,.2,1)}.cc-side-nav::-webkit-scrollbar{width:3px}.cc-side-nav::-webkit-scrollbar-track{background:transparent}.cc-side-nav::-webkit-scrollbar-thumb{background:#00000026;border-radius:2px}.cc-side-nav::-webkit-scrollbar-thumb:hover{background:#0000004d}.cc-side-nav.collapsed{width:var(--cc-side-nav-collapsed-width, 56px);min-width:var(--cc-side-nav-collapsed-width, 56px);padding:var(--cc-side-nav-padding, 16px) 8px}.cc-side-nav.collapsed .cc-side-nav-link{justify-content:center;gap:0;padding:10px}.cc-side-nav.collapsed .cc-side-nav-icon{font-size:20px}.cc-side-nav-section{display:flex;flex-direction:column;gap:12px}.cc-side-nav-heading-row{display:flex;align-items:center;justify-content:space-between;min-height:28px}.cc-side-nav-heading{margin:0;padding:0 16px;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif);font-weight:var(--cc-side-nav-heading-font-weight, 500);font-style:normal;font-size:var(--cc-side-nav-heading-font-size, 16px);text-transform:uppercase;color:var(--cc-side-nav-heading-color, #3C4043);flex:1}.cc-side-nav-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--cc-side-nav-item-gap, 4px)}.cc-side-nav-list-item{margin:0;padding:0}.cc-side-nav-link{display:flex;align-items:center;gap:12px;padding:var(--cc-side-nav-item-padding, 12px 16px);text-decoration:none;cursor:pointer;border-radius:var(--cc-side-nav-item-border-radius, 8px);transition:background-color .2s ease,opacity .2s ease;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif);font-weight:var(--cc-side-nav-item-font-weight, 400);font-style:normal;font-size:var(--cc-side-nav-item-font-size, 14px);color:var(--cc-side-nav-item-color, #5F6368)}.cc-side-nav-link:hover:not(.disabled){background-color:var(--cc-side-nav-item-hover-bg, rgba(30, 30, 45, .08));color:var(--cc-side-nav-item-hover-color, #1e1e2d)}.cc-side-nav-link.active{background:var(--cc-side-nav-active-bg, #1e1e2d);opacity:1;border-radius:var(--cc-side-nav-item-border-radius, 8px);font-weight:var(--cc-side-nav-active-font-weight, 500);color:var(--cc-side-nav-active-color, #ffffff)}.cc-side-nav-link.active:hover{background-color:var(--cc-side-nav-active-hover-bg, #2a2a3d);color:var(--cc-side-nav-active-color, #ffffff)}.cc-side-nav-link.disabled{cursor:not-allowed;opacity:var(--cc-side-nav-disabled-opacity, .5)}.cc-side-nav-icon{display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0}.cc-side-nav-label{flex:1;white-space:nowrap;overflow:hidden;max-width:200px;opacity:1;transition:opacity .15s ease,max-width .3s cubic-bezier(.4,0,.2,1)}.collapsed .cc-side-nav-label{opacity:0;max-width:0}.cc-side-nav-arrow{margin-left:auto;font-size:18px;opacity:.7;overflow:hidden;max-width:30px;flex-shrink:0;white-space:nowrap;transition:opacity .15s ease,max-width .3s cubic-bezier(.4,0,.2,1)}.collapsed .cc-side-nav-arrow{opacity:0;max-width:0}.cc-side-nav-link.active .cc-side-nav-arrow{color:var(--cc-side-nav-active-color, #FFFFFF);opacity:1}::ng-deep .mat-mdc-tooltip.cc-side-nav-tooltip{margin-left:var(--cc-side-nav-tooltip-offset, 20px)!important;overflow:visible!important}::ng-deep .mat-mdc-tooltip.cc-side-nav-tooltip .mdc-tooltip__surface{background-color:var(--cc-side-nav-tooltip-bg, #1e1e2d)!important;color:var(--cc-side-nav-tooltip-color, #FFFFFF)!important;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif)!important;font-size:var(--cc-side-nav-tooltip-font-size, 12px)!important;font-weight:var(--cc-side-nav-tooltip-font-weight, 500)!important;padding:var(--cc-side-nav-tooltip-padding, 8px 14px)!important;border-radius:var(--cc-side-nav-tooltip-border-radius, 6px)!important;box-shadow:var(--cc-side-nav-tooltip-shadow, 0 4px 12px rgba(0, 0, 0, .35))!important;letter-spacing:var(--cc-side-nav-tooltip-letter-spacing, .2px)!important;position:relative!important;overflow:visible!important}::ng-deep .mat-mdc-tooltip.cc-side-nav-tooltip .mdc-tooltip__surface:before{content:\"\";position:absolute;left:-6px;top:50%;transform:translateY(-50%);width:0;height:0;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:6px solid var(--cc-side-nav-tooltip-bg, #1e1e2d)}\n"] }]
|
|
9383
9445
|
}], propDecorators: { sections: [{
|
|
9384
9446
|
type: Input
|
|
9385
9447
|
}], userRoles: [{
|
|
@@ -9513,11 +9575,11 @@ class PaginationComponent {
|
|
|
9513
9575
|
}
|
|
9514
9576
|
}
|
|
9515
9577
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9516
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: PaginationComponent, isStandalone: false, selector: "lib-pagination", inputs: { totalItems: "totalItems", itemsPerPage: "itemsPerPage", currentPage: "currentPage", pageSizeOptions: "pageSizeOptions", theme: "theme", labels: "labels" }, outputs: { pageChange: "pageChange", itemsPerPageChange: "itemsPerPageChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"cc-pagination\" [ngClass]=\"theme\">\r\n <!-- Left Side: Items Info & Per Page -->\r\n <div class=\"pagination-left\" *ngIf=\"pageSizeOptions.length > 1 || totalItems > 0\">\r\n <span class=\"items-info\">\r\n {{ startItem }}-{{ endItem }} {{ labels.of }} {{ totalItems }} {{ labels.items }}\r\n </span>\r\n\r\n <div class=\"per-page-selector\" *ngIf=\"pageSizeOptions.length > 1\">\r\n <div class=\"select-wrapper\">\r\n <select
|
|
9578
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: PaginationComponent, isStandalone: false, selector: "lib-pagination", inputs: { totalItems: "totalItems", itemsPerPage: "itemsPerPage", currentPage: "currentPage", pageSizeOptions: "pageSizeOptions", theme: "theme", labels: "labels" }, outputs: { pageChange: "pageChange", itemsPerPageChange: "itemsPerPageChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"cc-pagination\" [ngClass]=\"theme\">\r\n <!-- Left Side: Items Info & Per Page -->\r\n <div class=\"pagination-left\" *ngIf=\"pageSizeOptions.length > 1 || totalItems > 0\">\r\n <span class=\"items-info\">\r\n {{ startItem }}-{{ endItem }} {{ labels.of }} {{ totalItems }} {{ labels.items }}\r\n </span>\r\n\r\n <div class=\"per-page-selector\" *ngIf=\"pageSizeOptions.length > 1\">\r\n <div class=\"select-wrapper\">\r\n <select (change)=\"onItemsPerPageChange($event)\">\r\n <option *ngFor=\"let option of pageSizeOptions\" [value]=\"option\" [selected]=\"option === itemsPerPage\">\r\n {{ option }}\r\n </option>\r\n </select>\r\n <span class=\"select-arrow\"></span>\r\n </div>\r\n <span class=\"per-page-label\">{{ labels.perPage }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Right Side: Page Controls -->\r\n <div class=\"pagination-right\">\r\n <button \r\n class=\"page-btn prev-btn\" \r\n [disabled]=\"currentPage === 1\" \r\n (click)=\"prevPage()\"\r\n aria-label=\"Previous Page\">\r\n <span class=\"icon\">‹</span>\r\n </button>\r\n\r\n <div class=\"page-numbers\">\r\n <ng-container *ngFor=\"let page of pages\">\r\n <button \r\n *ngIf=\"page !== '...'\"\r\n class=\"page-btn number-btn\" \r\n [class.active]=\"page === currentPage\"\r\n (click)=\"onPageChange(page)\">\r\n {{ page }}\r\n </button>\r\n <span *ngIf=\"page === '...'\" class=\"ellipsis\">...</span>\r\n </ng-container>\r\n </div>\r\n\r\n <button \r\n class=\"page-btn next-btn\" \r\n [disabled]=\"currentPage === totalPages || totalPages === 0\" \r\n (click)=\"nextPage()\"\r\n aria-label=\"Next Page\">\r\n <span class=\"icon\">›</span>\r\n </button>\r\n </div>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";.cc-pagination{display:flex;justify-content:space-between;align-items:center;padding:1rem;font-family:var(--cc-pagination-font-family);color:var(--cc-pagination-text-color);font-size:var(--cc-pagination-font-size);flex-wrap:wrap;gap:1rem;width:100%;box-sizing:border-box}.cc-pagination .pagination-left{display:flex;align-items:center;gap:1.5rem;flex-wrap:wrap}.cc-pagination .pagination-left .items-info{white-space:nowrap}.cc-pagination .pagination-left .per-page-selector{display:flex;align-items:center;gap:.5rem}.cc-pagination .pagination-left .per-page-selector .select-wrapper{position:relative;display:inline-block}.cc-pagination .pagination-left .per-page-selector .select-wrapper select{appearance:none;background-color:var(--cc-pagination-select-bg);border:var(--cc-pagination-select-border);border-radius:var(--cc-pagination-select-radius);padding:var(--cc-pagination-select-padding);padding-right:2rem;color:var(--cc-pagination-select-text-color);font-family:inherit;font-size:inherit;cursor:pointer;min-width:60px}.cc-pagination .pagination-left .per-page-selector .select-wrapper select:focus{outline:none;box-shadow:0 0 0 2px #0000001a}.cc-pagination .pagination-left .per-page-selector .select-wrapper:after{content:\"\\25bc\";position:absolute;right:10px;top:50%;transform:translateY(-50%);font-size:.7em;color:var(--cc-pagination-select-arrow-color);pointer-events:none}.cc-pagination .pagination-left .per-page-selector .per-page-label{white-space:nowrap}.cc-pagination .pagination-right{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.cc-pagination .pagination-right .page-btn{display:flex;align-items:center;justify-content:center;min-width:var(--cc-pagination-btn-size);height:var(--cc-pagination-btn-size);background-color:var(--cc-pagination-btn-bg);border:var(--cc-pagination-btn-border);border-radius:var(--cc-pagination-btn-radius);color:var(--cc-pagination-btn-text-color);font-family:inherit;cursor:pointer;transition:all .2s ease;padding:0 .5rem;-webkit-user-select:none;user-select:none}.cc-pagination .pagination-right .page-btn:hover:not(:disabled){background-color:var(--cc-pagination-btn-hover-bg)}.cc-pagination .pagination-right .page-btn:disabled{opacity:var(--cc-pagination-disabled-opacity);background-color:var(--cc-pagination-disabled-bg);cursor:not-allowed}.cc-pagination .pagination-right .page-btn.active{background-color:var(--cc-pagination-btn-active-bg);border:var(--cc-pagination-btn-active-border);color:var(--cc-pagination-btn-active-text);font-weight:700}.cc-pagination .pagination-right .page-btn .icon{font-size:1.2em;line-height:1}.cc-pagination .pagination-right .page-numbers{display:flex;gap:.5rem;flex-wrap:wrap}.cc-pagination .pagination-right .ellipsis{display:flex;align-items:center;justify-content:center;color:var(--cc-pagination-text-color);width:20px}@media(max-width:600px){.cc-pagination{flex-direction:column;align-items:center;gap:1.5rem}.cc-pagination .pagination-left{width:100%;justify-content:space-between;order:2}.cc-pagination .pagination-right{width:100%;justify-content:center;order:1}.cc-pagination .pagination-right .page-numbers{justify-content:center}}\n"], dependencies: [{ 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"] }] });
|
|
9517
9579
|
}
|
|
9518
9580
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: PaginationComponent, decorators: [{
|
|
9519
9581
|
type: Component,
|
|
9520
|
-
args: [{ selector: 'lib-pagination', standalone: false, template: "<div class=\"cc-pagination\" [ngClass]=\"theme\">\r\n <!-- Left Side: Items Info & Per Page -->\r\n <div class=\"pagination-left\" *ngIf=\"pageSizeOptions.length > 1 || totalItems > 0\">\r\n <span class=\"items-info\">\r\n {{ startItem }}-{{ endItem }} {{ labels.of }} {{ totalItems }} {{ labels.items }}\r\n </span>\r\n\r\n <div class=\"per-page-selector\" *ngIf=\"pageSizeOptions.length > 1\">\r\n <div class=\"select-wrapper\">\r\n <select
|
|
9582
|
+
args: [{ selector: 'lib-pagination', standalone: false, template: "<div class=\"cc-pagination\" [ngClass]=\"theme\">\r\n <!-- Left Side: Items Info & Per Page -->\r\n <div class=\"pagination-left\" *ngIf=\"pageSizeOptions.length > 1 || totalItems > 0\">\r\n <span class=\"items-info\">\r\n {{ startItem }}-{{ endItem }} {{ labels.of }} {{ totalItems }} {{ labels.items }}\r\n </span>\r\n\r\n <div class=\"per-page-selector\" *ngIf=\"pageSizeOptions.length > 1\">\r\n <div class=\"select-wrapper\">\r\n <select (change)=\"onItemsPerPageChange($event)\">\r\n <option *ngFor=\"let option of pageSizeOptions\" [value]=\"option\" [selected]=\"option === itemsPerPage\">\r\n {{ option }}\r\n </option>\r\n </select>\r\n <span class=\"select-arrow\"></span>\r\n </div>\r\n <span class=\"per-page-label\">{{ labels.perPage }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Right Side: Page Controls -->\r\n <div class=\"pagination-right\">\r\n <button \r\n class=\"page-btn prev-btn\" \r\n [disabled]=\"currentPage === 1\" \r\n (click)=\"prevPage()\"\r\n aria-label=\"Previous Page\">\r\n <span class=\"icon\">‹</span>\r\n </button>\r\n\r\n <div class=\"page-numbers\">\r\n <ng-container *ngFor=\"let page of pages\">\r\n <button \r\n *ngIf=\"page !== '...'\"\r\n class=\"page-btn number-btn\" \r\n [class.active]=\"page === currentPage\"\r\n (click)=\"onPageChange(page)\">\r\n {{ page }}\r\n </button>\r\n <span *ngIf=\"page === '...'\" class=\"ellipsis\">...</span>\r\n </ng-container>\r\n </div>\r\n\r\n <button \r\n class=\"page-btn next-btn\" \r\n [disabled]=\"currentPage === totalPages || totalPages === 0\" \r\n (click)=\"nextPage()\"\r\n aria-label=\"Next Page\">\r\n <span class=\"icon\">›</span>\r\n </button>\r\n </div>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";.cc-pagination{display:flex;justify-content:space-between;align-items:center;padding:1rem;font-family:var(--cc-pagination-font-family);color:var(--cc-pagination-text-color);font-size:var(--cc-pagination-font-size);flex-wrap:wrap;gap:1rem;width:100%;box-sizing:border-box}.cc-pagination .pagination-left{display:flex;align-items:center;gap:1.5rem;flex-wrap:wrap}.cc-pagination .pagination-left .items-info{white-space:nowrap}.cc-pagination .pagination-left .per-page-selector{display:flex;align-items:center;gap:.5rem}.cc-pagination .pagination-left .per-page-selector .select-wrapper{position:relative;display:inline-block}.cc-pagination .pagination-left .per-page-selector .select-wrapper select{appearance:none;background-color:var(--cc-pagination-select-bg);border:var(--cc-pagination-select-border);border-radius:var(--cc-pagination-select-radius);padding:var(--cc-pagination-select-padding);padding-right:2rem;color:var(--cc-pagination-select-text-color);font-family:inherit;font-size:inherit;cursor:pointer;min-width:60px}.cc-pagination .pagination-left .per-page-selector .select-wrapper select:focus{outline:none;box-shadow:0 0 0 2px #0000001a}.cc-pagination .pagination-left .per-page-selector .select-wrapper:after{content:\"\\25bc\";position:absolute;right:10px;top:50%;transform:translateY(-50%);font-size:.7em;color:var(--cc-pagination-select-arrow-color);pointer-events:none}.cc-pagination .pagination-left .per-page-selector .per-page-label{white-space:nowrap}.cc-pagination .pagination-right{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.cc-pagination .pagination-right .page-btn{display:flex;align-items:center;justify-content:center;min-width:var(--cc-pagination-btn-size);height:var(--cc-pagination-btn-size);background-color:var(--cc-pagination-btn-bg);border:var(--cc-pagination-btn-border);border-radius:var(--cc-pagination-btn-radius);color:var(--cc-pagination-btn-text-color);font-family:inherit;cursor:pointer;transition:all .2s ease;padding:0 .5rem;-webkit-user-select:none;user-select:none}.cc-pagination .pagination-right .page-btn:hover:not(:disabled){background-color:var(--cc-pagination-btn-hover-bg)}.cc-pagination .pagination-right .page-btn:disabled{opacity:var(--cc-pagination-disabled-opacity);background-color:var(--cc-pagination-disabled-bg);cursor:not-allowed}.cc-pagination .pagination-right .page-btn.active{background-color:var(--cc-pagination-btn-active-bg);border:var(--cc-pagination-btn-active-border);color:var(--cc-pagination-btn-active-text);font-weight:700}.cc-pagination .pagination-right .page-btn .icon{font-size:1.2em;line-height:1}.cc-pagination .pagination-right .page-numbers{display:flex;gap:.5rem;flex-wrap:wrap}.cc-pagination .pagination-right .ellipsis{display:flex;align-items:center;justify-content:center;color:var(--cc-pagination-text-color);width:20px}@media(max-width:600px){.cc-pagination{flex-direction:column;align-items:center;gap:1.5rem}.cc-pagination .pagination-left{width:100%;justify-content:space-between;order:2}.cc-pagination .pagination-right{width:100%;justify-content:center;order:1}.cc-pagination .pagination-right .page-numbers{justify-content:center}}\n"] }]
|
|
9521
9583
|
}], ctorParameters: () => [], propDecorators: { totalItems: [{
|
|
9522
9584
|
type: Input
|
|
9523
9585
|
}], itemsPerPage: [{
|
|
@@ -9622,6 +9684,7 @@ class SmartTableComponent {
|
|
|
9622
9684
|
}
|
|
9623
9685
|
ngOnInit() {
|
|
9624
9686
|
if (this.config) {
|
|
9687
|
+
this.applyPaginationDefaults();
|
|
9625
9688
|
if (this.config.sortBy) {
|
|
9626
9689
|
this.activeSort.key = this.config.sortBy;
|
|
9627
9690
|
}
|
|
@@ -9653,6 +9716,7 @@ class SmartTableComponent {
|
|
|
9653
9716
|
}
|
|
9654
9717
|
// Config changed (non-first) — reload filter opts; reload data only if NOT in external mode
|
|
9655
9718
|
if (changes['config'] && !changes['config'].firstChange) {
|
|
9719
|
+
this.applyPaginationDefaults();
|
|
9656
9720
|
this.loadFilterOptions();
|
|
9657
9721
|
if (this.tableData === undefined) {
|
|
9658
9722
|
this.loadData();
|
|
@@ -10298,6 +10362,23 @@ class SmartTableComponent {
|
|
|
10298
10362
|
get columnCount() {
|
|
10299
10363
|
return this.config.columns.length;
|
|
10300
10364
|
}
|
|
10365
|
+
get showPagination() {
|
|
10366
|
+
if (!this.config?.pagination?.enabled)
|
|
10367
|
+
return false;
|
|
10368
|
+
const pageSize = this.config.pagination.pageSize;
|
|
10369
|
+
const effectiveTotal = this.totalItems > 0 ? this.totalItems : this.data.length;
|
|
10370
|
+
return effectiveTotal > 10;
|
|
10371
|
+
}
|
|
10372
|
+
applyPaginationDefaults() {
|
|
10373
|
+
if (this.config?.pagination?.enabled) {
|
|
10374
|
+
if (!this.config.pagination.pageSize) {
|
|
10375
|
+
this.config.pagination.pageSize = 10;
|
|
10376
|
+
}
|
|
10377
|
+
if (!this.config.pagination.pageSizeOptions || this.config.pagination.pageSizeOptions.length === 0) {
|
|
10378
|
+
this.config.pagination.pageSizeOptions = [5, 10, 20, 50, 100];
|
|
10379
|
+
}
|
|
10380
|
+
}
|
|
10381
|
+
}
|
|
10301
10382
|
onColumnClick(row, col) {
|
|
10302
10383
|
if (col.clickAction === 'callback') {
|
|
10303
10384
|
this.columnClick.emit({ row, column: col.key });
|
|
@@ -10341,11 +10422,11 @@ class SmartTableComponent {
|
|
|
10341
10422
|
this.activeFilterData = null;
|
|
10342
10423
|
}
|
|
10343
10424
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartTableComponent, deps: [{ token: i3.HttpClient }, { token: i1$4.Router }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
|
|
10344
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SmartTableComponent, isStandalone: false, selector: "lib-smart-table", inputs: { config: "config", tableData: "tableData", totalItemsCount: "totalItemsCount", selectedRows: "selectedRows" }, outputs: { action: "action", topAction: "topAction", filterChange: "filterChange", rowSelect: "rowSelect", columnClick: "columnClick", sortChange: "sortChange", pageChange: "pageChange", searchChange: "searchChange" }, host: { listeners: { "document:click": "closeDropdown()" } }, viewQueries: [{ propertyName: "stickyHeaders", predicate: ["stickyHeader"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"smart-table-outer\">\r\n <!-- Table Card -->\r\n <div class=\"smart-table-wrapper\">\r\n <!-- Top Toolbar -->\r\n <div class=\"st-toolbar\" *ngIf=\"config.searchConfig?.enabled || (config.filters && config.filters.length > 0) || (config.topBarButtons && config.topBarButtons.length > 0)\">\r\n\r\n <!-- Search -->\r\n <div class=\"st-search\" *ngIf=\"config.searchConfig?.enabled\">\r\n <i class=\"fa fa-search\"></i>\r\n <input type=\"text\" [placeholder]=\"config.labels?.searchPlaceholder || 'Search'\" (input)=\"onSearch($event)\">\r\n </div>\r\n\r\n <!-- Filters -->\r\n <div class=\"st-filters\" *ngIf=\"config.filters\">\r\n <div class=\"st-filter-item\" *ngFor=\"let filter of config.filters\">\r\n <!-- Trigger -->\r\n <div class=\"st-filter-trigger\"\r\n [class.active]=\"openFilterKey === filter.key\"\r\n [class.has-value]=\"isFilterActive(filter)\"\r\n (click)=\"toggleFilter(filter.key, $event)\">\r\n <span class=\"st-filter-trigger-label\">{{ getFilterDisplay(filter) }}</span>\r\n <svg class=\"st-filter-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\r\n <path d=\"M2.5 4.5L6 8L9.5 4.5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n </svg>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Top Bar Buttons -->\r\n <div class=\"st-actions\" *ngIf=\"config.topBarButtons\">\r\n <lib-button *ngFor=\"let btn of config.topBarButtons\"\r\n [variant]=\"btn.btnVariant || 'primary'\"\r\n [icon]=\"btn.icon || ''\"\r\n (click)=\"onTopAction(btn)\">\r\n {{ btn.label }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n\r\n <!-- Selection count strip \u2014 visible whenever selectable is enabled -->\r\n <div class=\"st-selection-strip\" *ngIf=\"config.selectable\">\r\n <span class=\"st-selection-count\">{{ selectedRows.length }} of {{ totalItems > 0 ? totalItems : data.length }} selected</span>\r\n </div>\r\n\r\n <!-- Table Container -->\r\n <div class=\"st-table-container\">\r\n <div class=\"st-check-loader\" *ngIf=\"loading\">\r\n <div class=\"spinner\"></div>\r\n </div>\r\n <table class=\"st-table\" [class.loading-data]=\"loading\" [class.is-selectable]=\"config.selectable\">\r\n <thead>\r\n <tr>\r\n <th *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\r\n <input type=\"checkbox\" (change)=\"onSelectAll($event)\">\r\n </th>\r\n <th *ngFor=\"let col of config.columns; let colIndex = index\"\r\n #stickyHeader\r\n [class.sortable]=\"col.sortable\"\r\n [class.sticky-col]=\"col.sticky\"\r\n [class.first-data-col]=\"colIndex === 0\"\r\n [ngStyle]=\"stickyColumnStyles[col.key]\"\r\n (click)=\"onSort(col)\">\r\n {{ col.label }}\r\n <span *ngIf=\"col.sortable\" class=\"sort-icon-wrap\">\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\">\r\n <!-- Down arrow \u2014 RIGHT side, prominent when DESC -->\r\n <path d=\"M11.258 15.8153L14.5913 13.2699C14.7483 13.15 14.8364 12.9875 14.8364 12.818C14.8364 12.6486 14.7483 12.4861 14.5913 12.3662C14.4344 12.2464 14.2216 12.1791 13.9997 12.1791C13.7778 12.1791 13.5649 12.2464 13.408 12.3662L11.4997 13.8299V2.63636C11.4997 2.46758 11.4119 2.30572 11.2556 2.18638C11.0993 2.06704 10.8874 2 10.6663 2C10.4453 2 10.2334 2.06704 10.0771 2.18638C9.92081 2.30572 9.83301 2.46758 9.83301 2.63636V15.3635C9.83383 15.4891 9.88333 15.6117 9.97528 15.7159C10.0672 15.8201 10.1975 15.9012 10.3497 15.9489C10.4486 15.9845 10.557 16.002 10.6663 15.9998C10.776 16.0003 10.8847 15.9843 10.9863 15.9526C11.0878 15.9209 11.1801 15.8743 11.258 15.8153Z\"\r\n fill=\"black\" [attr.fill-opacity]=\"getDescOpacity(col.key)\"/>\r\n <!-- Up arrow \u2014 LEFT side, prominent when ASC -->\r\n <path d=\"M7.33316 16C7.11215 16 6.90019 15.933 6.74391 15.8136C6.58763 15.6942 6.49983 15.5324 6.49983 15.3636V4.16902L4.5915 5.63278C4.43458 5.75262 4.22175 5.81995 3.99983 5.81995C3.77791 5.81995 3.56508 5.75262 3.40816 5.63278C3.25124 5.51294 3.16309 5.3504 3.16309 5.18093C3.16309 5.01145 3.25124 4.84891 3.40816 4.72907L6.7415 2.18341C6.85868 2.0951 7.00749 2.03528 7.16915 2.01149C7.33081 1.98771 7.49807 2.00102 7.64983 2.04976C7.80201 2.0975 7.93228 2.17858 8.02423 2.28278C8.11617 2.38697 8.16567 2.50962 8.1665 2.63526V15.3636C8.1665 15.5324 8.0787 15.6942 7.92242 15.8136C7.76614 15.933 7.55418 16 7.33316 16Z\"\r\n fill=\"black\" [attr.fill-opacity]=\"getAscOpacity(col.key)\"/>\r\n </svg>\r\n </span>\r\n </th>\r\n <th *ngIf=\"config.actions && config.actions.length > 0\" class=\"st-actions-col\">\r\n {{ config.labels?.actionColumnHeader || 'Actions' }}\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let row of data; let rowIndex = index\">\r\n <td *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\r\n <input type=\"checkbox\" [(ngModel)]=\"row.selected\" (change)=\"onRowSelect(row)\">\r\n </td>\r\n <td *ngFor=\"let col of config.columns; let colIndex = index\"\r\n [class.sticky-col]=\"col.sticky\"\r\n [class.first-data-col]=\"colIndex === 0\"\r\n [ngStyle]=\"stickyColumnStyles[col.key]\"\r\n [class.clickable-cell]=\"col.clickAction\"\r\n (click)=\"onColumnClick(row, col)\">\r\n <!-- Text/Number/Date -->\r\n <span *ngIf=\"col.type !== 'custom' && col.type !== 'html' && col.type !== 'badge'\">\r\n {{ getCellValue(row, col) }}\r\n </span>\r\n <!-- HTML -->\r\n <div *ngIf=\"col.type === 'html'\" [innerHTML]=\"getCellValue(row, col)\"></div>\r\n <!-- Badge -->\r\n <span *ngIf=\"col.type === 'badge'\" class=\"st-badge\" [ngClass]=\"getBadgeClass(row, col)\">\r\n {{ getCellValue(row, col) }}\r\n </span>\r\n </td>\r\n\r\n <!-- Row Actions -->\r\n <td *ngIf=\"config.actions && config.actions.length > 0\" class=\"st-row-actions st-actions-col\">\r\n <div class=\"action-buttons\">\r\n <ng-container *ngFor=\"let action of config.actions; let i = index\">\r\n <ng-container *ngIf=\"action.type === 'dropdown'\">\r\n <div class=\"st-dropdown-container\" (click)=\"$event.stopPropagation()\">\r\n <button class=\"st-dropdown-btn\"\r\n [class.active]=\"openDropdownId === (rowIndex + '-' + i)\"\r\n (click)=\"toggleDropdown(rowIndex + '-' + i, $event, action.items, row)\">\r\n <span class=\"action-circle\">\r\n <i class=\"fa fa-ellipsis-h\"></i>\r\n </span>\r\n </button>\r\n </div>\r\n </ng-container>\r\n <ng-container *ngIf=\"action.type !== 'dropdown'\">\r\n <lib-button\r\n [variant]=\"action.btnVariant || 'secondary'\"\r\n [icon]=\"action.icon || ''\"\r\n (click)=\"onAction(action, row)\">\r\n {{ action.label }}\r\n </lib-button>\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n </td>\r\n </tr>\r\n <tr *ngIf=\"data.length === 0 && !loading\">\r\n <td [attr.colspan]=\"columnCount + (config.selectable ? 1 : 0) + (config.actions ? 1 : 0)\" class=\"no-data\">\r\n {{ config.labels?.noDataMessage || 'No data available' }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n <!-- Built-in Delete Confirmation Modal -->\r\n <cc-confirmation-modal\r\n [isOpen]=\"deleteModalOpen\"\r\n [config]=\"deleteModalConfig\"\r\n (confirm)=\"onDeleteConfirm()\"\r\n (cancel)=\"onDeleteCancel()\"\r\n (close)=\"onDeleteCancel()\">\r\n <p>{{ deleteModalMessage }}</p>\r\n </cc-confirmation-modal>\r\n </div>\r\n\r\n <!-- Pagination outside the table card -->\r\n <div class=\"st-pagination\" *ngIf=\"config.pagination && config.pagination.enabled && data.length > 0\">\r\n <lib-pagination\r\n [totalItems]=\"totalItems\"\r\n [itemsPerPage]=\"config.pagination.pageSize\"\r\n [currentPage]=\"currentPage\"\r\n [pageSizeOptions]=\"config.pagination.pageSizeOptions\"\r\n (pageChange)=\"onPageChange($event)\"\r\n (itemsPerPageChange)=\"onPageSizeChange($event)\">\r\n </lib-pagination>\r\n </div>\r\n</div>\r\n\r\n<!--\r\n Filter panel portal \u2014 position:fixed keeps it above all overflow/sticky containers.\r\n-->\r\n<div class=\"st-filter-panel\"\r\n *ngIf=\"openFilterKey !== null && activeFilterData\"\r\n [ngStyle]=\"{\r\n position: 'fixed',\r\n top: filterPosition.top + 'px',\r\n left: filterPosition.left + 'px',\r\n 'z-index': '99999'\r\n }\"\r\n (click)=\"$event.stopPropagation()\">\r\n <div class=\"st-filter-option\"\r\n [class.selected]=\"!isFilterActive(activeFilterData)\"\r\n (click)=\"selectFilterOption(activeFilterData, null)\">\r\n All {{ activeFilterData.label }}\r\n </div>\r\n <div class=\"st-filter-option\"\r\n *ngFor=\"let opt of getValidFilterOptions(activeFilterData)\"\r\n [class.selected]=\"activeFilters[activeFilterData.key] === opt.value\"\r\n (click)=\"selectFilterOption(activeFilterData, opt)\">\r\n {{ opt.label }}\r\n </div>\r\n</div>\r\n\r\n<!--\r\n Dropdown portal \u2014 rendered OUTSIDE every scroll/sticky container.\r\n position:fixed + z-index:99999 here is unconditionally above all sticky table cells.\r\n-->\r\n<div class=\"st-dropdown-menu st-dropdown-portal\"\r\n *ngIf=\"openDropdownId !== null && activeDropdownItems\"\r\n [ngStyle]=\"{\r\n position: 'fixed',\r\n top: dropdownPosition.top + 'px',\r\n right: dropdownPosition.right + 'px',\r\n left: 'auto',\r\n 'z-index': '99999'\r\n }\"\r\n (click)=\"$event.stopPropagation()\">\r\n <button class=\"st-dropdown-item\"\r\n *ngFor=\"let item of activeDropdownItems\"\r\n (click)=\"onActionItemClick(item, activeDropdownRow, $event); closeDropdown()\">\r\n <i *ngIf=\"item.icon\" [class]=\"item.icon + ' st-action-icon'\"></i>\r\n <span>{{ item.label }}</span>\r\n </button>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";.smart-table-outer{display:flex;flex-direction:column;gap:0}.smart-table-wrapper{font-family:var(--st-font-family, \"Roboto\", sans-serif);background:var(--st-table-bg, #fff);border-radius:var(--st-border-radius, 8px);box-shadow:var(--st-box-shadow, 0 2px 8px rgba(0, 0, 0, .08));display:flex;flex-direction:column;gap:0;padding:0;border:none}.st-toolbar{display:flex;justify-content:flex-start;align-items:center;flex-wrap:wrap;padding:var(--st-toolbar-padding, 1.2rem 0rem);background:var(--st-toolbar-bg, #fff);gap:var(--st-toolbar-gap, 1rem);border-bottom:var(--st-toolbar-border-bottom, 1px solid rgba(0, 0, 0, .08));box-shadow:#959da533 0 8px 24px}.st-toolbar .st-search{position:relative;display:flex;align-items:center;background:var(--st-search-bg, #f5f5f5);border-radius:var(--st-search-radius, 8px);padding:var(--st-search-wrapper-padding, 0 .875rem);height:var(--st-search-height, 36px);min-width:var(--st-search-min-width, 200px);width:var(--st-search-width, auto)}.st-toolbar .st-search input{padding:0 .5rem 0 1.5rem;border:none;background:transparent;font-size:var(--st-font-size, 14px);width:100%;color:var(--st-text-color, #333);outline:none;line-height:var(--st-search-height, 36px)}.st-toolbar .st-search input::placeholder{color:var(--st-search-placeholder-color, #999)}.st-toolbar .st-search i{position:absolute;left:.875rem;top:50%;transform:translateY(-50%);color:var(--st-search-icon-color, #888);font-size:13px;pointer-events:none}.st-toolbar .st-filters{display:flex;align-items:center;gap:.5rem}.st-toolbar .st-filters .st-filter-item{position:relative}.st-toolbar .st-filters .st-filter-trigger{display:flex;align-items:center;gap:6px;padding:var(--st-filter-trigger-padding, 0 .75rem);height:var(--st-filter-height, 36px);border:var(--st-filter-border, 1px solid #e0e0e0);border-radius:var(--st-filter-radius, 8px);cursor:pointer;font-size:var(--st-filter-font-size, 14px);color:var(--st-filter-color, #555);background:var(--st-filter-bg, #fff);transition:border-color .15s,background .15s;white-space:nowrap;-webkit-user-select:none;user-select:none}.st-toolbar .st-filters .st-filter-trigger:hover{border-color:var(--st-filter-hover-border-color, #bbb);background:var(--st-filter-hover-bg, #fafafa)}.st-toolbar .st-filters .st-filter-trigger.active{border-color:var(--st-filter-active-border-color, #999);background:var(--st-filter-hover-bg, #fafafa)}.st-toolbar .st-filters .st-filter-trigger.active .st-filter-chevron{transform:rotate(180deg)}.st-toolbar .st-filters .st-filter-trigger.has-value{color:var(--st-filter-selected-color, #222);font-weight:500;border-color:var(--st-filter-selected-border-color, #333)}.st-toolbar .st-filters .st-filter-trigger .st-filter-trigger-label{flex:1}.st-toolbar .st-filters .st-filter-trigger .st-filter-chevron{color:var(--st-filter-chevron-color, #999);flex-shrink:0;transition:transform .15s ease}.st-toolbar .st-actions{display:flex;gap:.5rem;margin-left:auto}.st-filter-panel{min-width:var(--st-filter-panel-min-width, 180px);background:var(--st-filter-panel-bg, #fff);border:var(--st-filter-panel-border, 1px solid #ebebeb);border-radius:var(--st-filter-panel-radius, 12px);box-shadow:var(--st-filter-panel-shadow, 0 8px 28px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06));padding:var(--st-filter-panel-padding, 6px)}.st-filter-option{display:flex;align-items:center;padding:var(--st-filter-option-padding, 8px 12px);margin-bottom:.3rem;border-radius:var(--st-filter-option-radius, 7px);font-size:var(--st-filter-font-size, 14px);color:var(--st-filter-color, #333);cursor:pointer;transition:background .1s;white-space:nowrap}.st-filter-option:hover{background:var(--st-filter-option-hover-bg, #f5f5f5)}.st-filter-option.selected{background:var(--st-filter-option-selected-bg, #f0f0f0);font-weight:500;color:var(--st-filter-option-selected-color, #111)}.st-table-container{overflow-x:auto;overflow-y:visible;padding:0}.st-table-container::-webkit-scrollbar{width:var(--st-scrollbar-width, 8px);height:var(--st-scrollbar-height, 8px)}.st-table-container::-webkit-scrollbar-track{background:var(--st-scrollbar-track-bg, #f1f1f1);border-radius:var(--st-scrollbar-track-radius, 4px)}.st-table-container::-webkit-scrollbar-thumb{background:var(--st-scrollbar-thumb-bg, #c1c1c1);border-radius:var(--st-scrollbar-thumb-radius, 4px)}.st-table-container::-webkit-scrollbar-thumb:hover{background:var(--st-scrollbar-thumb-hover-bg, #a8a8a8)}.st-table-container.has-sticky-header .st-table thead th{position:sticky;top:0;z-index:10;background:var(--st-header-bg, #f5f5f5);box-shadow:0 1px 2px -1px #0000001a}.st-table-container table{width:100%;border-collapse:separate;border-spacing:0;font-size:var(--st-font-size, 14px)}.st-table-container table thead{background:var(--st-header-bg, #f5f5f5)}.st-table-container table thead th{padding:.75rem 1rem;text-align:left;color:var(--st-header-color, #333);font-weight:var(--st-header-weight, 500);font-size:var(--st-header-size, 14px);text-transform:var(--st-header-transform, none);border-top:none;border-bottom:none;white-space:nowrap}.st-table-container table thead th:first-child{border-top-left-radius:var(--st-border-radius, 8px)}.st-table-container table thead th:last-child{border-top-right-radius:var(--st-border-radius, 8px)}.st-table-container table thead th.sortable{cursor:pointer}.st-table-container table thead th.sortable:hover{opacity:.8}.st-table-container table thead th.st-checkbox-col{width:40px;background:var(--st-header-bg, #f5f5f5);position:sticky;left:0;z-index:3}.st-table-container table thead th.sticky-col{position:sticky;z-index:3;background:var(--st-header-bg, #f5f5f5)}.st-table-container table thead th.st-actions-col{width:100px;min-width:100px;max-width:100px;background:var(--st-header-bg, #f5f5f5);box-shadow:var(--st-actions-col-shadow, -4px 0 8px rgba(0, 0, 0, .06));text-align:center;position:sticky;right:0;z-index:4}.st-table-container table tbody tr{background:var(--st-row-bg, #fff)}.st-table-container table tbody tr td{padding:var(--st-cell-padding, 1rem);color:var(--st-text-color, #333);vertical-align:middle;border-bottom:var(--st-row-border, 1px solid #eee);font-weight:400}.st-table-container table tbody tr td.st-checkbox-col{position:sticky;left:0;z-index:2}.st-table-container table tbody tr td.first-data-col{color:var(--st-text-color, #333);font-weight:500}.st-table-container table tbody tr td.sticky-col{position:sticky;z-index:2;background:var(--st-row-bg, #fff)}.st-table-container table tbody tr td.sticky-col:first-child{left:0}.st-table-container table tbody tr td.st-actions-col{width:100px;min-width:100px;max-width:100px;background:var(--st-frozen-col-bg, #f9f9f9);box-shadow:var(--st-actions-col-shadow, -4px 0 8px rgba(0, 0, 0, .06));text-align:center;position:sticky;right:0;z-index:2}.st-table-container table tbody tr:last-child td{border-bottom:none}.st-table-container table tbody tr:last-child td:first-child{border-bottom-left-radius:var(--st-border-radius, 8px)}.st-table-container table tbody tr:last-child td:last-child{border-bottom-right-radius:var(--st-border-radius, 8px)}.st-table-container table tbody tr:hover td{font-weight:500;background:var(--st-row-hover-bg, #f5f5f5)}.st-table-container table tbody tr:hover td.first-data-col{color:var(--st-first-col-color, #E84646)}.st-table-container table tbody tr.selected td,.st-table-container table tbody tr.selected td.sticky-col{background:var(--st-row-selected-bg, #f3e5f5)}.st-table-container table tbody tr .clickable-cell{cursor:pointer;transition:background .2s}.st-table-container table.is-selectable tbody td.first-data-col,.st-table-container table.is-selectable tbody td.first-data-col.sticky-col{background:var(--st-row-bg, #fff)}.st-table-container table.is-selectable tbody tr:hover td.first-data-col,.st-table-container table.is-selectable tbody tr:hover td.first-data-col.sticky-col{background:var(--st-row-hover-bg, #f5f5f5)}.st-table-container table.is-selectable tbody tr.selected td.first-data-col{background:var(--st-row-selected-bg, #f3e5f5)}.sort-icon-wrap{display:inline-flex;align-items:center;vertical-align:middle;flex-shrink:0;margin-left:4px;position:relative;top:-3px}.sort-icon-wrap svg{display:block}.st-selection-strip{display:flex;align-items:center;padding:var(--st-selection-strip-padding, .45rem 1rem);background:var(--st-selection-strip-bg, #fafafa);border-bottom:var(--st-selection-strip-border, 1px solid rgba(0, 0, 0, .08))}.st-selection-strip .st-selection-count{font-size:var(--st-selection-count-font-size, 13px);color:var(--st-selection-count-color, #444);font-weight:var(--st-selection-count-font-weight, 500)}input[type=checkbox]{accent-color:var(--st-checkbox-color, #e63e30);width:var(--st-checkbox-size, 16px);height:var(--st-checkbox-size, 16px);cursor:pointer}.st-badge{display:inline-block;padding:var(--st-badge-padding, 4px 12px);border-radius:var(--st-badge-radius, 12px);font-size:var(--st-badge-font-size, 12px);font-weight:var(--st-badge-font-weight, 500);text-align:center;white-space:nowrap}.st-badge.badge-success{background:var(--st-badge-success-bg, #e8f5e9);color:var(--st-badge-success-color, #2e7d32)}.st-badge.badge-warning{background:var(--st-badge-warning-bg, #fff3e0);color:var(--st-badge-warning-color, #ef6c00)}.st-badge.badge-danger{background:var(--st-badge-danger-bg, #ffebee);color:var(--st-badge-danger-color, #c62828)}.st-badge.badge-info{background:var(--st-badge-info-bg, #e3f2fd);color:var(--st-badge-info-color, #1565c0)}.st-badge.badge-neutral{background:var(--st-badge-neutral-bg, #f5f5f5);color:var(--st-badge-neutral-color, #616161)}.st-row-actions .action-buttons{display:flex;gap:.5rem;align-items:center;justify-content:center}.st-action-icon{margin-right:var(--st-action-icon-margin, 8px)}.st-dropdown-container{position:relative;display:inline-block}.st-dropdown-btn{background:transparent;border:none;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center}.st-dropdown-btn:hover .action-circle{background:#0000000f}.action-circle{display:inline-flex;align-items:center;justify-content:center;width:var(--st-action-circle-size, 32px);height:var(--st-action-circle-size, 32px);border-radius:50%;background:var(--st-table-bg, #fff);border:none;color:var(--st-text-color, #555);font-size:1rem;transition:background .15s}.st-dropdown-menu{min-width:var(--st-dropdown-min-width, 150px);background:var(--st-dropdown-bg, #fff);border:var(--st-dropdown-border, 1px solid #e0e0e0);border-radius:var(--st-dropdown-radius, 8px);box-shadow:var(--st-dropdown-shadow, 0 8px 24px rgba(0, 0, 0, .12));z-index:9999;display:flex;flex-direction:column;padding:var(--st-dropdown-padding, 4px);margin-top:4px}.st-dropdown-item{background:transparent;border:none;padding:var(--st-dropdown-item-padding, 8px 12px);border-radius:var(--st-dropdown-item-radius, 6px);text-align:left;cursor:pointer;display:flex;align-items:center;font-size:var(--st-font-size, 14px);color:var(--st-text-color, #333);transition:background .15s}.st-dropdown-item:hover{background:var(--st-dropdown-item-hover-bg, #f5f5f5)}.st-dropdown-item .st-action-icon{margin-right:8px;width:16px;text-align:center}.no-data{padding:2rem;color:var(--st-no-data-color, #888);text-align:center}.st-pagination{padding:var(--st-pagination-padding, .75rem 0)}@media(max-width:768px){.st-toolbar{flex-direction:column;align-items:stretch}.st-toolbar .st-search,.st-toolbar .st-filters,.st-toolbar .st-actions,.st-toolbar .st-search input{width:100%}.st-table-container{font-size:13px}}.st-table-container{position:relative}.st-table-container .st-table.loading-data{opacity:.5;pointer-events:none}.st-table-container .st-check-loader{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;z-index:10}.st-table-container .st-check-loader .spinner{width:40px;height:40px;border:4px solid var(--st-spinner-border-color, rgba(0, 0, 0, .1));border-left-color:var(--st-loader-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: PaginationComponent, selector: "lib-pagination", inputs: ["totalItems", "itemsPerPage", "currentPage", "pageSizeOptions", "theme", "labels"], outputs: ["pageChange", "itemsPerPageChange"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }, { kind: "component", type: ConfirmationModalComponent, selector: "cc-confirmation-modal", inputs: ["config", "isOpen", "confirmDisabled", "confirmLoading"], outputs: ["confirm", "cancel", "close", "showCodeSnippet"] }] });
|
|
10425
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SmartTableComponent, isStandalone: false, selector: "lib-smart-table", inputs: { config: "config", tableData: "tableData", totalItemsCount: "totalItemsCount", selectedRows: "selectedRows" }, outputs: { action: "action", topAction: "topAction", filterChange: "filterChange", rowSelect: "rowSelect", columnClick: "columnClick", sortChange: "sortChange", pageChange: "pageChange", searchChange: "searchChange" }, host: { listeners: { "document:click": "closeDropdown()" } }, viewQueries: [{ propertyName: "stickyHeaders", predicate: ["stickyHeader"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"smart-table-outer\">\n <!-- Table Card -->\n <div class=\"smart-table-wrapper\">\n <!-- Top Toolbar -->\n <div class=\"st-toolbar\" *ngIf=\"config.searchConfig?.enabled || (config.filters && config.filters.length > 0) || (config.topBarButtons && config.topBarButtons.length > 0)\">\n\n <!-- Search -->\n <div class=\"st-search\" *ngIf=\"config.searchConfig?.enabled\">\n <i class=\"fa fa-search\"></i>\n <input type=\"text\" [placeholder]=\"config.labels?.searchPlaceholder || 'Search'\" (input)=\"onSearch($event)\">\n </div>\n\n <!-- Filters -->\n <div class=\"st-filters\" *ngIf=\"config.filters\">\n <div class=\"st-filter-item\" *ngFor=\"let filter of config.filters\">\n <!-- Trigger -->\n <div class=\"st-filter-trigger\"\n [class.active]=\"openFilterKey === filter.key\"\n [class.has-value]=\"isFilterActive(filter)\"\n (click)=\"toggleFilter(filter.key, $event)\">\n <span class=\"st-filter-trigger-label\">{{ getFilterDisplay(filter) }}</span>\n <svg class=\"st-filter-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M2.5 4.5L6 8L9.5 4.5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n </div>\n </div>\n\n <!-- Top Bar Buttons -->\n <div class=\"st-actions\" *ngIf=\"config.topBarButtons\">\n <lib-button *ngFor=\"let btn of config.topBarButtons\"\n [variant]=\"btn.btnVariant || 'primary'\"\n [icon]=\"btn.icon || ''\"\n (click)=\"onTopAction(btn)\">\n {{ btn.label }}\n </lib-button>\n </div>\n </div>\n\n <!-- Selection count strip \u2014 visible whenever selectable is enabled -->\n <div class=\"st-selection-strip\" *ngIf=\"config.selectable\">\n <span class=\"st-selection-count\">{{ selectedRows.length }} of {{ totalItems > 0 ? totalItems : data.length }} selected</span>\n </div>\n\n <!-- Table Container -->\n <div class=\"st-table-container\">\n <div class=\"st-check-loader\" *ngIf=\"loading\">\n <div class=\"spinner\"></div>\n </div>\n <table class=\"st-table\" [class.loading-data]=\"loading\" [class.is-selectable]=\"config.selectable\">\n <thead>\n <tr>\n <th *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\n <input type=\"checkbox\" (change)=\"onSelectAll($event)\">\n </th>\n <th *ngFor=\"let col of config.columns; let colIndex = index\"\n #stickyHeader\n [class.sortable]=\"col.sortable\"\n [class.sticky-col]=\"col.sticky\"\n [class.first-data-col]=\"colIndex === 0\"\n [ngStyle]=\"stickyColumnStyles[col.key]\"\n (click)=\"onSort(col)\">\n {{ col.label }}\n <span *ngIf=\"col.sortable\" class=\"sort-icon-wrap\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\">\n <!-- Down arrow \u2014 RIGHT side, prominent when DESC -->\n <path d=\"M11.258 15.8153L14.5913 13.2699C14.7483 13.15 14.8364 12.9875 14.8364 12.818C14.8364 12.6486 14.7483 12.4861 14.5913 12.3662C14.4344 12.2464 14.2216 12.1791 13.9997 12.1791C13.7778 12.1791 13.5649 12.2464 13.408 12.3662L11.4997 13.8299V2.63636C11.4997 2.46758 11.4119 2.30572 11.2556 2.18638C11.0993 2.06704 10.8874 2 10.6663 2C10.4453 2 10.2334 2.06704 10.0771 2.18638C9.92081 2.30572 9.83301 2.46758 9.83301 2.63636V15.3635C9.83383 15.4891 9.88333 15.6117 9.97528 15.7159C10.0672 15.8201 10.1975 15.9012 10.3497 15.9489C10.4486 15.9845 10.557 16.002 10.6663 15.9998C10.776 16.0003 10.8847 15.9843 10.9863 15.9526C11.0878 15.9209 11.1801 15.8743 11.258 15.8153Z\"\n fill=\"black\" [attr.fill-opacity]=\"getDescOpacity(col.key)\"/>\n <!-- Up arrow \u2014 LEFT side, prominent when ASC -->\n <path d=\"M7.33316 16C7.11215 16 6.90019 15.933 6.74391 15.8136C6.58763 15.6942 6.49983 15.5324 6.49983 15.3636V4.16902L4.5915 5.63278C4.43458 5.75262 4.22175 5.81995 3.99983 5.81995C3.77791 5.81995 3.56508 5.75262 3.40816 5.63278C3.25124 5.51294 3.16309 5.3504 3.16309 5.18093C3.16309 5.01145 3.25124 4.84891 3.40816 4.72907L6.7415 2.18341C6.85868 2.0951 7.00749 2.03528 7.16915 2.01149C7.33081 1.98771 7.49807 2.00102 7.64983 2.04976C7.80201 2.0975 7.93228 2.17858 8.02423 2.28278C8.11617 2.38697 8.16567 2.50962 8.1665 2.63526V15.3636C8.1665 15.5324 8.0787 15.6942 7.92242 15.8136C7.76614 15.933 7.55418 16 7.33316 16Z\"\n fill=\"black\" [attr.fill-opacity]=\"getAscOpacity(col.key)\"/>\n </svg>\n </span>\n </th>\n <th *ngIf=\"config.actions && config.actions.length > 0\" class=\"st-actions-col\">\n {{ config.labels?.actionColumnHeader || 'Actions' }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let row of data; let rowIndex = index\">\n <td *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\n <input type=\"checkbox\" [(ngModel)]=\"row.selected\" (change)=\"onRowSelect(row)\">\n </td>\n <td *ngFor=\"let col of config.columns; let colIndex = index\"\n [class.sticky-col]=\"col.sticky\"\n [class.first-data-col]=\"colIndex === 0\"\n [ngStyle]=\"stickyColumnStyles[col.key]\"\n [class.clickable-cell]=\"col.clickAction\"\n (click)=\"onColumnClick(row, col)\">\n <!-- Text/Number/Date -->\n <span *ngIf=\"col.type !== 'custom' && col.type !== 'html' && col.type !== 'badge'\">\n {{ getCellValue(row, col) }}\n </span>\n <!-- HTML -->\n <div *ngIf=\"col.type === 'html'\" [innerHTML]=\"getCellValue(row, col)\"></div>\n <!-- Badge -->\n <span *ngIf=\"col.type === 'badge'\" class=\"st-badge\" [ngClass]=\"getBadgeClass(row, col)\">\n {{ getCellValue(row, col) }}\n </span>\n </td>\n\n <!-- Row Actions -->\n <td *ngIf=\"config.actions && config.actions.length > 0\" class=\"st-row-actions st-actions-col\">\n <div class=\"action-buttons\">\n <ng-container *ngFor=\"let action of config.actions; let i = index\">\n <ng-container *ngIf=\"action.type === 'dropdown'\">\n <div class=\"st-dropdown-container\" (click)=\"$event.stopPropagation()\">\n <button class=\"st-dropdown-btn\"\n [class.active]=\"openDropdownId === (rowIndex + '-' + i)\"\n (click)=\"toggleDropdown(rowIndex + '-' + i, $event, action.items, row)\">\n <span class=\"action-circle\">\n <i class=\"fa fa-ellipsis-h\"></i>\n </span>\n </button>\n </div>\n </ng-container>\n <ng-container *ngIf=\"action.type !== 'dropdown'\">\n <lib-button\n [variant]=\"action.btnVariant || 'secondary'\"\n [icon]=\"action.icon || ''\"\n (click)=\"onAction(action, row)\">\n {{ action.label }}\n </lib-button>\n </ng-container>\n </ng-container>\n </div>\n </td>\n </tr>\n <tr *ngIf=\"data.length === 0 && !loading\">\n <td [attr.colspan]=\"columnCount + (config.selectable ? 1 : 0) + (config.actions ? 1 : 0)\" class=\"no-data\">\n {{ config.labels?.noDataMessage || 'No data available' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- Pagination \u2014 inside the card, sticks to the bottom -->\n <div class=\"st-pagination\" *ngIf=\"showPagination\">\n <lib-pagination\n [totalItems]=\"totalItems\"\n [itemsPerPage]=\"config.pagination!.pageSize\"\n [currentPage]=\"currentPage\"\n [pageSizeOptions]=\"config.pagination!.pageSizeOptions\"\n (pageChange)=\"onPageChange($event)\"\n (itemsPerPageChange)=\"onPageSizeChange($event)\">\n </lib-pagination>\n </div>\n\n <!-- Built-in Delete Confirmation Modal -->\n <cc-confirmation-modal\n [isOpen]=\"deleteModalOpen\"\n [config]=\"deleteModalConfig\"\n (confirm)=\"onDeleteConfirm()\"\n (cancel)=\"onDeleteCancel()\"\n (close)=\"onDeleteCancel()\">\n <p>{{ deleteModalMessage }}</p>\n </cc-confirmation-modal>\n </div>\n</div>\n\n<!--\n Filter panel portal \u2014 position:fixed keeps it above all overflow/sticky containers.\n-->\n<div class=\"st-filter-panel\"\n *ngIf=\"openFilterKey !== null && activeFilterData\"\n [ngStyle]=\"{\n position: 'fixed',\n top: filterPosition.top + 'px',\n left: filterPosition.left + 'px',\n 'z-index': '99999'\n }\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"st-filter-option\"\n [class.selected]=\"!isFilterActive(activeFilterData)\"\n (click)=\"selectFilterOption(activeFilterData, null)\">\n All {{ activeFilterData.label }}\n </div>\n <div class=\"st-filter-option\"\n *ngFor=\"let opt of getValidFilterOptions(activeFilterData)\"\n [class.selected]=\"activeFilters[activeFilterData.key] === opt.value\"\n (click)=\"selectFilterOption(activeFilterData, opt)\">\n {{ opt.label }}\n </div>\n</div>\n\n<!--\n Dropdown portal \u2014 rendered OUTSIDE every scroll/sticky container.\n position:fixed + z-index:99999 here is unconditionally above all sticky table cells.\n-->\n<div class=\"st-dropdown-menu st-dropdown-portal\"\n *ngIf=\"openDropdownId !== null && activeDropdownItems\"\n [ngStyle]=\"{\n position: 'fixed',\n top: dropdownPosition.top + 'px',\n right: dropdownPosition.right + 'px',\n left: 'auto',\n 'z-index': '99999'\n }\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"st-dropdown-item\"\n *ngFor=\"let item of activeDropdownItems\"\n (click)=\"onActionItemClick(item, activeDropdownRow, $event); closeDropdown()\">\n <i *ngIf=\"item.icon\" [class]=\"item.icon + ' st-action-icon'\"></i>\n <span>{{ item.label }}</span>\n </button>\n</div>\n", styles: ["@charset \"UTF-8\";.smart-table-outer{display:flex;flex-direction:column;gap:0}.smart-table-wrapper{font-family:var(--st-font-family, \"Roboto\", sans-serif);background:var(--st-table-bg, #fff);border-radius:var(--st-border-radius, 8px);box-shadow:var(--st-box-shadow, 0 2px 8px rgba(0, 0, 0, .08));display:flex;flex-direction:column;gap:0;padding:0;border:none;margin-bottom:1.5rem}.st-toolbar{display:flex;justify-content:flex-start;align-items:center;flex-wrap:wrap;padding:var(--st-toolbar-padding, 1.2rem 0rem);background:var(--st-toolbar-bg, #fff);gap:var(--st-toolbar-gap, 1rem);border-bottom:var(--st-toolbar-border-bottom, 1px solid rgba(0, 0, 0, .08));box-shadow:#959da533 0 8px 24px}.st-toolbar .st-search{position:relative;display:flex;align-items:center;background:var(--st-search-bg, #f5f5f5);border-radius:var(--st-search-radius, 8px);padding:var(--st-search-wrapper-padding, 0 .875rem);height:var(--st-search-height, 36px);min-width:var(--st-search-min-width, 200px);width:var(--st-search-width, auto)}.st-toolbar .st-search input{padding:0 .5rem 0 1.5rem;border:none;background:transparent;font-size:var(--st-font-size, 14px);width:100%;color:var(--st-text-color, #333);outline:none;line-height:var(--st-search-height, 36px)}.st-toolbar .st-search input::placeholder{color:var(--st-search-placeholder-color, #999)}.st-toolbar .st-search i{position:absolute;left:.875rem;top:50%;transform:translateY(-50%);color:var(--st-search-icon-color, #888);font-size:13px;pointer-events:none}.st-toolbar .st-filters{display:flex;align-items:center;gap:.5rem}.st-toolbar .st-filters .st-filter-item{position:relative}.st-toolbar .st-filters .st-filter-trigger{display:flex;align-items:center;gap:6px;padding:var(--st-filter-trigger-padding, 0 .75rem);height:var(--st-filter-height, 36px);border:var(--st-filter-border, 1px solid #e0e0e0);border-radius:var(--st-filter-radius, 8px);cursor:pointer;font-size:var(--st-filter-font-size, 14px);color:var(--st-filter-color, #555);background:var(--st-filter-bg, #fff);transition:border-color .15s,background .15s;white-space:nowrap;-webkit-user-select:none;user-select:none}.st-toolbar .st-filters .st-filter-trigger:hover{border-color:var(--st-filter-hover-border-color, #bbb);background:var(--st-filter-hover-bg, #fafafa)}.st-toolbar .st-filters .st-filter-trigger.active{border-color:var(--st-filter-active-border-color, #999);background:var(--st-filter-hover-bg, #fafafa)}.st-toolbar .st-filters .st-filter-trigger.active .st-filter-chevron{transform:rotate(180deg)}.st-toolbar .st-filters .st-filter-trigger.has-value{color:var(--st-filter-selected-color, #222);font-weight:500;border-color:var(--st-filter-selected-border-color, #333)}.st-toolbar .st-filters .st-filter-trigger .st-filter-trigger-label{flex:1}.st-toolbar .st-filters .st-filter-trigger .st-filter-chevron{color:var(--st-filter-chevron-color, #999);flex-shrink:0;transition:transform .15s ease}.st-toolbar .st-actions{display:flex;gap:.5rem;margin-left:auto}.st-filter-panel{min-width:var(--st-filter-panel-min-width, 180px);background:var(--st-filter-panel-bg, #fff);border:var(--st-filter-panel-border, 1px solid #ebebeb);border-radius:var(--st-filter-panel-radius, 12px);box-shadow:var(--st-filter-panel-shadow, 0 8px 28px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06));padding:var(--st-filter-panel-padding, 6px)}.st-filter-option{display:flex;align-items:center;padding:var(--st-filter-option-padding, 8px 12px);margin-bottom:.3rem;border-radius:var(--st-filter-option-radius, 7px);font-size:var(--st-filter-font-size, 14px);color:var(--st-filter-color, #333);cursor:pointer;transition:background .1s;white-space:nowrap}.st-filter-option:hover{background:var(--st-filter-option-hover-bg, #f5f5f5)}.st-filter-option.selected{background:var(--st-filter-option-selected-bg, #f0f0f0);font-weight:500;color:var(--st-filter-option-selected-color, #111)}.st-table-container{overflow-x:auto;overflow-y:visible;padding:0}.st-table-container::-webkit-scrollbar{width:var(--st-scrollbar-width, 8px);height:var(--st-scrollbar-height, 8px)}.st-table-container::-webkit-scrollbar-track{background:var(--st-scrollbar-track-bg, #f1f1f1);border-radius:var(--st-scrollbar-track-radius, 4px)}.st-table-container::-webkit-scrollbar-thumb{background:var(--st-scrollbar-thumb-bg, #c1c1c1);border-radius:var(--st-scrollbar-thumb-radius, 4px)}.st-table-container::-webkit-scrollbar-thumb:hover{background:var(--st-scrollbar-thumb-hover-bg, #a8a8a8)}.st-table-container.has-sticky-header .st-table thead th{position:sticky;top:0;z-index:10;background:var(--st-header-bg, #f5f5f5);box-shadow:0 1px 2px -1px #0000001a}.st-table-container table{width:100%;border-collapse:separate;border-spacing:0;font-size:var(--st-font-size, 14px)}.st-table-container table thead{background:var(--st-header-bg, #f5f5f5)}.st-table-container table thead th{padding:.4rem 1rem;text-align:left;color:var(--st-header-color, #333);font-weight:var(--st-header-weight, 500);font-size:var(--st-header-size, 14px);text-transform:var(--st-header-transform, none);border-top:none;border-bottom:none;white-space:nowrap}.st-table-container table thead th:first-child{border-top-left-radius:var(--st-border-radius, 8px)}.st-table-container table thead th:last-child{border-top-right-radius:var(--st-border-radius, 8px)}.st-table-container table thead th.sortable{cursor:pointer}.st-table-container table thead th.sortable:hover{opacity:.8}.st-table-container table thead th.st-checkbox-col{width:40px;background:var(--st-header-bg, #f5f5f5);position:sticky;left:0;z-index:3}.st-table-container table thead th.sticky-col{position:sticky;z-index:3;background:var(--st-header-bg, #f5f5f5)}.st-table-container table thead th.st-actions-col{width:100px;min-width:100px;max-width:100px;background:var(--st-header-bg, #f5f5f5);box-shadow:var(--st-actions-col-shadow, -4px 0 8px rgba(0, 0, 0, .06));text-align:center;position:sticky;right:0;z-index:4}.st-table-container table tbody tr{background:var(--st-row-bg, #fff)}.st-table-container table tbody tr td{padding:.5rem 1rem;color:var(--st-text-color, #333);vertical-align:middle;border-bottom:var(--st-row-border, 1px solid #eee);font-weight:400}.st-table-container table tbody tr td.st-checkbox-col{position:sticky;left:0;z-index:2}.st-table-container table tbody tr td.first-data-col{color:var(--st-text-color, #333);font-weight:500}.st-table-container table tbody tr td.sticky-col{position:sticky;z-index:2;background:var(--st-row-bg, #fff)}.st-table-container table tbody tr td.sticky-col:first-child{left:0}.st-table-container table tbody tr td.st-actions-col{width:100px;min-width:100px;max-width:100px;background:var(--st-frozen-col-bg, #f9f9f9);box-shadow:var(--st-actions-col-shadow, -4px 0 8px rgba(0, 0, 0, .06));text-align:center;position:sticky;right:0;z-index:2}.st-table-container table tbody tr:last-child td{border-bottom:none}.st-table-container table tbody tr:last-child td:first-child{border-bottom-left-radius:var(--st-border-radius, 8px)}.st-table-container table tbody tr:last-child td:last-child{border-bottom-right-radius:var(--st-border-radius, 8px)}.st-table-container table tbody tr:hover td{font-weight:500;background:var(--st-row-hover-bg, #f5f5f5)}.st-table-container table tbody tr:hover td.first-data-col{color:var(--st-first-col-color, #E84646)}.st-table-container table tbody tr.selected td,.st-table-container table tbody tr.selected td.sticky-col{background:var(--st-row-selected-bg, #f3e5f5)}.st-table-container table tbody tr .clickable-cell{cursor:pointer;transition:background .2s}.st-table-container table.is-selectable tbody td.first-data-col,.st-table-container table.is-selectable tbody td.first-data-col.sticky-col{background:var(--st-row-bg, #fff)}.st-table-container table.is-selectable tbody tr:hover td.first-data-col,.st-table-container table.is-selectable tbody tr:hover td.first-data-col.sticky-col{background:var(--st-row-hover-bg, #f5f5f5)}.st-table-container table.is-selectable tbody tr.selected td.first-data-col{background:var(--st-row-selected-bg, #f3e5f5)}.sort-icon-wrap{display:inline-flex;align-items:center;vertical-align:middle;flex-shrink:0;margin-left:4px;position:relative;top:-3px}.sort-icon-wrap svg{display:block}.st-selection-strip{display:flex;align-items:center;padding:var(--st-selection-strip-padding, .45rem 1rem);background:var(--st-selection-strip-bg, #fafafa);border-bottom:var(--st-selection-strip-border, 1px solid rgba(0, 0, 0, .08))}.st-selection-strip .st-selection-count{font-size:var(--st-selection-count-font-size, 13px);color:var(--st-selection-count-color, #444);font-weight:var(--st-selection-count-font-weight, 500)}input[type=checkbox]{accent-color:var(--st-checkbox-color, #e63e30);width:var(--st-checkbox-size, 16px);height:var(--st-checkbox-size, 16px);cursor:pointer}.st-badge{display:inline-block;padding:var(--st-badge-padding, 4px 12px);border-radius:var(--st-badge-radius, 12px);font-size:var(--st-badge-font-size, 12px);font-weight:var(--st-badge-font-weight, 500);text-align:center;white-space:nowrap}.st-badge.badge-success{background:var(--st-badge-success-bg, #e8f5e9);color:var(--st-badge-success-color, #2e7d32)}.st-badge.badge-warning{background:var(--st-badge-warning-bg, #fff3e0);color:var(--st-badge-warning-color, #ef6c00)}.st-badge.badge-danger{background:var(--st-badge-danger-bg, #ffebee);color:var(--st-badge-danger-color, #c62828)}.st-badge.badge-info{background:var(--st-badge-info-bg, #e3f2fd);color:var(--st-badge-info-color, #1565c0)}.st-badge.badge-neutral{background:var(--st-badge-neutral-bg, #f5f5f5);color:var(--st-badge-neutral-color, #616161)}.st-row-actions .action-buttons{display:flex;gap:.5rem;align-items:center;justify-content:center}.st-action-icon{margin-right:var(--st-action-icon-margin, 8px)}.st-dropdown-container{position:relative;display:inline-block}.st-dropdown-btn{background:transparent;border:none;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center}.st-dropdown-btn:hover .action-circle{background:#0000000f}.action-circle{display:inline-flex;align-items:center;justify-content:center;width:var(--st-action-circle-size, 32px);height:var(--st-action-circle-size, 32px);border-radius:50%;background:var(--st-table-bg, #fff);border:none;color:var(--st-text-color, #555);font-size:1rem;transition:background .15s}.st-dropdown-menu{min-width:var(--st-dropdown-min-width, 150px);background:var(--st-dropdown-bg, #fff);border:var(--st-dropdown-border, 1px solid #e0e0e0);border-radius:var(--st-dropdown-radius, 8px);box-shadow:var(--st-dropdown-shadow, 0 8px 24px rgba(0, 0, 0, .12));z-index:9999;display:flex;flex-direction:column;padding:var(--st-dropdown-padding, 4px);margin-top:4px}.st-dropdown-item{background:transparent;border:none;padding:var(--st-dropdown-item-padding, 8px 12px);border-radius:var(--st-dropdown-item-radius, 6px);text-align:left;cursor:pointer;display:flex;align-items:center;font-size:var(--st-font-size, 14px);color:var(--st-text-color, #333);transition:background .15s}.st-dropdown-item:hover{background:var(--st-dropdown-item-hover-bg, #f5f5f5)}.st-dropdown-item .st-action-icon{margin-right:8px;width:16px;text-align:center}.no-data{padding:2rem;color:var(--st-no-data-color, #888);text-align:center}.st-pagination{padding:var(--st-pagination-padding, .6rem 1rem);border-top:1px solid rgba(0,0,0,.07);background:var(--st-table-bg, #fff);border-bottom-left-radius:var(--st-border-radius, 8px);border-bottom-right-radius:var(--st-border-radius, 8px)}@media(max-width:768px){.st-toolbar{flex-direction:column;align-items:stretch}.st-toolbar .st-search,.st-toolbar .st-filters,.st-toolbar .st-actions,.st-toolbar .st-search input{width:100%}.st-table-container{font-size:13px}}.st-table-container{position:relative}.st-table-container .st-table.loading-data{opacity:.5;pointer-events:none}.st-table-container .st-check-loader{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;z-index:10}.st-table-container .st-check-loader .spinner{width:40px;height:40px;border:4px solid var(--st-spinner-border-color, rgba(0, 0, 0, .1));border-left-color:var(--st-loader-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: PaginationComponent, selector: "lib-pagination", inputs: ["totalItems", "itemsPerPage", "currentPage", "pageSizeOptions", "theme", "labels"], outputs: ["pageChange", "itemsPerPageChange"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }, { kind: "component", type: ConfirmationModalComponent, selector: "cc-confirmation-modal", inputs: ["config", "isOpen", "confirmDisabled", "confirmLoading"], outputs: ["confirm", "cancel", "close", "showCodeSnippet"] }] });
|
|
10345
10426
|
}
|
|
10346
10427
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartTableComponent, decorators: [{
|
|
10347
10428
|
type: Component,
|
|
10348
|
-
args: [{ selector: 'lib-smart-table', standalone: false, template: "<div class=\"smart-table-outer\">\r\n <!-- Table Card -->\r\n <div class=\"smart-table-wrapper\">\r\n <!-- Top Toolbar -->\r\n <div class=\"st-toolbar\" *ngIf=\"config.searchConfig?.enabled || (config.filters && config.filters.length > 0) || (config.topBarButtons && config.topBarButtons.length > 0)\">\r\n\r\n <!-- Search -->\r\n <div class=\"st-search\" *ngIf=\"config.searchConfig?.enabled\">\r\n <i class=\"fa fa-search\"></i>\r\n <input type=\"text\" [placeholder]=\"config.labels?.searchPlaceholder || 'Search'\" (input)=\"onSearch($event)\">\r\n </div>\r\n\r\n <!-- Filters -->\r\n <div class=\"st-filters\" *ngIf=\"config.filters\">\r\n <div class=\"st-filter-item\" *ngFor=\"let filter of config.filters\">\r\n <!-- Trigger -->\r\n <div class=\"st-filter-trigger\"\r\n [class.active]=\"openFilterKey === filter.key\"\r\n [class.has-value]=\"isFilterActive(filter)\"\r\n (click)=\"toggleFilter(filter.key, $event)\">\r\n <span class=\"st-filter-trigger-label\">{{ getFilterDisplay(filter) }}</span>\r\n <svg class=\"st-filter-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\r\n <path d=\"M2.5 4.5L6 8L9.5 4.5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n </svg>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Top Bar Buttons -->\r\n <div class=\"st-actions\" *ngIf=\"config.topBarButtons\">\r\n <lib-button *ngFor=\"let btn of config.topBarButtons\"\r\n [variant]=\"btn.btnVariant || 'primary'\"\r\n [icon]=\"btn.icon || ''\"\r\n (click)=\"onTopAction(btn)\">\r\n {{ btn.label }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n\r\n <!-- Selection count strip \u2014 visible whenever selectable is enabled -->\r\n <div class=\"st-selection-strip\" *ngIf=\"config.selectable\">\r\n <span class=\"st-selection-count\">{{ selectedRows.length }} of {{ totalItems > 0 ? totalItems : data.length }} selected</span>\r\n </div>\r\n\r\n <!-- Table Container -->\r\n <div class=\"st-table-container\">\r\n <div class=\"st-check-loader\" *ngIf=\"loading\">\r\n <div class=\"spinner\"></div>\r\n </div>\r\n <table class=\"st-table\" [class.loading-data]=\"loading\" [class.is-selectable]=\"config.selectable\">\r\n <thead>\r\n <tr>\r\n <th *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\r\n <input type=\"checkbox\" (change)=\"onSelectAll($event)\">\r\n </th>\r\n <th *ngFor=\"let col of config.columns; let colIndex = index\"\r\n #stickyHeader\r\n [class.sortable]=\"col.sortable\"\r\n [class.sticky-col]=\"col.sticky\"\r\n [class.first-data-col]=\"colIndex === 0\"\r\n [ngStyle]=\"stickyColumnStyles[col.key]\"\r\n (click)=\"onSort(col)\">\r\n {{ col.label }}\r\n <span *ngIf=\"col.sortable\" class=\"sort-icon-wrap\">\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\">\r\n <!-- Down arrow \u2014 RIGHT side, prominent when DESC -->\r\n <path d=\"M11.258 15.8153L14.5913 13.2699C14.7483 13.15 14.8364 12.9875 14.8364 12.818C14.8364 12.6486 14.7483 12.4861 14.5913 12.3662C14.4344 12.2464 14.2216 12.1791 13.9997 12.1791C13.7778 12.1791 13.5649 12.2464 13.408 12.3662L11.4997 13.8299V2.63636C11.4997 2.46758 11.4119 2.30572 11.2556 2.18638C11.0993 2.06704 10.8874 2 10.6663 2C10.4453 2 10.2334 2.06704 10.0771 2.18638C9.92081 2.30572 9.83301 2.46758 9.83301 2.63636V15.3635C9.83383 15.4891 9.88333 15.6117 9.97528 15.7159C10.0672 15.8201 10.1975 15.9012 10.3497 15.9489C10.4486 15.9845 10.557 16.002 10.6663 15.9998C10.776 16.0003 10.8847 15.9843 10.9863 15.9526C11.0878 15.9209 11.1801 15.8743 11.258 15.8153Z\"\r\n fill=\"black\" [attr.fill-opacity]=\"getDescOpacity(col.key)\"/>\r\n <!-- Up arrow \u2014 LEFT side, prominent when ASC -->\r\n <path d=\"M7.33316 16C7.11215 16 6.90019 15.933 6.74391 15.8136C6.58763 15.6942 6.49983 15.5324 6.49983 15.3636V4.16902L4.5915 5.63278C4.43458 5.75262 4.22175 5.81995 3.99983 5.81995C3.77791 5.81995 3.56508 5.75262 3.40816 5.63278C3.25124 5.51294 3.16309 5.3504 3.16309 5.18093C3.16309 5.01145 3.25124 4.84891 3.40816 4.72907L6.7415 2.18341C6.85868 2.0951 7.00749 2.03528 7.16915 2.01149C7.33081 1.98771 7.49807 2.00102 7.64983 2.04976C7.80201 2.0975 7.93228 2.17858 8.02423 2.28278C8.11617 2.38697 8.16567 2.50962 8.1665 2.63526V15.3636C8.1665 15.5324 8.0787 15.6942 7.92242 15.8136C7.76614 15.933 7.55418 16 7.33316 16Z\"\r\n fill=\"black\" [attr.fill-opacity]=\"getAscOpacity(col.key)\"/>\r\n </svg>\r\n </span>\r\n </th>\r\n <th *ngIf=\"config.actions && config.actions.length > 0\" class=\"st-actions-col\">\r\n {{ config.labels?.actionColumnHeader || 'Actions' }}\r\n </th>\r\n </tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let row of data; let rowIndex = index\">\r\n <td *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\r\n <input type=\"checkbox\" [(ngModel)]=\"row.selected\" (change)=\"onRowSelect(row)\">\r\n </td>\r\n <td *ngFor=\"let col of config.columns; let colIndex = index\"\r\n [class.sticky-col]=\"col.sticky\"\r\n [class.first-data-col]=\"colIndex === 0\"\r\n [ngStyle]=\"stickyColumnStyles[col.key]\"\r\n [class.clickable-cell]=\"col.clickAction\"\r\n (click)=\"onColumnClick(row, col)\">\r\n <!-- Text/Number/Date -->\r\n <span *ngIf=\"col.type !== 'custom' && col.type !== 'html' && col.type !== 'badge'\">\r\n {{ getCellValue(row, col) }}\r\n </span>\r\n <!-- HTML -->\r\n <div *ngIf=\"col.type === 'html'\" [innerHTML]=\"getCellValue(row, col)\"></div>\r\n <!-- Badge -->\r\n <span *ngIf=\"col.type === 'badge'\" class=\"st-badge\" [ngClass]=\"getBadgeClass(row, col)\">\r\n {{ getCellValue(row, col) }}\r\n </span>\r\n </td>\r\n\r\n <!-- Row Actions -->\r\n <td *ngIf=\"config.actions && config.actions.length > 0\" class=\"st-row-actions st-actions-col\">\r\n <div class=\"action-buttons\">\r\n <ng-container *ngFor=\"let action of config.actions; let i = index\">\r\n <ng-container *ngIf=\"action.type === 'dropdown'\">\r\n <div class=\"st-dropdown-container\" (click)=\"$event.stopPropagation()\">\r\n <button class=\"st-dropdown-btn\"\r\n [class.active]=\"openDropdownId === (rowIndex + '-' + i)\"\r\n (click)=\"toggleDropdown(rowIndex + '-' + i, $event, action.items, row)\">\r\n <span class=\"action-circle\">\r\n <i class=\"fa fa-ellipsis-h\"></i>\r\n </span>\r\n </button>\r\n </div>\r\n </ng-container>\r\n <ng-container *ngIf=\"action.type !== 'dropdown'\">\r\n <lib-button\r\n [variant]=\"action.btnVariant || 'secondary'\"\r\n [icon]=\"action.icon || ''\"\r\n (click)=\"onAction(action, row)\">\r\n {{ action.label }}\r\n </lib-button>\r\n </ng-container>\r\n </ng-container>\r\n </div>\r\n </td>\r\n </tr>\r\n <tr *ngIf=\"data.length === 0 && !loading\">\r\n <td [attr.colspan]=\"columnCount + (config.selectable ? 1 : 0) + (config.actions ? 1 : 0)\" class=\"no-data\">\r\n {{ config.labels?.noDataMessage || 'No data available' }}\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n </div>\r\n\r\n <!-- Built-in Delete Confirmation Modal -->\r\n <cc-confirmation-modal\r\n [isOpen]=\"deleteModalOpen\"\r\n [config]=\"deleteModalConfig\"\r\n (confirm)=\"onDeleteConfirm()\"\r\n (cancel)=\"onDeleteCancel()\"\r\n (close)=\"onDeleteCancel()\">\r\n <p>{{ deleteModalMessage }}</p>\r\n </cc-confirmation-modal>\r\n </div>\r\n\r\n <!-- Pagination outside the table card -->\r\n <div class=\"st-pagination\" *ngIf=\"config.pagination && config.pagination.enabled && data.length > 0\">\r\n <lib-pagination\r\n [totalItems]=\"totalItems\"\r\n [itemsPerPage]=\"config.pagination.pageSize\"\r\n [currentPage]=\"currentPage\"\r\n [pageSizeOptions]=\"config.pagination.pageSizeOptions\"\r\n (pageChange)=\"onPageChange($event)\"\r\n (itemsPerPageChange)=\"onPageSizeChange($event)\">\r\n </lib-pagination>\r\n </div>\r\n</div>\r\n\r\n<!--\r\n Filter panel portal \u2014 position:fixed keeps it above all overflow/sticky containers.\r\n-->\r\n<div class=\"st-filter-panel\"\r\n *ngIf=\"openFilterKey !== null && activeFilterData\"\r\n [ngStyle]=\"{\r\n position: 'fixed',\r\n top: filterPosition.top + 'px',\r\n left: filterPosition.left + 'px',\r\n 'z-index': '99999'\r\n }\"\r\n (click)=\"$event.stopPropagation()\">\r\n <div class=\"st-filter-option\"\r\n [class.selected]=\"!isFilterActive(activeFilterData)\"\r\n (click)=\"selectFilterOption(activeFilterData, null)\">\r\n All {{ activeFilterData.label }}\r\n </div>\r\n <div class=\"st-filter-option\"\r\n *ngFor=\"let opt of getValidFilterOptions(activeFilterData)\"\r\n [class.selected]=\"activeFilters[activeFilterData.key] === opt.value\"\r\n (click)=\"selectFilterOption(activeFilterData, opt)\">\r\n {{ opt.label }}\r\n </div>\r\n</div>\r\n\r\n<!--\r\n Dropdown portal \u2014 rendered OUTSIDE every scroll/sticky container.\r\n position:fixed + z-index:99999 here is unconditionally above all sticky table cells.\r\n-->\r\n<div class=\"st-dropdown-menu st-dropdown-portal\"\r\n *ngIf=\"openDropdownId !== null && activeDropdownItems\"\r\n [ngStyle]=\"{\r\n position: 'fixed',\r\n top: dropdownPosition.top + 'px',\r\n right: dropdownPosition.right + 'px',\r\n left: 'auto',\r\n 'z-index': '99999'\r\n }\"\r\n (click)=\"$event.stopPropagation()\">\r\n <button class=\"st-dropdown-item\"\r\n *ngFor=\"let item of activeDropdownItems\"\r\n (click)=\"onActionItemClick(item, activeDropdownRow, $event); closeDropdown()\">\r\n <i *ngIf=\"item.icon\" [class]=\"item.icon + ' st-action-icon'\"></i>\r\n <span>{{ item.label }}</span>\r\n </button>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";.smart-table-outer{display:flex;flex-direction:column;gap:0}.smart-table-wrapper{font-family:var(--st-font-family, \"Roboto\", sans-serif);background:var(--st-table-bg, #fff);border-radius:var(--st-border-radius, 8px);box-shadow:var(--st-box-shadow, 0 2px 8px rgba(0, 0, 0, .08));display:flex;flex-direction:column;gap:0;padding:0;border:none}.st-toolbar{display:flex;justify-content:flex-start;align-items:center;flex-wrap:wrap;padding:var(--st-toolbar-padding, 1.2rem 0rem);background:var(--st-toolbar-bg, #fff);gap:var(--st-toolbar-gap, 1rem);border-bottom:var(--st-toolbar-border-bottom, 1px solid rgba(0, 0, 0, .08));box-shadow:#959da533 0 8px 24px}.st-toolbar .st-search{position:relative;display:flex;align-items:center;background:var(--st-search-bg, #f5f5f5);border-radius:var(--st-search-radius, 8px);padding:var(--st-search-wrapper-padding, 0 .875rem);height:var(--st-search-height, 36px);min-width:var(--st-search-min-width, 200px);width:var(--st-search-width, auto)}.st-toolbar .st-search input{padding:0 .5rem 0 1.5rem;border:none;background:transparent;font-size:var(--st-font-size, 14px);width:100%;color:var(--st-text-color, #333);outline:none;line-height:var(--st-search-height, 36px)}.st-toolbar .st-search input::placeholder{color:var(--st-search-placeholder-color, #999)}.st-toolbar .st-search i{position:absolute;left:.875rem;top:50%;transform:translateY(-50%);color:var(--st-search-icon-color, #888);font-size:13px;pointer-events:none}.st-toolbar .st-filters{display:flex;align-items:center;gap:.5rem}.st-toolbar .st-filters .st-filter-item{position:relative}.st-toolbar .st-filters .st-filter-trigger{display:flex;align-items:center;gap:6px;padding:var(--st-filter-trigger-padding, 0 .75rem);height:var(--st-filter-height, 36px);border:var(--st-filter-border, 1px solid #e0e0e0);border-radius:var(--st-filter-radius, 8px);cursor:pointer;font-size:var(--st-filter-font-size, 14px);color:var(--st-filter-color, #555);background:var(--st-filter-bg, #fff);transition:border-color .15s,background .15s;white-space:nowrap;-webkit-user-select:none;user-select:none}.st-toolbar .st-filters .st-filter-trigger:hover{border-color:var(--st-filter-hover-border-color, #bbb);background:var(--st-filter-hover-bg, #fafafa)}.st-toolbar .st-filters .st-filter-trigger.active{border-color:var(--st-filter-active-border-color, #999);background:var(--st-filter-hover-bg, #fafafa)}.st-toolbar .st-filters .st-filter-trigger.active .st-filter-chevron{transform:rotate(180deg)}.st-toolbar .st-filters .st-filter-trigger.has-value{color:var(--st-filter-selected-color, #222);font-weight:500;border-color:var(--st-filter-selected-border-color, #333)}.st-toolbar .st-filters .st-filter-trigger .st-filter-trigger-label{flex:1}.st-toolbar .st-filters .st-filter-trigger .st-filter-chevron{color:var(--st-filter-chevron-color, #999);flex-shrink:0;transition:transform .15s ease}.st-toolbar .st-actions{display:flex;gap:.5rem;margin-left:auto}.st-filter-panel{min-width:var(--st-filter-panel-min-width, 180px);background:var(--st-filter-panel-bg, #fff);border:var(--st-filter-panel-border, 1px solid #ebebeb);border-radius:var(--st-filter-panel-radius, 12px);box-shadow:var(--st-filter-panel-shadow, 0 8px 28px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06));padding:var(--st-filter-panel-padding, 6px)}.st-filter-option{display:flex;align-items:center;padding:var(--st-filter-option-padding, 8px 12px);margin-bottom:.3rem;border-radius:var(--st-filter-option-radius, 7px);font-size:var(--st-filter-font-size, 14px);color:var(--st-filter-color, #333);cursor:pointer;transition:background .1s;white-space:nowrap}.st-filter-option:hover{background:var(--st-filter-option-hover-bg, #f5f5f5)}.st-filter-option.selected{background:var(--st-filter-option-selected-bg, #f0f0f0);font-weight:500;color:var(--st-filter-option-selected-color, #111)}.st-table-container{overflow-x:auto;overflow-y:visible;padding:0}.st-table-container::-webkit-scrollbar{width:var(--st-scrollbar-width, 8px);height:var(--st-scrollbar-height, 8px)}.st-table-container::-webkit-scrollbar-track{background:var(--st-scrollbar-track-bg, #f1f1f1);border-radius:var(--st-scrollbar-track-radius, 4px)}.st-table-container::-webkit-scrollbar-thumb{background:var(--st-scrollbar-thumb-bg, #c1c1c1);border-radius:var(--st-scrollbar-thumb-radius, 4px)}.st-table-container::-webkit-scrollbar-thumb:hover{background:var(--st-scrollbar-thumb-hover-bg, #a8a8a8)}.st-table-container.has-sticky-header .st-table thead th{position:sticky;top:0;z-index:10;background:var(--st-header-bg, #f5f5f5);box-shadow:0 1px 2px -1px #0000001a}.st-table-container table{width:100%;border-collapse:separate;border-spacing:0;font-size:var(--st-font-size, 14px)}.st-table-container table thead{background:var(--st-header-bg, #f5f5f5)}.st-table-container table thead th{padding:.75rem 1rem;text-align:left;color:var(--st-header-color, #333);font-weight:var(--st-header-weight, 500);font-size:var(--st-header-size, 14px);text-transform:var(--st-header-transform, none);border-top:none;border-bottom:none;white-space:nowrap}.st-table-container table thead th:first-child{border-top-left-radius:var(--st-border-radius, 8px)}.st-table-container table thead th:last-child{border-top-right-radius:var(--st-border-radius, 8px)}.st-table-container table thead th.sortable{cursor:pointer}.st-table-container table thead th.sortable:hover{opacity:.8}.st-table-container table thead th.st-checkbox-col{width:40px;background:var(--st-header-bg, #f5f5f5);position:sticky;left:0;z-index:3}.st-table-container table thead th.sticky-col{position:sticky;z-index:3;background:var(--st-header-bg, #f5f5f5)}.st-table-container table thead th.st-actions-col{width:100px;min-width:100px;max-width:100px;background:var(--st-header-bg, #f5f5f5);box-shadow:var(--st-actions-col-shadow, -4px 0 8px rgba(0, 0, 0, .06));text-align:center;position:sticky;right:0;z-index:4}.st-table-container table tbody tr{background:var(--st-row-bg, #fff)}.st-table-container table tbody tr td{padding:var(--st-cell-padding, 1rem);color:var(--st-text-color, #333);vertical-align:middle;border-bottom:var(--st-row-border, 1px solid #eee);font-weight:400}.st-table-container table tbody tr td.st-checkbox-col{position:sticky;left:0;z-index:2}.st-table-container table tbody tr td.first-data-col{color:var(--st-text-color, #333);font-weight:500}.st-table-container table tbody tr td.sticky-col{position:sticky;z-index:2;background:var(--st-row-bg, #fff)}.st-table-container table tbody tr td.sticky-col:first-child{left:0}.st-table-container table tbody tr td.st-actions-col{width:100px;min-width:100px;max-width:100px;background:var(--st-frozen-col-bg, #f9f9f9);box-shadow:var(--st-actions-col-shadow, -4px 0 8px rgba(0, 0, 0, .06));text-align:center;position:sticky;right:0;z-index:2}.st-table-container table tbody tr:last-child td{border-bottom:none}.st-table-container table tbody tr:last-child td:first-child{border-bottom-left-radius:var(--st-border-radius, 8px)}.st-table-container table tbody tr:last-child td:last-child{border-bottom-right-radius:var(--st-border-radius, 8px)}.st-table-container table tbody tr:hover td{font-weight:500;background:var(--st-row-hover-bg, #f5f5f5)}.st-table-container table tbody tr:hover td.first-data-col{color:var(--st-first-col-color, #E84646)}.st-table-container table tbody tr.selected td,.st-table-container table tbody tr.selected td.sticky-col{background:var(--st-row-selected-bg, #f3e5f5)}.st-table-container table tbody tr .clickable-cell{cursor:pointer;transition:background .2s}.st-table-container table.is-selectable tbody td.first-data-col,.st-table-container table.is-selectable tbody td.first-data-col.sticky-col{background:var(--st-row-bg, #fff)}.st-table-container table.is-selectable tbody tr:hover td.first-data-col,.st-table-container table.is-selectable tbody tr:hover td.first-data-col.sticky-col{background:var(--st-row-hover-bg, #f5f5f5)}.st-table-container table.is-selectable tbody tr.selected td.first-data-col{background:var(--st-row-selected-bg, #f3e5f5)}.sort-icon-wrap{display:inline-flex;align-items:center;vertical-align:middle;flex-shrink:0;margin-left:4px;position:relative;top:-3px}.sort-icon-wrap svg{display:block}.st-selection-strip{display:flex;align-items:center;padding:var(--st-selection-strip-padding, .45rem 1rem);background:var(--st-selection-strip-bg, #fafafa);border-bottom:var(--st-selection-strip-border, 1px solid rgba(0, 0, 0, .08))}.st-selection-strip .st-selection-count{font-size:var(--st-selection-count-font-size, 13px);color:var(--st-selection-count-color, #444);font-weight:var(--st-selection-count-font-weight, 500)}input[type=checkbox]{accent-color:var(--st-checkbox-color, #e63e30);width:var(--st-checkbox-size, 16px);height:var(--st-checkbox-size, 16px);cursor:pointer}.st-badge{display:inline-block;padding:var(--st-badge-padding, 4px 12px);border-radius:var(--st-badge-radius, 12px);font-size:var(--st-badge-font-size, 12px);font-weight:var(--st-badge-font-weight, 500);text-align:center;white-space:nowrap}.st-badge.badge-success{background:var(--st-badge-success-bg, #e8f5e9);color:var(--st-badge-success-color, #2e7d32)}.st-badge.badge-warning{background:var(--st-badge-warning-bg, #fff3e0);color:var(--st-badge-warning-color, #ef6c00)}.st-badge.badge-danger{background:var(--st-badge-danger-bg, #ffebee);color:var(--st-badge-danger-color, #c62828)}.st-badge.badge-info{background:var(--st-badge-info-bg, #e3f2fd);color:var(--st-badge-info-color, #1565c0)}.st-badge.badge-neutral{background:var(--st-badge-neutral-bg, #f5f5f5);color:var(--st-badge-neutral-color, #616161)}.st-row-actions .action-buttons{display:flex;gap:.5rem;align-items:center;justify-content:center}.st-action-icon{margin-right:var(--st-action-icon-margin, 8px)}.st-dropdown-container{position:relative;display:inline-block}.st-dropdown-btn{background:transparent;border:none;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center}.st-dropdown-btn:hover .action-circle{background:#0000000f}.action-circle{display:inline-flex;align-items:center;justify-content:center;width:var(--st-action-circle-size, 32px);height:var(--st-action-circle-size, 32px);border-radius:50%;background:var(--st-table-bg, #fff);border:none;color:var(--st-text-color, #555);font-size:1rem;transition:background .15s}.st-dropdown-menu{min-width:var(--st-dropdown-min-width, 150px);background:var(--st-dropdown-bg, #fff);border:var(--st-dropdown-border, 1px solid #e0e0e0);border-radius:var(--st-dropdown-radius, 8px);box-shadow:var(--st-dropdown-shadow, 0 8px 24px rgba(0, 0, 0, .12));z-index:9999;display:flex;flex-direction:column;padding:var(--st-dropdown-padding, 4px);margin-top:4px}.st-dropdown-item{background:transparent;border:none;padding:var(--st-dropdown-item-padding, 8px 12px);border-radius:var(--st-dropdown-item-radius, 6px);text-align:left;cursor:pointer;display:flex;align-items:center;font-size:var(--st-font-size, 14px);color:var(--st-text-color, #333);transition:background .15s}.st-dropdown-item:hover{background:var(--st-dropdown-item-hover-bg, #f5f5f5)}.st-dropdown-item .st-action-icon{margin-right:8px;width:16px;text-align:center}.no-data{padding:2rem;color:var(--st-no-data-color, #888);text-align:center}.st-pagination{padding:var(--st-pagination-padding, .75rem 0)}@media(max-width:768px){.st-toolbar{flex-direction:column;align-items:stretch}.st-toolbar .st-search,.st-toolbar .st-filters,.st-toolbar .st-actions,.st-toolbar .st-search input{width:100%}.st-table-container{font-size:13px}}.st-table-container{position:relative}.st-table-container .st-table.loading-data{opacity:.5;pointer-events:none}.st-table-container .st-check-loader{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;z-index:10}.st-table-container .st-check-loader .spinner{width:40px;height:40px;border:4px solid var(--st-spinner-border-color, rgba(0, 0, 0, .1));border-left-color:var(--st-loader-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
10429
|
+
args: [{ selector: 'lib-smart-table', standalone: false, template: "<div class=\"smart-table-outer\">\n <!-- Table Card -->\n <div class=\"smart-table-wrapper\">\n <!-- Top Toolbar -->\n <div class=\"st-toolbar\" *ngIf=\"config.searchConfig?.enabled || (config.filters && config.filters.length > 0) || (config.topBarButtons && config.topBarButtons.length > 0)\">\n\n <!-- Search -->\n <div class=\"st-search\" *ngIf=\"config.searchConfig?.enabled\">\n <i class=\"fa fa-search\"></i>\n <input type=\"text\" [placeholder]=\"config.labels?.searchPlaceholder || 'Search'\" (input)=\"onSearch($event)\">\n </div>\n\n <!-- Filters -->\n <div class=\"st-filters\" *ngIf=\"config.filters\">\n <div class=\"st-filter-item\" *ngFor=\"let filter of config.filters\">\n <!-- Trigger -->\n <div class=\"st-filter-trigger\"\n [class.active]=\"openFilterKey === filter.key\"\n [class.has-value]=\"isFilterActive(filter)\"\n (click)=\"toggleFilter(filter.key, $event)\">\n <span class=\"st-filter-trigger-label\">{{ getFilterDisplay(filter) }}</span>\n <svg class=\"st-filter-chevron\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M2.5 4.5L6 8L9.5 4.5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n </div>\n </div>\n\n <!-- Top Bar Buttons -->\n <div class=\"st-actions\" *ngIf=\"config.topBarButtons\">\n <lib-button *ngFor=\"let btn of config.topBarButtons\"\n [variant]=\"btn.btnVariant || 'primary'\"\n [icon]=\"btn.icon || ''\"\n (click)=\"onTopAction(btn)\">\n {{ btn.label }}\n </lib-button>\n </div>\n </div>\n\n <!-- Selection count strip \u2014 visible whenever selectable is enabled -->\n <div class=\"st-selection-strip\" *ngIf=\"config.selectable\">\n <span class=\"st-selection-count\">{{ selectedRows.length }} of {{ totalItems > 0 ? totalItems : data.length }} selected</span>\n </div>\n\n <!-- Table Container -->\n <div class=\"st-table-container\">\n <div class=\"st-check-loader\" *ngIf=\"loading\">\n <div class=\"spinner\"></div>\n </div>\n <table class=\"st-table\" [class.loading-data]=\"loading\" [class.is-selectable]=\"config.selectable\">\n <thead>\n <tr>\n <th *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\n <input type=\"checkbox\" (change)=\"onSelectAll($event)\">\n </th>\n <th *ngFor=\"let col of config.columns; let colIndex = index\"\n #stickyHeader\n [class.sortable]=\"col.sortable\"\n [class.sticky-col]=\"col.sticky\"\n [class.first-data-col]=\"colIndex === 0\"\n [ngStyle]=\"stickyColumnStyles[col.key]\"\n (click)=\"onSort(col)\">\n {{ col.label }}\n <span *ngIf=\"col.sortable\" class=\"sort-icon-wrap\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 18 18\" fill=\"none\">\n <!-- Down arrow \u2014 RIGHT side, prominent when DESC -->\n <path d=\"M11.258 15.8153L14.5913 13.2699C14.7483 13.15 14.8364 12.9875 14.8364 12.818C14.8364 12.6486 14.7483 12.4861 14.5913 12.3662C14.4344 12.2464 14.2216 12.1791 13.9997 12.1791C13.7778 12.1791 13.5649 12.2464 13.408 12.3662L11.4997 13.8299V2.63636C11.4997 2.46758 11.4119 2.30572 11.2556 2.18638C11.0993 2.06704 10.8874 2 10.6663 2C10.4453 2 10.2334 2.06704 10.0771 2.18638C9.92081 2.30572 9.83301 2.46758 9.83301 2.63636V15.3635C9.83383 15.4891 9.88333 15.6117 9.97528 15.7159C10.0672 15.8201 10.1975 15.9012 10.3497 15.9489C10.4486 15.9845 10.557 16.002 10.6663 15.9998C10.776 16.0003 10.8847 15.9843 10.9863 15.9526C11.0878 15.9209 11.1801 15.8743 11.258 15.8153Z\"\n fill=\"black\" [attr.fill-opacity]=\"getDescOpacity(col.key)\"/>\n <!-- Up arrow \u2014 LEFT side, prominent when ASC -->\n <path d=\"M7.33316 16C7.11215 16 6.90019 15.933 6.74391 15.8136C6.58763 15.6942 6.49983 15.5324 6.49983 15.3636V4.16902L4.5915 5.63278C4.43458 5.75262 4.22175 5.81995 3.99983 5.81995C3.77791 5.81995 3.56508 5.75262 3.40816 5.63278C3.25124 5.51294 3.16309 5.3504 3.16309 5.18093C3.16309 5.01145 3.25124 4.84891 3.40816 4.72907L6.7415 2.18341C6.85868 2.0951 7.00749 2.03528 7.16915 2.01149C7.33081 1.98771 7.49807 2.00102 7.64983 2.04976C7.80201 2.0975 7.93228 2.17858 8.02423 2.28278C8.11617 2.38697 8.16567 2.50962 8.1665 2.63526V15.3636C8.1665 15.5324 8.0787 15.6942 7.92242 15.8136C7.76614 15.933 7.55418 16 7.33316 16Z\"\n fill=\"black\" [attr.fill-opacity]=\"getAscOpacity(col.key)\"/>\n </svg>\n </span>\n </th>\n <th *ngIf=\"config.actions && config.actions.length > 0\" class=\"st-actions-col\">\n {{ config.labels?.actionColumnHeader || 'Actions' }}\n </th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let row of data; let rowIndex = index\">\n <td *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\n <input type=\"checkbox\" [(ngModel)]=\"row.selected\" (change)=\"onRowSelect(row)\">\n </td>\n <td *ngFor=\"let col of config.columns; let colIndex = index\"\n [class.sticky-col]=\"col.sticky\"\n [class.first-data-col]=\"colIndex === 0\"\n [ngStyle]=\"stickyColumnStyles[col.key]\"\n [class.clickable-cell]=\"col.clickAction\"\n (click)=\"onColumnClick(row, col)\">\n <!-- Text/Number/Date -->\n <span *ngIf=\"col.type !== 'custom' && col.type !== 'html' && col.type !== 'badge'\">\n {{ getCellValue(row, col) }}\n </span>\n <!-- HTML -->\n <div *ngIf=\"col.type === 'html'\" [innerHTML]=\"getCellValue(row, col)\"></div>\n <!-- Badge -->\n <span *ngIf=\"col.type === 'badge'\" class=\"st-badge\" [ngClass]=\"getBadgeClass(row, col)\">\n {{ getCellValue(row, col) }}\n </span>\n </td>\n\n <!-- Row Actions -->\n <td *ngIf=\"config.actions && config.actions.length > 0\" class=\"st-row-actions st-actions-col\">\n <div class=\"action-buttons\">\n <ng-container *ngFor=\"let action of config.actions; let i = index\">\n <ng-container *ngIf=\"action.type === 'dropdown'\">\n <div class=\"st-dropdown-container\" (click)=\"$event.stopPropagation()\">\n <button class=\"st-dropdown-btn\"\n [class.active]=\"openDropdownId === (rowIndex + '-' + i)\"\n (click)=\"toggleDropdown(rowIndex + '-' + i, $event, action.items, row)\">\n <span class=\"action-circle\">\n <i class=\"fa fa-ellipsis-h\"></i>\n </span>\n </button>\n </div>\n </ng-container>\n <ng-container *ngIf=\"action.type !== 'dropdown'\">\n <lib-button\n [variant]=\"action.btnVariant || 'secondary'\"\n [icon]=\"action.icon || ''\"\n (click)=\"onAction(action, row)\">\n {{ action.label }}\n </lib-button>\n </ng-container>\n </ng-container>\n </div>\n </td>\n </tr>\n <tr *ngIf=\"data.length === 0 && !loading\">\n <td [attr.colspan]=\"columnCount + (config.selectable ? 1 : 0) + (config.actions ? 1 : 0)\" class=\"no-data\">\n {{ config.labels?.noDataMessage || 'No data available' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- Pagination \u2014 inside the card, sticks to the bottom -->\n <div class=\"st-pagination\" *ngIf=\"showPagination\">\n <lib-pagination\n [totalItems]=\"totalItems\"\n [itemsPerPage]=\"config.pagination!.pageSize\"\n [currentPage]=\"currentPage\"\n [pageSizeOptions]=\"config.pagination!.pageSizeOptions\"\n (pageChange)=\"onPageChange($event)\"\n (itemsPerPageChange)=\"onPageSizeChange($event)\">\n </lib-pagination>\n </div>\n\n <!-- Built-in Delete Confirmation Modal -->\n <cc-confirmation-modal\n [isOpen]=\"deleteModalOpen\"\n [config]=\"deleteModalConfig\"\n (confirm)=\"onDeleteConfirm()\"\n (cancel)=\"onDeleteCancel()\"\n (close)=\"onDeleteCancel()\">\n <p>{{ deleteModalMessage }}</p>\n </cc-confirmation-modal>\n </div>\n</div>\n\n<!--\n Filter panel portal \u2014 position:fixed keeps it above all overflow/sticky containers.\n-->\n<div class=\"st-filter-panel\"\n *ngIf=\"openFilterKey !== null && activeFilterData\"\n [ngStyle]=\"{\n position: 'fixed',\n top: filterPosition.top + 'px',\n left: filterPosition.left + 'px',\n 'z-index': '99999'\n }\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"st-filter-option\"\n [class.selected]=\"!isFilterActive(activeFilterData)\"\n (click)=\"selectFilterOption(activeFilterData, null)\">\n All {{ activeFilterData.label }}\n </div>\n <div class=\"st-filter-option\"\n *ngFor=\"let opt of getValidFilterOptions(activeFilterData)\"\n [class.selected]=\"activeFilters[activeFilterData.key] === opt.value\"\n (click)=\"selectFilterOption(activeFilterData, opt)\">\n {{ opt.label }}\n </div>\n</div>\n\n<!--\n Dropdown portal \u2014 rendered OUTSIDE every scroll/sticky container.\n position:fixed + z-index:99999 here is unconditionally above all sticky table cells.\n-->\n<div class=\"st-dropdown-menu st-dropdown-portal\"\n *ngIf=\"openDropdownId !== null && activeDropdownItems\"\n [ngStyle]=\"{\n position: 'fixed',\n top: dropdownPosition.top + 'px',\n right: dropdownPosition.right + 'px',\n left: 'auto',\n 'z-index': '99999'\n }\"\n (click)=\"$event.stopPropagation()\">\n <button class=\"st-dropdown-item\"\n *ngFor=\"let item of activeDropdownItems\"\n (click)=\"onActionItemClick(item, activeDropdownRow, $event); closeDropdown()\">\n <i *ngIf=\"item.icon\" [class]=\"item.icon + ' st-action-icon'\"></i>\n <span>{{ item.label }}</span>\n </button>\n</div>\n", styles: ["@charset \"UTF-8\";.smart-table-outer{display:flex;flex-direction:column;gap:0}.smart-table-wrapper{font-family:var(--st-font-family, \"Roboto\", sans-serif);background:var(--st-table-bg, #fff);border-radius:var(--st-border-radius, 8px);box-shadow:var(--st-box-shadow, 0 2px 8px rgba(0, 0, 0, .08));display:flex;flex-direction:column;gap:0;padding:0;border:none;margin-bottom:1.5rem}.st-toolbar{display:flex;justify-content:flex-start;align-items:center;flex-wrap:wrap;padding:var(--st-toolbar-padding, 1.2rem 0rem);background:var(--st-toolbar-bg, #fff);gap:var(--st-toolbar-gap, 1rem);border-bottom:var(--st-toolbar-border-bottom, 1px solid rgba(0, 0, 0, .08));box-shadow:#959da533 0 8px 24px}.st-toolbar .st-search{position:relative;display:flex;align-items:center;background:var(--st-search-bg, #f5f5f5);border-radius:var(--st-search-radius, 8px);padding:var(--st-search-wrapper-padding, 0 .875rem);height:var(--st-search-height, 36px);min-width:var(--st-search-min-width, 200px);width:var(--st-search-width, auto)}.st-toolbar .st-search input{padding:0 .5rem 0 1.5rem;border:none;background:transparent;font-size:var(--st-font-size, 14px);width:100%;color:var(--st-text-color, #333);outline:none;line-height:var(--st-search-height, 36px)}.st-toolbar .st-search input::placeholder{color:var(--st-search-placeholder-color, #999)}.st-toolbar .st-search i{position:absolute;left:.875rem;top:50%;transform:translateY(-50%);color:var(--st-search-icon-color, #888);font-size:13px;pointer-events:none}.st-toolbar .st-filters{display:flex;align-items:center;gap:.5rem}.st-toolbar .st-filters .st-filter-item{position:relative}.st-toolbar .st-filters .st-filter-trigger{display:flex;align-items:center;gap:6px;padding:var(--st-filter-trigger-padding, 0 .75rem);height:var(--st-filter-height, 36px);border:var(--st-filter-border, 1px solid #e0e0e0);border-radius:var(--st-filter-radius, 8px);cursor:pointer;font-size:var(--st-filter-font-size, 14px);color:var(--st-filter-color, #555);background:var(--st-filter-bg, #fff);transition:border-color .15s,background .15s;white-space:nowrap;-webkit-user-select:none;user-select:none}.st-toolbar .st-filters .st-filter-trigger:hover{border-color:var(--st-filter-hover-border-color, #bbb);background:var(--st-filter-hover-bg, #fafafa)}.st-toolbar .st-filters .st-filter-trigger.active{border-color:var(--st-filter-active-border-color, #999);background:var(--st-filter-hover-bg, #fafafa)}.st-toolbar .st-filters .st-filter-trigger.active .st-filter-chevron{transform:rotate(180deg)}.st-toolbar .st-filters .st-filter-trigger.has-value{color:var(--st-filter-selected-color, #222);font-weight:500;border-color:var(--st-filter-selected-border-color, #333)}.st-toolbar .st-filters .st-filter-trigger .st-filter-trigger-label{flex:1}.st-toolbar .st-filters .st-filter-trigger .st-filter-chevron{color:var(--st-filter-chevron-color, #999);flex-shrink:0;transition:transform .15s ease}.st-toolbar .st-actions{display:flex;gap:.5rem;margin-left:auto}.st-filter-panel{min-width:var(--st-filter-panel-min-width, 180px);background:var(--st-filter-panel-bg, #fff);border:var(--st-filter-panel-border, 1px solid #ebebeb);border-radius:var(--st-filter-panel-radius, 12px);box-shadow:var(--st-filter-panel-shadow, 0 8px 28px rgba(0, 0, 0, .12), 0 2px 8px rgba(0, 0, 0, .06));padding:var(--st-filter-panel-padding, 6px)}.st-filter-option{display:flex;align-items:center;padding:var(--st-filter-option-padding, 8px 12px);margin-bottom:.3rem;border-radius:var(--st-filter-option-radius, 7px);font-size:var(--st-filter-font-size, 14px);color:var(--st-filter-color, #333);cursor:pointer;transition:background .1s;white-space:nowrap}.st-filter-option:hover{background:var(--st-filter-option-hover-bg, #f5f5f5)}.st-filter-option.selected{background:var(--st-filter-option-selected-bg, #f0f0f0);font-weight:500;color:var(--st-filter-option-selected-color, #111)}.st-table-container{overflow-x:auto;overflow-y:visible;padding:0}.st-table-container::-webkit-scrollbar{width:var(--st-scrollbar-width, 8px);height:var(--st-scrollbar-height, 8px)}.st-table-container::-webkit-scrollbar-track{background:var(--st-scrollbar-track-bg, #f1f1f1);border-radius:var(--st-scrollbar-track-radius, 4px)}.st-table-container::-webkit-scrollbar-thumb{background:var(--st-scrollbar-thumb-bg, #c1c1c1);border-radius:var(--st-scrollbar-thumb-radius, 4px)}.st-table-container::-webkit-scrollbar-thumb:hover{background:var(--st-scrollbar-thumb-hover-bg, #a8a8a8)}.st-table-container.has-sticky-header .st-table thead th{position:sticky;top:0;z-index:10;background:var(--st-header-bg, #f5f5f5);box-shadow:0 1px 2px -1px #0000001a}.st-table-container table{width:100%;border-collapse:separate;border-spacing:0;font-size:var(--st-font-size, 14px)}.st-table-container table thead{background:var(--st-header-bg, #f5f5f5)}.st-table-container table thead th{padding:.4rem 1rem;text-align:left;color:var(--st-header-color, #333);font-weight:var(--st-header-weight, 500);font-size:var(--st-header-size, 14px);text-transform:var(--st-header-transform, none);border-top:none;border-bottom:none;white-space:nowrap}.st-table-container table thead th:first-child{border-top-left-radius:var(--st-border-radius, 8px)}.st-table-container table thead th:last-child{border-top-right-radius:var(--st-border-radius, 8px)}.st-table-container table thead th.sortable{cursor:pointer}.st-table-container table thead th.sortable:hover{opacity:.8}.st-table-container table thead th.st-checkbox-col{width:40px;background:var(--st-header-bg, #f5f5f5);position:sticky;left:0;z-index:3}.st-table-container table thead th.sticky-col{position:sticky;z-index:3;background:var(--st-header-bg, #f5f5f5)}.st-table-container table thead th.st-actions-col{width:100px;min-width:100px;max-width:100px;background:var(--st-header-bg, #f5f5f5);box-shadow:var(--st-actions-col-shadow, -4px 0 8px rgba(0, 0, 0, .06));text-align:center;position:sticky;right:0;z-index:4}.st-table-container table tbody tr{background:var(--st-row-bg, #fff)}.st-table-container table tbody tr td{padding:.5rem 1rem;color:var(--st-text-color, #333);vertical-align:middle;border-bottom:var(--st-row-border, 1px solid #eee);font-weight:400}.st-table-container table tbody tr td.st-checkbox-col{position:sticky;left:0;z-index:2}.st-table-container table tbody tr td.first-data-col{color:var(--st-text-color, #333);font-weight:500}.st-table-container table tbody tr td.sticky-col{position:sticky;z-index:2;background:var(--st-row-bg, #fff)}.st-table-container table tbody tr td.sticky-col:first-child{left:0}.st-table-container table tbody tr td.st-actions-col{width:100px;min-width:100px;max-width:100px;background:var(--st-frozen-col-bg, #f9f9f9);box-shadow:var(--st-actions-col-shadow, -4px 0 8px rgba(0, 0, 0, .06));text-align:center;position:sticky;right:0;z-index:2}.st-table-container table tbody tr:last-child td{border-bottom:none}.st-table-container table tbody tr:last-child td:first-child{border-bottom-left-radius:var(--st-border-radius, 8px)}.st-table-container table tbody tr:last-child td:last-child{border-bottom-right-radius:var(--st-border-radius, 8px)}.st-table-container table tbody tr:hover td{font-weight:500;background:var(--st-row-hover-bg, #f5f5f5)}.st-table-container table tbody tr:hover td.first-data-col{color:var(--st-first-col-color, #E84646)}.st-table-container table tbody tr.selected td,.st-table-container table tbody tr.selected td.sticky-col{background:var(--st-row-selected-bg, #f3e5f5)}.st-table-container table tbody tr .clickable-cell{cursor:pointer;transition:background .2s}.st-table-container table.is-selectable tbody td.first-data-col,.st-table-container table.is-selectable tbody td.first-data-col.sticky-col{background:var(--st-row-bg, #fff)}.st-table-container table.is-selectable tbody tr:hover td.first-data-col,.st-table-container table.is-selectable tbody tr:hover td.first-data-col.sticky-col{background:var(--st-row-hover-bg, #f5f5f5)}.st-table-container table.is-selectable tbody tr.selected td.first-data-col{background:var(--st-row-selected-bg, #f3e5f5)}.sort-icon-wrap{display:inline-flex;align-items:center;vertical-align:middle;flex-shrink:0;margin-left:4px;position:relative;top:-3px}.sort-icon-wrap svg{display:block}.st-selection-strip{display:flex;align-items:center;padding:var(--st-selection-strip-padding, .45rem 1rem);background:var(--st-selection-strip-bg, #fafafa);border-bottom:var(--st-selection-strip-border, 1px solid rgba(0, 0, 0, .08))}.st-selection-strip .st-selection-count{font-size:var(--st-selection-count-font-size, 13px);color:var(--st-selection-count-color, #444);font-weight:var(--st-selection-count-font-weight, 500)}input[type=checkbox]{accent-color:var(--st-checkbox-color, #e63e30);width:var(--st-checkbox-size, 16px);height:var(--st-checkbox-size, 16px);cursor:pointer}.st-badge{display:inline-block;padding:var(--st-badge-padding, 4px 12px);border-radius:var(--st-badge-radius, 12px);font-size:var(--st-badge-font-size, 12px);font-weight:var(--st-badge-font-weight, 500);text-align:center;white-space:nowrap}.st-badge.badge-success{background:var(--st-badge-success-bg, #e8f5e9);color:var(--st-badge-success-color, #2e7d32)}.st-badge.badge-warning{background:var(--st-badge-warning-bg, #fff3e0);color:var(--st-badge-warning-color, #ef6c00)}.st-badge.badge-danger{background:var(--st-badge-danger-bg, #ffebee);color:var(--st-badge-danger-color, #c62828)}.st-badge.badge-info{background:var(--st-badge-info-bg, #e3f2fd);color:var(--st-badge-info-color, #1565c0)}.st-badge.badge-neutral{background:var(--st-badge-neutral-bg, #f5f5f5);color:var(--st-badge-neutral-color, #616161)}.st-row-actions .action-buttons{display:flex;gap:.5rem;align-items:center;justify-content:center}.st-action-icon{margin-right:var(--st-action-icon-margin, 8px)}.st-dropdown-container{position:relative;display:inline-block}.st-dropdown-btn{background:transparent;border:none;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center}.st-dropdown-btn:hover .action-circle{background:#0000000f}.action-circle{display:inline-flex;align-items:center;justify-content:center;width:var(--st-action-circle-size, 32px);height:var(--st-action-circle-size, 32px);border-radius:50%;background:var(--st-table-bg, #fff);border:none;color:var(--st-text-color, #555);font-size:1rem;transition:background .15s}.st-dropdown-menu{min-width:var(--st-dropdown-min-width, 150px);background:var(--st-dropdown-bg, #fff);border:var(--st-dropdown-border, 1px solid #e0e0e0);border-radius:var(--st-dropdown-radius, 8px);box-shadow:var(--st-dropdown-shadow, 0 8px 24px rgba(0, 0, 0, .12));z-index:9999;display:flex;flex-direction:column;padding:var(--st-dropdown-padding, 4px);margin-top:4px}.st-dropdown-item{background:transparent;border:none;padding:var(--st-dropdown-item-padding, 8px 12px);border-radius:var(--st-dropdown-item-radius, 6px);text-align:left;cursor:pointer;display:flex;align-items:center;font-size:var(--st-font-size, 14px);color:var(--st-text-color, #333);transition:background .15s}.st-dropdown-item:hover{background:var(--st-dropdown-item-hover-bg, #f5f5f5)}.st-dropdown-item .st-action-icon{margin-right:8px;width:16px;text-align:center}.no-data{padding:2rem;color:var(--st-no-data-color, #888);text-align:center}.st-pagination{padding:var(--st-pagination-padding, .6rem 1rem);border-top:1px solid rgba(0,0,0,.07);background:var(--st-table-bg, #fff);border-bottom-left-radius:var(--st-border-radius, 8px);border-bottom-right-radius:var(--st-border-radius, 8px)}@media(max-width:768px){.st-toolbar{flex-direction:column;align-items:stretch}.st-toolbar .st-search,.st-toolbar .st-filters,.st-toolbar .st-actions,.st-toolbar .st-search input{width:100%}.st-table-container{font-size:13px}}.st-table-container{position:relative}.st-table-container .st-table.loading-data{opacity:.5;pointer-events:none}.st-table-container .st-check-loader{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;z-index:10}.st-table-container .st-check-loader .spinner{width:40px;height:40px;border:4px solid var(--st-spinner-border-color, rgba(0, 0, 0, .1));border-left-color:var(--st-loader-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
10349
10430
|
}], ctorParameters: () => [{ type: i3.HttpClient }, { type: i1$4.Router }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }], propDecorators: { config: [{
|
|
10350
10431
|
type: Input
|
|
10351
10432
|
}], tableData: [{
|
|
@@ -10648,11 +10729,11 @@ class FilterTableSelectorComponent {
|
|
|
10648
10729
|
return Object.keys(this.activeFilterParams).length > 0;
|
|
10649
10730
|
}
|
|
10650
10731
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FilterTableSelectorComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
|
|
10651
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FilterTableSelectorComponent, isStandalone: false, selector: "lib-filter-table-selector", inputs: { config: "config", labels: "labels", preSelectedRows: "preSelectedRows" }, outputs: { onSubmit: "onSubmit", onCancel: "onCancel" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fts-container\">\r\n\r\n <!-- \u2500\u2500 Modal Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"fts-header\" *ngIf=\"config?.title\">\r\n <h2 class=\"fts-header__title\">{{ titleLabel }}</h2>\r\n </div>\r\n\r\n <!-- \u2500\u2500 Body: Filter Panel + Table Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"fts-body\" *ngIf=\"config\">\r\n\r\n <!-- LEFT: Filter Panel -->\r\n <aside class=\"fts-filter-panel\" [class.fts-filter-panel--hidden]=\"!filterPanelVisible\">\r\n\r\n <div class=\"fts-filter-panel__header\">\r\n <span class=\"fts-filter-panel__title\">{{ translate('COMMON.FILTER_TABLE.FILTER_BY') || 'Filter By' }}</span>\r\n </div>\r\n\r\n <div class=\"fts-filter-panel__content\">\r\n <!-- SmartForm renders the dynamic filter form. -->\r\n <ng-container *ngIf=\"resolvedFormJson && formVersion >= 0\">\r\n <lib-smart-form [formJson]=\"resolvedFormJson\" [labels]=\"labels\" [initialValues]=\"resolvedInitialValues\"\r\n (valueChange)=\"onFormValueChange($event)\">\r\n </lib-smart-form>\r\n </ng-container>\r\n </div>\r\n\r\n </aside>\r\n\r\n <!-- RIGHT: Table Panel -->\r\n <section class=\"fts-table-panel\">\r\n\r\n <!-- Table toolbar: selection count -->\r\n <!-- <div class=\"fts-table-panel__toolbar\">\r\n <span class=\"fts-selection-count\" *ngIf=\"config.selectionConfig?.selectionCountLabel\">\r\n {{ selectionCountText }}\r\n </span>\r\n </div> -->\r\n\r\n <!-- SmartTable -->\r\n <div class=\"fts-table-wrapper\">\r\n <lib-smart-table *ngIf=\"resolvedTableConfig\" [config]=\"resolvedTableConfig\" [selectedRows]=\"selectedRows\"\r\n (rowSelect)=\"onRowSelect($event)\">\r\n </lib-smart-table>\r\n </div>\r\n\r\n </section>\r\n\r\n </div>\r\n\r\n <!-- \u2500\u2500 Footer Action Bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"fts-footer\">\r\n <div class=\"fts-footer__left\">\r\n <lib-button id=\"fts-clear-filter-btn\" variant=\"outline\" (click)=\"onClearFilter()\">\r\n {{ clearFilterButtonLabel }}\r\n </lib-button>\r\n\r\n <lib-button id=\"fts-apply-filter-btn\" variant=\"primary\" (click)=\"applyCurrentFilter()\">\r\n {{ applyFilterButtonLabel }}\r\n </lib-button>\r\n </div>\r\n\r\n <div class=\"fts-footer__right\">\r\n <lib-button id=\"fts-cancel-btn\" variant=\"outline\" (click)=\"handleCancel()\">\r\n {{ cancelButtonLabel }}\r\n </lib-button>\r\n\r\n <lib-button id=\"fts-submit-btn\" variant=\"primary\" [disabled]=\"selectedRows.length === 0\" (click)=\"handleSubmit()\">\r\n {{ submitButtonLabel }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n\r\n</div>", styles: [":host{--fts-panel-bg: #ffffff;--fts-filter-panel-width: 300px;--fts-filter-panel-border: 1px solid #dee2e6;--fts-header-bg: #ffffff;--fts-header-border: 1px solid #f1f3f4;--fts-header-height: 60px;--fts-header-padding: 0 24px;--fts-header-title-color: #111827;--fts-header-title-font-size: 1.125rem;--fts-header-title-font-weight: 600;--fts-footer-bg: #ffffff;--fts-footer-border: 1px solid #f1f3f4;--fts-footer-height: 72px;--fts-footer-padding: 0 24px;--fts-footer-gap: 12px;--fts-body-bg: #ffffff;--fts-filter-section-title-color: var(--text-muted, #6b7280);--fts-filter-section-title-font-size: .75rem;--fts-filter-section-title-font-weight: 600;--fts-selection-count-color: var(--text-muted, #6b7280);--fts-selection-count-font-size: .875rem;--fts-toolbar-border: 1px solid #f1f3f4;--fts-divider-color: #f1f3f4;--fts-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--fts-border-radius: 12px;display:flex;flex-direction:column;flex:1;min-height:0;width:100%}.fts-container{display:flex;flex-direction:column;flex:1;min-height:0;width:100%;overflow:hidden;background-color:var(--fts-panel-bg);border:1px solid var(--border-color, #e0e0e0);border-radius:var(--fts-border-radius);box-shadow:0 4px 20px #00000014}.fts-header{display:flex;align-items:center;min-height:var(--fts-header-height);padding:var(--fts-header-padding);border-bottom:var(--fts-header-border);background-color:var(--fts-header-bg);flex-shrink:0}.fts-header__title{margin:0;font-size:var(--fts-header-title-font-size);font-weight:var(--fts-header-title-font-weight);color:var(--fts-header-title-color);letter-spacing:-.01em}.fts-body{display:flex;flex:1;min-height:0;background-color:var(--fts-body-bg)}.fts-filter-panel{width:var(--fts-filter-panel-width);min-width:var(--fts-filter-panel-width);display:flex;flex-direction:column;overflow:hidden;background-color:#fcfcfd;border-right:1px solid var(--fts-divider-color)}.fts-filter-panel--hidden{display:none}.fts-filter-panel__header{padding:20px 20px 12px;flex-shrink:0}.fts-filter-panel__title{font-size:var(--fts-filter-section-title-font-size);font-weight:var(--fts-filter-section-title-font-weight);color:var(--fts-filter-section-title-color);text-transform:uppercase;letter-spacing:.05em}.fts-filter-panel__content{flex:1;min-height:0;overflow-y:auto;padding:0}.fts-filter-panel__content::-webkit-scrollbar{width:4px}.fts-filter-panel__content::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:10px}.fts-filter-panel ::ng-deep .smart-form-container,.fts-filter-panel ::ng-deep .smart-form-wrapper{padding:0}.fts-filter-panel ::ng-deep .form-header,.fts-filter-panel ::ng-deep .form-actions{display:none}.fts-filter-panel ::ng-deep .form-section{padding:0}.fts-filter-panel ::ng-deep .form-field-wrapper{margin-bottom:16px}.fts-filter-panel ::ng-deep .form-field-wrapper:last-child{margin-bottom:0}.fts-filter-panel ::ng-deep .field-label{font-size:12px;font-weight:500;color:#374151;margin-bottom:4px}.fts-filter-panel ::ng-deep input,.fts-filter-panel ::ng-deep select,.fts-filter-panel ::ng-deep .mat-select-trigger{font-size:13px!important;border-color:#e5e7eb!important;background-color:#fff!important}.fts-filter-panel ::ng-deep input:focus,.fts-filter-panel ::ng-deep select:focus,.fts-filter-panel ::ng-deep .mat-select-trigger:focus{border-color:var(--primary-color)!important;box-shadow:0 0 0 2px #c21e250d!important}.form-section-container .section-fields.sf-grid{gap:2px!important}.fts-table-panel{flex:1;min-width:0;display:flex;flex-direction:column;overflow:hidden;background-color:var(--fts-panel-bg)}.fts-table-panel__toolbar{display:flex;align-items:center;justify-content:flex-end;flex-shrink:0;min-height:48px;padding:0 24px;background-color:var(--fts-panel-bg)}.fts-table-wrapper{flex:1;min-height:0;display:flex;flex-direction:column;overflow-y:auto}.fts-table-wrapper ::ng-deep .smart-table-outer{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.fts-table-wrapper ::ng-deep .smart-table-wrapper{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.fts-table-wrapper ::ng-deep .st-table-container{flex:1;min-height:0;overflow-x:auto;overflow-y:auto!important}.fts-table-wrapper ::ng-deep .st-table-container::-webkit-scrollbar{width:6px}.fts-table-wrapper ::ng-deep .st-table-container::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:3px}.fts-table-wrapper ::ng-deep .st-pagination{flex-shrink:0;padding:12px 24px;border-top:1px solid var(--fts-divider-color);background-color:var(--fts-panel-bg)}.fts-selection-count{font-size:13px;color:#4b5563;white-space:nowrap;background:#f3f4f6;padding:4px 14px;border-radius:100px;font-weight:500;border:1px solid #e5e7eb}.fts-footer{display:flex;align-items:center;justify-content:space-between;min-height:var(--fts-footer-height);padding:var(--fts-footer-padding);border-top:var(--fts-footer-border);flex-shrink:0;background-color:var(--fts-footer-bg);box-shadow:0 -4px 12px #0000000d;z-index:10}.fts-footer__left,.fts-footer__right{display:flex;align-items:center;gap:var(--fts-footer-gap)}.fts-footer ::ng-deep lib-button button{min-width:100px;font-weight:500;letter-spacing:.01em}::ng-deep .cc-modal-footer{display:none!important}::ng-deep .cc-modal-body:has(lib-filter-table-selector){padding:0!important;overflow:hidden!important;display:flex!important;flex-direction:column!important}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SmartFormComponent, selector: "lib-smart-form", inputs: ["formJson", "initialValues", "enableDraftAutoSave", "labels", "mode", "readOnly"], outputs: ["submit", "draftSave", "actionClick", "valueChange", "fileAdded", "fileUploadFinished", "fileRemoved", "stepChange"] }, { kind: "component", type: SmartTableComponent, selector: "lib-smart-table", inputs: ["config", "tableData", "totalItemsCount", "selectedRows"], outputs: ["action", "topAction", "filterChange", "rowSelect", "columnClick", "sortChange", "pageChange", "searchChange"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }] });
|
|
10732
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FilterTableSelectorComponent, isStandalone: false, selector: "lib-filter-table-selector", inputs: { config: "config", labels: "labels", preSelectedRows: "preSelectedRows" }, outputs: { onSubmit: "onSubmit", onCancel: "onCancel" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fts-container\">\r\n\r\n <!-- \u2500\u2500 Modal Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"fts-header\" *ngIf=\"config?.title\">\r\n <h2 class=\"fts-header__title\">{{ titleLabel }}</h2>\r\n </div>\r\n\r\n <!-- \u2500\u2500 Body: Filter Panel + Table Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"fts-body\" *ngIf=\"config\">\r\n\r\n <!-- LEFT: Filter Panel -->\r\n <aside class=\"fts-filter-panel\" [class.fts-filter-panel--hidden]=\"!filterPanelVisible\">\r\n\r\n <div class=\"fts-filter-panel__header\">\r\n <span class=\"fts-filter-panel__title\">{{ translate('COMMON.FILTER_TABLE.FILTER_BY') || 'Filter By' }}</span>\r\n </div>\r\n\r\n <div class=\"fts-filter-panel__content\">\r\n <!-- SmartForm renders the dynamic filter form. -->\r\n <ng-container *ngIf=\"resolvedFormJson && formVersion >= 0\">\r\n <lib-smart-form [formJson]=\"resolvedFormJson\" [labels]=\"labels\" [initialValues]=\"resolvedInitialValues\"\r\n (valueChange)=\"onFormValueChange($event)\">\r\n </lib-smart-form>\r\n </ng-container>\r\n </div>\r\n\r\n </aside>\r\n\r\n <!-- RIGHT: Table Panel -->\r\n <section class=\"fts-table-panel\">\r\n\r\n <!-- Table toolbar: selection count -->\r\n <!-- <div class=\"fts-table-panel__toolbar\">\r\n <span class=\"fts-selection-count\" *ngIf=\"config.selectionConfig?.selectionCountLabel\">\r\n {{ selectionCountText }}\r\n </span>\r\n </div> -->\r\n\r\n <!-- SmartTable -->\r\n <div class=\"fts-table-wrapper\">\r\n <lib-smart-table *ngIf=\"resolvedTableConfig\" [config]=\"resolvedTableConfig\" [selectedRows]=\"selectedRows\"\r\n (rowSelect)=\"onRowSelect($event)\">\r\n </lib-smart-table>\r\n </div>\r\n\r\n </section>\r\n\r\n </div>\r\n\r\n <!-- \u2500\u2500 Footer Action Bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"fts-footer\">\r\n <div class=\"fts-footer__left\">\r\n <lib-button id=\"fts-clear-filter-btn\" variant=\"outline\" (click)=\"onClearFilter()\">\r\n {{ clearFilterButtonLabel }}\r\n </lib-button>\r\n\r\n <lib-button id=\"fts-apply-filter-btn\" variant=\"primary\" (click)=\"applyCurrentFilter()\">\r\n {{ applyFilterButtonLabel }}\r\n </lib-button>\r\n </div>\r\n\r\n <div class=\"fts-footer__right\">\r\n <lib-button id=\"fts-cancel-btn\" variant=\"outline\" (click)=\"handleCancel()\">\r\n {{ cancelButtonLabel }}\r\n </lib-button>\r\n\r\n <lib-button id=\"fts-submit-btn\" variant=\"primary\" (click)=\"handleSubmit()\">\r\n {{ submitButtonLabel }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n\r\n</div>", styles: [":host{--fts-panel-bg: #ffffff;--fts-filter-panel-width: 300px;--fts-filter-panel-border: 1px solid #dee2e6;--fts-header-bg: #ffffff;--fts-header-border: 1px solid #f1f3f4;--fts-header-height: 60px;--fts-header-padding: 0 24px;--fts-header-title-color: #111827;--fts-header-title-font-size: 1.125rem;--fts-header-title-font-weight: 600;--fts-footer-bg: #ffffff;--fts-footer-border: 1px solid #f1f3f4;--fts-footer-height: 72px;--fts-footer-padding: 0 24px;--fts-footer-gap: 12px;--fts-body-bg: #ffffff;--fts-filter-section-title-color: var(--text-muted, #6b7280);--fts-filter-section-title-font-size: .75rem;--fts-filter-section-title-font-weight: 600;--fts-selection-count-color: var(--text-muted, #6b7280);--fts-selection-count-font-size: .875rem;--fts-toolbar-border: 1px solid #f1f3f4;--fts-divider-color: #f1f3f4;--fts-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--fts-border-radius: 12px;display:flex;flex-direction:column;flex:1;min-height:0;width:100%}.fts-container{display:flex;flex-direction:column;flex:1;min-height:0;width:100%;overflow:hidden;background-color:var(--fts-panel-bg);border:1px solid var(--border-color, #e0e0e0);border-radius:var(--fts-border-radius);box-shadow:0 4px 20px #00000014}.fts-header{display:flex;align-items:center;min-height:var(--fts-header-height);padding:var(--fts-header-padding);border-bottom:var(--fts-header-border);background-color:var(--fts-header-bg);flex-shrink:0}.fts-header__title{margin:0;font-size:var(--fts-header-title-font-size);font-weight:var(--fts-header-title-font-weight);color:var(--fts-header-title-color);letter-spacing:-.01em}.fts-body{display:flex;flex:1;min-height:0;background-color:var(--fts-body-bg)}.fts-filter-panel{width:var(--fts-filter-panel-width);min-width:var(--fts-filter-panel-width);display:flex;flex-direction:column;overflow:hidden;background-color:#fcfcfd;border-right:1px solid var(--fts-divider-color)}.fts-filter-panel--hidden{display:none}.fts-filter-panel__header{padding:20px 20px 12px;flex-shrink:0}.fts-filter-panel__title{font-size:var(--fts-filter-section-title-font-size);font-weight:var(--fts-filter-section-title-font-weight);color:var(--fts-filter-section-title-color);text-transform:uppercase;letter-spacing:.05em}.fts-filter-panel__content{flex:1;min-height:0;overflow-y:auto;padding:0}.fts-filter-panel__content::-webkit-scrollbar{width:4px}.fts-filter-panel__content::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:10px}.fts-filter-panel ::ng-deep .smart-form-container,.fts-filter-panel ::ng-deep .smart-form-wrapper{padding:0}.fts-filter-panel ::ng-deep .form-header,.fts-filter-panel ::ng-deep .form-actions{display:none}.fts-filter-panel ::ng-deep .form-section{padding:0}.fts-filter-panel ::ng-deep .form-field-wrapper{margin-bottom:16px}.fts-filter-panel ::ng-deep .form-field-wrapper:last-child{margin-bottom:0}.fts-filter-panel ::ng-deep .field-label{font-size:12px;font-weight:500;color:#374151;margin-bottom:4px}.fts-filter-panel ::ng-deep input,.fts-filter-panel ::ng-deep select,.fts-filter-panel ::ng-deep .mat-select-trigger{font-size:13px!important;border-color:#e5e7eb!important;background-color:#fff!important}.fts-filter-panel ::ng-deep input:focus,.fts-filter-panel ::ng-deep select:focus,.fts-filter-panel ::ng-deep .mat-select-trigger:focus{border-color:var(--primary-color)!important;box-shadow:0 0 0 2px #c21e250d!important}.form-section-container .section-fields.sf-grid{gap:2px!important}.fts-table-panel{flex:1;min-width:0;display:flex;flex-direction:column;overflow:hidden;background-color:var(--fts-panel-bg)}.fts-table-panel__toolbar{display:flex;align-items:center;justify-content:flex-end;flex-shrink:0;min-height:48px;padding:0 24px;background-color:var(--fts-panel-bg)}.fts-table-wrapper{flex:1;min-height:0;display:flex;flex-direction:column;overflow-y:auto}.fts-table-wrapper ::ng-deep .smart-table-outer{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.fts-table-wrapper ::ng-deep .smart-table-wrapper{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.fts-table-wrapper ::ng-deep .st-table-container{flex:1;min-height:0;overflow-x:auto;overflow-y:auto!important}.fts-table-wrapper ::ng-deep .st-table-container::-webkit-scrollbar{width:6px}.fts-table-wrapper ::ng-deep .st-table-container::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:3px}.fts-table-wrapper ::ng-deep .st-pagination{flex-shrink:0;padding:12px 24px;border-top:1px solid var(--fts-divider-color);background-color:var(--fts-panel-bg)}.fts-selection-count{font-size:13px;color:#4b5563;white-space:nowrap;background:#f3f4f6;padding:4px 14px;border-radius:100px;font-weight:500;border:1px solid #e5e7eb}.fts-footer{display:flex;align-items:center;justify-content:space-between;min-height:var(--fts-footer-height);padding:var(--fts-footer-padding);border-top:var(--fts-footer-border);flex-shrink:0;background-color:var(--fts-footer-bg);box-shadow:0 -4px 12px #0000000d;z-index:10}.fts-footer__left,.fts-footer__right{display:flex;align-items:center;gap:var(--fts-footer-gap)}.fts-footer ::ng-deep lib-button button{min-width:100px;font-weight:500;letter-spacing:.01em}::ng-deep .cc-modal-footer{display:none!important}::ng-deep .cc-modal-body:has(lib-filter-table-selector){padding:0!important;overflow:hidden!important;display:flex!important;flex-direction:column!important}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: SmartFormComponent, selector: "lib-smart-form", inputs: ["formJson", "initialValues", "enableDraftAutoSave", "labels", "mode", "readOnly"], outputs: ["submit", "draftSave", "actionClick", "valueChange", "fileAdded", "fileUploadFinished", "fileRemoved", "stepChange"] }, { kind: "component", type: SmartTableComponent, selector: "lib-smart-table", inputs: ["config", "tableData", "totalItemsCount", "selectedRows"], outputs: ["action", "topAction", "filterChange", "rowSelect", "columnClick", "sortChange", "pageChange", "searchChange"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }] });
|
|
10652
10733
|
}
|
|
10653
10734
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FilterTableSelectorComponent, decorators: [{
|
|
10654
10735
|
type: Component,
|
|
10655
|
-
args: [{ selector: 'lib-filter-table-selector', standalone: false, template: "<div class=\"fts-container\">\r\n\r\n <!-- \u2500\u2500 Modal Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"fts-header\" *ngIf=\"config?.title\">\r\n <h2 class=\"fts-header__title\">{{ titleLabel }}</h2>\r\n </div>\r\n\r\n <!-- \u2500\u2500 Body: Filter Panel + Table Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"fts-body\" *ngIf=\"config\">\r\n\r\n <!-- LEFT: Filter Panel -->\r\n <aside class=\"fts-filter-panel\" [class.fts-filter-panel--hidden]=\"!filterPanelVisible\">\r\n\r\n <div class=\"fts-filter-panel__header\">\r\n <span class=\"fts-filter-panel__title\">{{ translate('COMMON.FILTER_TABLE.FILTER_BY') || 'Filter By' }}</span>\r\n </div>\r\n\r\n <div class=\"fts-filter-panel__content\">\r\n <!-- SmartForm renders the dynamic filter form. -->\r\n <ng-container *ngIf=\"resolvedFormJson && formVersion >= 0\">\r\n <lib-smart-form [formJson]=\"resolvedFormJson\" [labels]=\"labels\" [initialValues]=\"resolvedInitialValues\"\r\n (valueChange)=\"onFormValueChange($event)\">\r\n </lib-smart-form>\r\n </ng-container>\r\n </div>\r\n\r\n </aside>\r\n\r\n <!-- RIGHT: Table Panel -->\r\n <section class=\"fts-table-panel\">\r\n\r\n <!-- Table toolbar: selection count -->\r\n <!-- <div class=\"fts-table-panel__toolbar\">\r\n <span class=\"fts-selection-count\" *ngIf=\"config.selectionConfig?.selectionCountLabel\">\r\n {{ selectionCountText }}\r\n </span>\r\n </div> -->\r\n\r\n <!-- SmartTable -->\r\n <div class=\"fts-table-wrapper\">\r\n <lib-smart-table *ngIf=\"resolvedTableConfig\" [config]=\"resolvedTableConfig\" [selectedRows]=\"selectedRows\"\r\n (rowSelect)=\"onRowSelect($event)\">\r\n </lib-smart-table>\r\n </div>\r\n\r\n </section>\r\n\r\n </div>\r\n\r\n <!-- \u2500\u2500 Footer Action Bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"fts-footer\">\r\n <div class=\"fts-footer__left\">\r\n <lib-button id=\"fts-clear-filter-btn\" variant=\"outline\" (click)=\"onClearFilter()\">\r\n {{ clearFilterButtonLabel }}\r\n </lib-button>\r\n\r\n <lib-button id=\"fts-apply-filter-btn\" variant=\"primary\" (click)=\"applyCurrentFilter()\">\r\n {{ applyFilterButtonLabel }}\r\n </lib-button>\r\n </div>\r\n\r\n <div class=\"fts-footer__right\">\r\n <lib-button id=\"fts-cancel-btn\" variant=\"outline\" (click)=\"handleCancel()\">\r\n {{ cancelButtonLabel }}\r\n </lib-button>\r\n\r\n <lib-button id=\"fts-submit-btn\" variant=\"primary\"
|
|
10736
|
+
args: [{ selector: 'lib-filter-table-selector', standalone: false, template: "<div class=\"fts-container\">\r\n\r\n <!-- \u2500\u2500 Modal Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"fts-header\" *ngIf=\"config?.title\">\r\n <h2 class=\"fts-header__title\">{{ titleLabel }}</h2>\r\n </div>\r\n\r\n <!-- \u2500\u2500 Body: Filter Panel + Table Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"fts-body\" *ngIf=\"config\">\r\n\r\n <!-- LEFT: Filter Panel -->\r\n <aside class=\"fts-filter-panel\" [class.fts-filter-panel--hidden]=\"!filterPanelVisible\">\r\n\r\n <div class=\"fts-filter-panel__header\">\r\n <span class=\"fts-filter-panel__title\">{{ translate('COMMON.FILTER_TABLE.FILTER_BY') || 'Filter By' }}</span>\r\n </div>\r\n\r\n <div class=\"fts-filter-panel__content\">\r\n <!-- SmartForm renders the dynamic filter form. -->\r\n <ng-container *ngIf=\"resolvedFormJson && formVersion >= 0\">\r\n <lib-smart-form [formJson]=\"resolvedFormJson\" [labels]=\"labels\" [initialValues]=\"resolvedInitialValues\"\r\n (valueChange)=\"onFormValueChange($event)\">\r\n </lib-smart-form>\r\n </ng-container>\r\n </div>\r\n\r\n </aside>\r\n\r\n <!-- RIGHT: Table Panel -->\r\n <section class=\"fts-table-panel\">\r\n\r\n <!-- Table toolbar: selection count -->\r\n <!-- <div class=\"fts-table-panel__toolbar\">\r\n <span class=\"fts-selection-count\" *ngIf=\"config.selectionConfig?.selectionCountLabel\">\r\n {{ selectionCountText }}\r\n </span>\r\n </div> -->\r\n\r\n <!-- SmartTable -->\r\n <div class=\"fts-table-wrapper\">\r\n <lib-smart-table *ngIf=\"resolvedTableConfig\" [config]=\"resolvedTableConfig\" [selectedRows]=\"selectedRows\"\r\n (rowSelect)=\"onRowSelect($event)\">\r\n </lib-smart-table>\r\n </div>\r\n\r\n </section>\r\n\r\n </div>\r\n\r\n <!-- \u2500\u2500 Footer Action Bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <div class=\"fts-footer\">\r\n <div class=\"fts-footer__left\">\r\n <lib-button id=\"fts-clear-filter-btn\" variant=\"outline\" (click)=\"onClearFilter()\">\r\n {{ clearFilterButtonLabel }}\r\n </lib-button>\r\n\r\n <lib-button id=\"fts-apply-filter-btn\" variant=\"primary\" (click)=\"applyCurrentFilter()\">\r\n {{ applyFilterButtonLabel }}\r\n </lib-button>\r\n </div>\r\n\r\n <div class=\"fts-footer__right\">\r\n <lib-button id=\"fts-cancel-btn\" variant=\"outline\" (click)=\"handleCancel()\">\r\n {{ cancelButtonLabel }}\r\n </lib-button>\r\n\r\n <lib-button id=\"fts-submit-btn\" variant=\"primary\" (click)=\"handleSubmit()\">\r\n {{ submitButtonLabel }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n\r\n</div>", styles: [":host{--fts-panel-bg: #ffffff;--fts-filter-panel-width: 300px;--fts-filter-panel-border: 1px solid #dee2e6;--fts-header-bg: #ffffff;--fts-header-border: 1px solid #f1f3f4;--fts-header-height: 60px;--fts-header-padding: 0 24px;--fts-header-title-color: #111827;--fts-header-title-font-size: 1.125rem;--fts-header-title-font-weight: 600;--fts-footer-bg: #ffffff;--fts-footer-border: 1px solid #f1f3f4;--fts-footer-height: 72px;--fts-footer-padding: 0 24px;--fts-footer-gap: 12px;--fts-body-bg: #ffffff;--fts-filter-section-title-color: var(--text-muted, #6b7280);--fts-filter-section-title-font-size: .75rem;--fts-filter-section-title-font-weight: 600;--fts-selection-count-color: var(--text-muted, #6b7280);--fts-selection-count-font-size: .875rem;--fts-toolbar-border: 1px solid #f1f3f4;--fts-divider-color: #f1f3f4;--fts-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, .05);--fts-border-radius: 12px;display:flex;flex-direction:column;flex:1;min-height:0;width:100%}.fts-container{display:flex;flex-direction:column;flex:1;min-height:0;width:100%;overflow:hidden;background-color:var(--fts-panel-bg);border:1px solid var(--border-color, #e0e0e0);border-radius:var(--fts-border-radius);box-shadow:0 4px 20px #00000014}.fts-header{display:flex;align-items:center;min-height:var(--fts-header-height);padding:var(--fts-header-padding);border-bottom:var(--fts-header-border);background-color:var(--fts-header-bg);flex-shrink:0}.fts-header__title{margin:0;font-size:var(--fts-header-title-font-size);font-weight:var(--fts-header-title-font-weight);color:var(--fts-header-title-color);letter-spacing:-.01em}.fts-body{display:flex;flex:1;min-height:0;background-color:var(--fts-body-bg)}.fts-filter-panel{width:var(--fts-filter-panel-width);min-width:var(--fts-filter-panel-width);display:flex;flex-direction:column;overflow:hidden;background-color:#fcfcfd;border-right:1px solid var(--fts-divider-color)}.fts-filter-panel--hidden{display:none}.fts-filter-panel__header{padding:20px 20px 12px;flex-shrink:0}.fts-filter-panel__title{font-size:var(--fts-filter-section-title-font-size);font-weight:var(--fts-filter-section-title-font-weight);color:var(--fts-filter-section-title-color);text-transform:uppercase;letter-spacing:.05em}.fts-filter-panel__content{flex:1;min-height:0;overflow-y:auto;padding:0}.fts-filter-panel__content::-webkit-scrollbar{width:4px}.fts-filter-panel__content::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:10px}.fts-filter-panel ::ng-deep .smart-form-container,.fts-filter-panel ::ng-deep .smart-form-wrapper{padding:0}.fts-filter-panel ::ng-deep .form-header,.fts-filter-panel ::ng-deep .form-actions{display:none}.fts-filter-panel ::ng-deep .form-section{padding:0}.fts-filter-panel ::ng-deep .form-field-wrapper{margin-bottom:16px}.fts-filter-panel ::ng-deep .form-field-wrapper:last-child{margin-bottom:0}.fts-filter-panel ::ng-deep .field-label{font-size:12px;font-weight:500;color:#374151;margin-bottom:4px}.fts-filter-panel ::ng-deep input,.fts-filter-panel ::ng-deep select,.fts-filter-panel ::ng-deep .mat-select-trigger{font-size:13px!important;border-color:#e5e7eb!important;background-color:#fff!important}.fts-filter-panel ::ng-deep input:focus,.fts-filter-panel ::ng-deep select:focus,.fts-filter-panel ::ng-deep .mat-select-trigger:focus{border-color:var(--primary-color)!important;box-shadow:0 0 0 2px #c21e250d!important}.form-section-container .section-fields.sf-grid{gap:2px!important}.fts-table-panel{flex:1;min-width:0;display:flex;flex-direction:column;overflow:hidden;background-color:var(--fts-panel-bg)}.fts-table-panel__toolbar{display:flex;align-items:center;justify-content:flex-end;flex-shrink:0;min-height:48px;padding:0 24px;background-color:var(--fts-panel-bg)}.fts-table-wrapper{flex:1;min-height:0;display:flex;flex-direction:column;overflow-y:auto}.fts-table-wrapper ::ng-deep .smart-table-outer{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.fts-table-wrapper ::ng-deep .smart-table-wrapper{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.fts-table-wrapper ::ng-deep .st-table-container{flex:1;min-height:0;overflow-x:auto;overflow-y:auto!important}.fts-table-wrapper ::ng-deep .st-table-container::-webkit-scrollbar{width:6px}.fts-table-wrapper ::ng-deep .st-table-container::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:3px}.fts-table-wrapper ::ng-deep .st-pagination{flex-shrink:0;padding:12px 24px;border-top:1px solid var(--fts-divider-color);background-color:var(--fts-panel-bg)}.fts-selection-count{font-size:13px;color:#4b5563;white-space:nowrap;background:#f3f4f6;padding:4px 14px;border-radius:100px;font-weight:500;border:1px solid #e5e7eb}.fts-footer{display:flex;align-items:center;justify-content:space-between;min-height:var(--fts-footer-height);padding:var(--fts-footer-padding);border-top:var(--fts-footer-border);flex-shrink:0;background-color:var(--fts-footer-bg);box-shadow:0 -4px 12px #0000000d;z-index:10}.fts-footer__left,.fts-footer__right{display:flex;align-items:center;gap:var(--fts-footer-gap)}.fts-footer ::ng-deep lib-button button{min-width:100px;font-weight:500;letter-spacing:.01em}::ng-deep .cc-modal-footer{display:none!important}::ng-deep .cc-modal-body:has(lib-filter-table-selector){padding:0!important;overflow:hidden!important;display:flex!important;flex-direction:column!important}\n"] }]
|
|
10656
10737
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i0.NgZone }], propDecorators: { config: [{
|
|
10657
10738
|
type: Input
|
|
10658
10739
|
}], labels: [{
|