commons-shared-web-ui 0.0.83 → 0.0.85

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.
@@ -255,6 +255,39 @@ class SmartFormController {
255
255
  suffixActionClick$ = new Subject();
256
256
  /** Selected options details passed dynamically from parent component */
257
257
  selectedOptionsData = {};
258
+ /**
259
+ * Repeater sections register themselves here so SmartFormComponent can reach them
260
+ * at submit time. A @ViewChildren query can't: the fields are rendered inside
261
+ * FormSectionComponent's template, which is a separate view, so the query never
262
+ * matches them.
263
+ */
264
+ repeaters = new Set();
265
+ /**
266
+ * Set on the per-row controllers a repeater creates for its instances. Registrations
267
+ * walk up this chain so a repeater nested inside another repeater's row is still
268
+ * visible to the root form.
269
+ */
270
+ parent;
271
+ /** The controller the root SmartFormComponent owns — this one unless it is a row controller. */
272
+ get rootController() {
273
+ let node = this;
274
+ const seen = new Set();
275
+ while (node.parent && !seen.has(node)) {
276
+ seen.add(node);
277
+ node = node.parent;
278
+ }
279
+ return node;
280
+ }
281
+ registerRepeater(host) {
282
+ this.rootController.repeaters.add(host);
283
+ }
284
+ unregisterRepeater(host) {
285
+ this.rootController.repeaters.delete(host);
286
+ }
287
+ /** Every repeater currently rendered under the root form. */
288
+ get repeaterHosts() {
289
+ return Array.from(this.rootController.repeaters);
290
+ }
258
291
  initialize(initialData) {
259
292
  this.formData = { ...initialData };
260
293
  this.fieldSubjects.forEach((subject, key) => {
@@ -289,6 +322,7 @@ class SmartFormController {
289
322
  destroy() {
290
323
  this.fieldSubjects.forEach(subject => subject.complete());
291
324
  this.fieldSubjects.clear();
325
+ this.repeaters.clear();
292
326
  }
293
327
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartFormController, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
294
328
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartFormController });
@@ -1842,6 +1876,8 @@ class FormFieldComponent {
1842
1876
  if (this.config.sectionConfig?.allowMulti) {
1843
1877
  this.groupFormArray = this.fb.array([]);
1844
1878
  this.formGroup.addControl(this.groupKey, this.groupFormArray);
1879
+ // Let the root form find this repeater at submit time (see SmartFormController.registerRepeater).
1880
+ this.controller.registerRepeater(this);
1845
1881
  const initialData = this.controller.getFieldValue(this.groupKey);
1846
1882
  if (Array.isArray(initialData) && initialData.length > 0) {
1847
1883
  initialData.forEach((item) => {
@@ -1907,6 +1943,9 @@ class FormFieldComponent {
1907
1943
  // This allows cascading dropdowns (dependencies) to work within each repeater row
1908
1944
  // without cross-row data collisions.
1909
1945
  const rowController = new SmartFormController();
1946
+ // Keeps the chain to the root controller intact so a repeater nested inside this
1947
+ // row still registers where SmartFormComponent looks for it.
1948
+ rowController.parent = this.controller;
1910
1949
  rowController.token = this.controller.token;
1911
1950
  rowController.tokenHeader = this.controller.tokenHeader;
1912
1951
  rowController.labels = this.controller.labels;
@@ -1968,6 +2007,67 @@ class FormFieldComponent {
1968
2007
  }
1969
2008
  }
1970
2009
  }
2010
+ /**
2011
+ * Saves any multi-save entry the user filled in but never pressed Save on.
2012
+ *
2013
+ * The card UI only pushes an entry into `groupFormArray` once Save is clicked, so an
2014
+ * entry left open in the editor is invisible to the root form: its values never reach
2015
+ * the payload and its required fields can't block the submit. SmartForm calls this
2016
+ * before validating, which makes an open entry behave exactly as if Save had been
2017
+ * pressed — complete entries are committed, incomplete ones stop the submit.
2018
+ *
2019
+ * Entries the user never touched are skipped: every multi-save section renders one
2020
+ * blank editor by default, and that blank must not make the whole form unsubmittable.
2021
+ *
2022
+ * @returns true when an entry has content but is still invalid — it stays open with
2023
+ * its field errors and the section-level message on screen.
2024
+ */
2025
+ commitPendingInstances() {
2026
+ if (!this.isGroup || !this.config.sectionConfig?.multiSaveConfig?.active)
2027
+ return false;
2028
+ let blocked = false;
2029
+ // Snapshot: saveGroupInstance() mutates instanceList entries in place, and a commit
2030
+ // must not be skipped because the list was re-assigned mid-iteration.
2031
+ [...this.instanceList].forEach(instance => {
2032
+ if (!instance.isEditing || this.isIgnorableBlank(instance))
2033
+ return;
2034
+ instance.fg.markAllAsTouched();
2035
+ if (instance.fg.invalid) {
2036
+ this.multiSaveError =
2037
+ this.controller.labels?.['ERR_MSG_EMPTY_MULTI'] || 'Please fill the required fields before saving.';
2038
+ blocked = true;
2039
+ return;
2040
+ }
2041
+ this.saveGroupInstance(this.instanceList.indexOf(instance));
2042
+ });
2043
+ // Still short of the minimum with no editor open — the user saved then deleted every
2044
+ // entry. Re-open one so the requirement lands on actual fields instead of a message
2045
+ // pointing at an empty section.
2046
+ if (this.groupFormArray.length < this.minEntries && !this.instanceList.some(i => i.isEditing)) {
2047
+ this.addGroupInstance();
2048
+ this.instanceList[this.instanceList.length - 1].fg.markAllAsTouched();
2049
+ this.multiSaveError =
2050
+ this.controller.labels?.['ERR_MSG_EMPTY_MULTI'] || 'Please fill the required fields before saving.';
2051
+ blocked = true;
2052
+ }
2053
+ return blocked;
2054
+ }
2055
+ /** Saved entries this section must hold before the form can be submitted (0 = optional). */
2056
+ get minEntries() {
2057
+ return this.config.sectionConfig?.multiSaveConfig?.minEntries ?? 0;
2058
+ }
2059
+ /**
2060
+ * True when a blank editor can be passed over at submit time.
2061
+ *
2062
+ * Every multi-save section renders one blank editor, so an optional section (the
2063
+ * default) must ignore it — otherwise its required fields would make the whole form
2064
+ * permanently unsubmittable. A section with `minEntries` set that hasn't reached its
2065
+ * quota is the opposite case: the blank editor IS the entry the user still owes, so
2066
+ * it gets validated and lights up like any other incomplete entry.
2067
+ */
2068
+ isIgnorableBlank(instance) {
2069
+ return this.isInstanceBlank(instance) && this.groupFormArray.length >= this.minEntries;
2070
+ }
1971
2071
  /**
1972
2072
  * Re-opens any repeater instance (multiSave card or standard accordion panel)
1973
2073
  * whose nested FormGroup is invalid, so errors hidden by a collapsed card/panel
@@ -1980,6 +2080,12 @@ class FormFieldComponent {
1980
2080
  const isMultiSave = !!this.config.sectionConfig?.multiSaveConfig?.active;
1981
2081
  let foundInvalid = false;
1982
2082
  this.instanceList.forEach((instance, i) => {
2083
+ // An untouched blank editor in an optional section is not an error — the section
2084
+ // always renders one, and flagging it would paint a red "required" on an entry the
2085
+ // user never opened whenever the submit was blocked by some unrelated field
2086
+ // elsewhere on the form. A section still short of its minEntries is exempt.
2087
+ if (isMultiSave && this.isIgnorableBlank(instance))
2088
+ return;
1983
2089
  instance.fg.markAllAsTouched();
1984
2090
  if (instance.fg.invalid) {
1985
2091
  foundInvalid = true;
@@ -1993,6 +2099,38 @@ class FormFieldComponent {
1993
2099
  });
1994
2100
  return foundInvalid;
1995
2101
  }
2102
+ /**
2103
+ * True when the user has put nothing into this repeater entry.
2104
+ *
2105
+ * Value-based rather than `fg.pristine`, because several fields here (date picker,
2106
+ * rich text, the custom dropdown overlays) write through `setValue`, which never
2107
+ * marks the control dirty. `dirty` is still honoured as a second signal so a field
2108
+ * whose value round-trips back to blank still counts as touched.
2109
+ */
2110
+ isInstanceBlank(instance) {
2111
+ if (instance.isSaved)
2112
+ return false;
2113
+ if (instance.fg.dirty)
2114
+ return false;
2115
+ return Object.values(instance.fg.value ?? {}).every(v => FormFieldComponent.isBlankValue(v));
2116
+ }
2117
+ /** Blank = nothing a user could have entered. `false` counts as blank so an unchecked
2118
+ * CHECKBOX (or one defaulted to false) doesn't make an otherwise empty entry look filled. */
2119
+ static isBlankValue(value) {
2120
+ if (value === null || value === undefined || value === false)
2121
+ return true;
2122
+ if (typeof value === 'string') {
2123
+ // Strip markup so an "empty" rich text editor (Quill leaves <p><br></p>) reads as blank.
2124
+ return value.replace(/<[^>]*>/g, '').replace(/&nbsp;/g, ' ').trim() === '';
2125
+ }
2126
+ if (Array.isArray(value))
2127
+ return value.length === 0;
2128
+ if (value instanceof Date)
2129
+ return false;
2130
+ if (typeof value === 'object')
2131
+ return Object.values(value).every(v => FormFieldComponent.isBlankValue(v));
2132
+ return false;
2133
+ }
1996
2134
  cancelGroupInstance(index) {
1997
2135
  const instance = this.instanceList[index];
1998
2136
  if (!instance.isSaved) {
@@ -2083,6 +2221,7 @@ class FormFieldComponent {
2083
2221
  this.destroy$.complete();
2084
2222
  if (this.isGroup) {
2085
2223
  if (this.config.sectionConfig?.allowMulti) {
2224
+ this.controller.unregisterRepeater(this);
2086
2225
  this.instanceList.forEach(inst => inst.rowController?.destroy());
2087
2226
  }
2088
2227
  if (this.formGroup?.contains(this.groupKey)) {
@@ -5387,6 +5526,9 @@ class SmartFormComponent {
5387
5526
  this.handleSubmit();
5388
5527
  break;
5389
5528
  case 'draft':
5529
+ // Pick up any repeater entry the user filled in but left open, so the draft keeps
5530
+ // it. An incomplete one stays open — a draft shouldn't be blocked on it.
5531
+ this.commitOpenEntries();
5390
5532
  this.submitToApi(this.collectFormData(), 'draft', btn);
5391
5533
  break;
5392
5534
  case 'navigate':
@@ -5600,14 +5742,37 @@ class SmartFormComponent {
5600
5742
  });
5601
5743
  return result;
5602
5744
  }
5745
+ /**
5746
+ * Saves every multi-save repeater entry (Event Agenda, FAQs, …) that the user filled in
5747
+ * but left open in its editor, so those values are part of `collectFormData()` even
5748
+ * though the entry's own Save button was never clicked. An entry still missing a
5749
+ * required field is left open with its errors showing rather than committed half-done.
5750
+ *
5751
+ * `validate()` calls this already. Hosts that collect form data on their own — a
5752
+ * "Save as draft" button in an external footer, say — should call it first.
5753
+ *
5754
+ * @returns true when an open entry is incomplete.
5755
+ */
5756
+ commitOpenEntries() {
5757
+ let blocked = false;
5758
+ this.controller.repeaterHosts.forEach(host => {
5759
+ if (host.commitPendingInstances())
5760
+ blocked = true;
5761
+ });
5762
+ return blocked;
5763
+ }
5603
5764
  validate() {
5604
- if (this.formGroup.invalid) {
5765
+ // A multi-save repeater entry the user typed into but never pressed Save on is not
5766
+ // part of formGroup yet. Commit the complete ones so their values reach the payload,
5767
+ // and let an incomplete one block the submit with its own errors on screen.
5768
+ const blockedByOpenEntry = this.commitOpenEntries();
5769
+ if (this.formGroup.invalid || blockedByOpenEntry) {
5605
5770
  this.formGroup.markAllAsTouched();
5606
5771
  // Re-open any repeater card/accordion entry whose nested fields are invalid,
5607
5772
  // so its error messages (otherwise hidden behind a collapsed panel) become visible.
5608
5773
  let hadHiddenInvalidEntry = false;
5609
- this.formFieldComponents?.forEach(field => {
5610
- if (field.revealInvalidInstances())
5774
+ this.controller.repeaterHosts.forEach(host => {
5775
+ if (host.revealInvalidInstances())
5611
5776
  hadHiddenInvalidEntry = true;
5612
5777
  });
5613
5778
  const message = hadHiddenInvalidEntry
@@ -12159,6 +12324,10 @@ class FilterSidebarComponent {
12159
12324
  }
12160
12325
  return styles;
12161
12326
  }
12327
+ /** `cc-filter-header cc-filter-header--light|--dark` — light (white bar, black title) is the default. */
12328
+ get headerClass() {
12329
+ return `cc-filter-header cc-filter-header--${this.config?.header?.theme || 'light'}`;
12330
+ }
12162
12331
  trackByFn(index, item) {
12163
12332
  return item.key || index;
12164
12333
  }
@@ -12169,7 +12338,7 @@ class FilterSidebarComponent {
12169
12338
  useExisting: forwardRef(() => FilterSidebarComponent),
12170
12339
  multi: true
12171
12340
  }
12172
- ], ngImport: i0, template: "<div class=\"cc-filter-sidebar\" [ngStyle]=\"sidebarStyles\" [class.collapsed]=\"isCollapsed\">\r\n\r\n <!-- Header -->\r\n <div class=\"cc-filter-header\" *ngIf=\"config.header?.visible !== false\">\r\n <div class=\"cc-header-content\">\r\n <span class=\"cc-header-title\" *ngIf=\"config.header?.title\">\r\n <span *ngIf=\"config.header?.icon\" [class]=\"config.header?.icon\" class=\"cc-header-icon material-icons\">\r\n <!-- {{config.header?.icon }} -->\r\n </span>\r\n {{ config.header?.title }}\r\n </span>\r\n <ng-content select=\"[header-actions]\"></ng-content>\r\n </div>\r\n <button type=\"button\" *ngIf=\"config.settings?.collapsible\" (click)=\"toggleCollapse()\" class=\"cc-icon-btn\"\r\n [attr.aria-label]=\"isCollapsed ? config.labels?.expandAriaLabel : config.labels?.collapseAriaLabel\">\r\n <span class=\"material-icons\">{{ isCollapsed ? 'menu_open' : 'menu' }}</span>\r\n </button>\r\n <button type=\"button\" *ngIf=\"config.header?.showClose && !isCollapsed\" (click)=\"onClose()\"\r\n class=\"cc-icon-btn cc-close-btn\" [attr.aria-label]=\"config.labels?.closeAriaLabel\">\r\n <span class=\"material-icons\">close</span>\r\n </button>\r\n <!-- Code snippet debug (optional) -->\r\n <button type=\"button\" *ngIf=\"config.settings?.showCodeSnippet\" (click)=\"onShowCodeSnippet()\" class=\"cc-icon-btn\"\r\n [attr.aria-label]=\"config.labels?.codeSnippetAriaLabel\">\r\n <span class=\"material-icons\">code</span>\r\n </button>\r\n </div>\r\n\r\n <!-- Content (Scrollable) -->\r\n <div class=\"cc-filter-content\" [class.hidden]=\"isCollapsed\">\r\n <ng-container *ngFor=\"let item of config.items; trackBy: trackByFn\">\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: item }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"cc-filter-footer\" *ngIf=\"config.footer?.visible !== false && !isCollapsed\">\r\n <lib-button variant=\"outline\" color=\"warn\" class=\"cc-btn-clear\"\r\n *ngIf=\"config.footer?.clearButton?.visible !== false\" (click)=\"clearFilters()\">\r\n {{ config.footer?.clearButton?.label }}\r\n </lib-button>\r\n <lib-button variant=\"primary\" color=\"warn\" class=\"cc-btn-apply\"\r\n *ngIf=\"config.footer?.applyButton?.visible !== false\" [disabled]=\"config.footer?.applyButton?.disabled ?? false\"\r\n (click)=\"applyFilters()\">\r\n {{ config.footer?.applyButton?.label }}\r\n </lib-button>\r\n </div>\r\n</div>\r\n\r\n<!-- Recursive Template for Items -->\r\n<ng-template #itemTemplate let-item=\"item\">\r\n <div class=\"cc-filter-item\" [ngClass]=\"['type-' + item.type]\"\r\n [style.display]=\"item.visible === false ? 'none' : 'block'\" [ngStyle]=\"item.styles\">\r\n\r\n <ng-container [ngSwitch]=\"item.type\">\r\n\r\n <!-- Input -->\r\n <lib-input *ngSwitchCase=\"'input'\" [config]=\"item.inputConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (valueChange)=\"onValueChange(item.key, $event)\">\r\n </lib-input>\r\n\r\n <!-- Dropdown -->\r\n <lib-dropdown *ngSwitchCase=\"'dropdown'\" [config]=\"item.dropdownConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (selectionChange)=\"onValueChange(item.key, $event)\">\r\n </lib-dropdown>\r\n\r\n <!-- Checkbox -->\r\n <lib-checkbox *ngSwitchCase=\"'checkbox'\" [config]=\"item.checkboxConfig\" [label]=\"item.label\"\r\n [checked]=\"!!filters[item.key]\" (checkedChange)=\"onValueChange(item.key, $event)\">\r\n </lib-checkbox>\r\n\r\n <!-- Radio -->\r\n <lib-radio *ngSwitchCase=\"'radio'\" [config]=\"item.radioConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (selectionChange)=\"onValueChange(item.key, $event)\">\r\n </lib-radio>\r\n\r\n <!-- Toggle -->\r\n <lib-toggle *ngSwitchCase=\"'toggle'\" [config]=\"item.toggleConfig\" [label]=\"item.label\"\r\n [checked]=\"filters[item.key]\" (toggleChange)=\"onValueChange(item.key, $event)\">\r\n </lib-toggle>\r\n\r\n <!-- Datepicker -->\r\n <lib-datepicker *ngSwitchCase=\"'datepicker'\" [config]=\"item.datepickerConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (dateChange)=\"onValueChange(item.key, $event)\">\r\n </lib-datepicker>\r\n\r\n <!-- Search -->\r\n <lib-search *ngSwitchCase=\"'active-search'\" [config]=\"item.searchConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (search)=\"onValueChange(item.key, $event)\">\r\n </lib-search>\r\n\r\n <!-- Group / Accordion -->\r\n <div *ngSwitchCase=\"'group'\" class=\"cc-accordion-item\" [class.expanded]=\"item.expanded\">\r\n <button type=\"button\" class=\"cc-accordion-header\" (click)=\"item.expanded = !item.expanded\">\r\n <span class=\"cc-accordion-title\">{{ item.label }}</span>\r\n <span class=\"material-icons cc-accordion-icon\">{{ item.expanded ? 'expand_less' : 'expand_more'\r\n }}</span>\r\n </button>\r\n\r\n <div class=\"cc-accordion-body\" *ngIf=\"item.expanded\">\r\n <ng-container *ngFor=\"let child of item.children; trackBy: trackByFn\">\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: child }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <!-- Custom Divider -->\r\n <div *ngSwitchCase=\"'divider'\" class=\"cc-filter-divider\"></div>\r\n\r\n </ng-container>\r\n </div>\r\n</ng-template>", styles: [":host{display:block;height:100%;--cc-filter-sidebar-width: 100%;--cc-filter-sidebar-padding: 1.5rem;--cc-filter-sidebar-gap: 1.5rem;--cc-filter-sidebar-bg: #FEFEFE;--cc-filter-sidebar-border-radius: 0;--cc-filter-header-height: 3rem;--cc-filter-header-title-size: 1.125rem;--cc-filter-header-title-weight: 500;--cc-filter-header-title-color: #202124;--cc-filter-footer-padding: 1rem 0 0 0;--cc-filter-footer-gap: 1rem}.cc-filter-sidebar{display:flex;flex-direction:column;height:100%;width:var(--cc-filter-sidebar-width, 280px);background-color:var(--cc-filter-sidebar-bg, #FEFEFE);padding:var(--cc-filter-sidebar-padding, 1.5rem);box-sizing:border-box;overflow:hidden;border-radius:var(--cc-filter-sidebar-border-radius, 0);transition:width .3s ease,padding .3s ease}.cc-filter-sidebar.collapsed{width:60px!important;padding:1.5rem .5rem}.cc-filter-sidebar.collapsed .cc-filter-header{justify-content:center;margin-bottom:0}.cc-filter-sidebar.collapsed .cc-filter-header .cc-header-content,.cc-filter-sidebar.collapsed .cc-filter-header .cc-close-btn,.cc-filter-sidebar.collapsed .cc-filter-content{display:none}.cc-filter-header{display:flex;align-items:center;justify-content:space-between;min-height:var(--cc-filter-header-height, 3rem);margin-bottom:1rem;flex-shrink:0}.cc-filter-header .cc-header-content{display:flex;align-items:center;gap:.5rem;flex:1}.cc-filter-header .cc-header-title{font-size:var(--cc-filter-header-title-size, 1.125rem);font-weight:var(--cc-filter-header-title-weight, 500);color:var(--cc-filter-header-title-color, #202124);display:flex;align-items:center;gap:.5rem}.cc-filter-content{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:var(--cc-filter-sidebar-gap, 1.5rem);padding-right:.25rem}.cc-filter-content::-webkit-scrollbar{width:6px}.cc-filter-content::-webkit-scrollbar-thumb{background-color:#e0e0e0;border-radius:3px}.cc-accordion-item{border-bottom:1px solid #E0E0E0}.cc-accordion-item:last-child{border-bottom:none}.cc-accordion-item .cc-accordion-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:.75rem 0;background:none;border:none;cursor:pointer;text-align:left;outline:none}.cc-accordion-item .cc-accordion-header .cc-accordion-title{font-size:.875rem;font-weight:500;color:#202124}.cc-accordion-item .cc-accordion-header .cc-accordion-icon{color:#5f6368;transition:transform .2s}.cc-accordion-item .cc-accordion-body{padding:0 0 1rem;display:flex;flex-direction:column;gap:1rem;animation:slideDown .2s ease-out}@keyframes slideDown{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.cc-group-content{display:flex;flex-direction:column;gap:1rem}.cc-filter-divider{height:1px;background-color:#e0e0e0;margin:.5rem 0}.cc-filter-footer{display:flex;gap:var(--cc-filter-footer-gap, 1rem);padding:var(--cc-filter-footer-padding, 1rem 0 0 0);margin-top:auto;flex-shrink:0}.cc-filter-footer button{flex:1}.cc-icon-btn{background:none;border:none;cursor:pointer;color:#5f6368;padding:.5rem;border-radius:50%;display:flex;align-items:center;justify-content:center}.cc-icon-btn:hover{background-color:#0000000a}.cc-close-btn{margin-right:-.5rem}\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.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: InputComponent, selector: "lib-input", inputs: ["config", "labels", "type", "label", "placeholder", "disabled", "required", "readonly", "clearable", "maxLength", "minLength", "min", "max", "pattern", "errorMessage", "helperText", "rows", "prefixIcon", "suffixIcon", "value", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["valueChange", "inputBlur", "inputFocus"] }, { kind: "component", type: DropdownComponent, selector: "lib-dropdown", inputs: ["config", "labels", "options", "selectedValue", "placeholder", "label", "multiple", "searchable", "clearable", "disabled", "required", "errorMessage", "serverSearch", "searchDebounceMs", "lazyLoad", "loading", "loadingMore", "hasMore", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["selectionChange", "searchTermChange", "loadMore", "opened"] }, { kind: "component", type: CheckboxComponent, selector: "lib-checkbox", inputs: ["config", "labels", "label", "checked", "disabled", "required", "indeterminate", "options", "labelPosition", "color", "borderRadius", "value", "errorMessage", "width", "height", "fontSize", "fontWeight", "labelColor", "labelFontSize", "labelFontWeight", "gap", "fontFamily", "backgroundColor", "borderColor", "borderWidth", "padding", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow", "size", "checkedColor", "uncheckedColor", "groupLabelColor", "groupLabelFontSize", "groupLabelFontWeight"], outputs: ["checkedChange"] }, { kind: "component", type: RadioComponent, selector: "lib-radio", inputs: ["config", "label", "options", "disabled", "required", "labelPosition", "color", "layout", "labels", "gap", "labelColor", "checkedColor", "uncheckedColor", "fontSize", "fontWeight", "fontFamily", "groupLabelColor", "groupLabelFontSize", "groupLabelFontWeight", "disabledColor", "errorColor", "size", "borderRadius", "labelFontSize", "labelFontWeight"], outputs: ["selectionChange"] }, { kind: "component", type: ToggleComponent, selector: "lib-toggle", inputs: ["config", "labels", "label", "checked", "disabled", "required", "labelPosition", "color", "labelColor", "uncheckedColor", "checkedColor", "thumbColor", "checkedThumbColor", "fontSize", "fontWeight", "fontFamily", "toggleWidth", "toggleHeight", "gap", "sliderColor", "labelFontSize", "labelFontWeight", "disabledColor"], outputs: ["toggleChange"] }, { kind: "component", type: DatepickerComponent, selector: "lib-datepicker", inputs: ["config", "labels", "label", "placeholder", "disabled", "required", "minDate", "maxDate", "isRange", "startView", "value", "startDate", "endDate", "errorMessage", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["dateChange"] }, { kind: "component", type: SearchComponent, selector: "lib-search", inputs: ["config", "labels", "placeholder", "label", "disabled", "debounceMs", "clearable", "value", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "border", "padding", "fontWeight", "color", "textColor", "iconColor", "placeholderColor", "focusBorderColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["search", "clear"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }] });
12341
+ ], ngImport: i0, template: "<div class=\"cc-filter-sidebar\" [ngStyle]=\"sidebarStyles\" [class.collapsed]=\"isCollapsed\">\r\n\r\n <!-- Header -->\r\n <div [ngClass]=\"headerClass\" *ngIf=\"config.header?.visible !== false\"\r\n [style.background-color]=\"config.header?.backgroundColor\" [style.border-bottom]=\"config.header?.borderBottom\">\r\n <div class=\"cc-header-content\">\r\n <span class=\"cc-header-title\" *ngIf=\"config.header?.title\" [style.color]=\"config.header?.textColor\">\r\n <span *ngIf=\"config.header?.icon\" [class]=\"config.header?.icon\" class=\"cc-header-icon material-icons\">\r\n <!-- {{config.header?.icon }} -->\r\n </span>\r\n {{ config.header?.title }}\r\n </span>\r\n <ng-content select=\"[header-actions]\"></ng-content>\r\n </div>\r\n <button type=\"button\" *ngIf=\"config.settings?.collapsible\" (click)=\"toggleCollapse()\" class=\"cc-icon-btn\"\r\n [attr.aria-label]=\"isCollapsed ? config.labels?.expandAriaLabel : config.labels?.collapseAriaLabel\">\r\n <span class=\"material-icons\">{{ isCollapsed ? 'menu_open' : 'menu' }}</span>\r\n </button>\r\n <button type=\"button\" *ngIf=\"config.header?.showClose && !isCollapsed\" (click)=\"onClose()\"\r\n class=\"cc-icon-btn cc-close-btn\" [attr.aria-label]=\"config.labels?.closeAriaLabel\">\r\n <span class=\"material-icons\">close</span>\r\n </button>\r\n <!-- Code snippet debug (optional) -->\r\n <button type=\"button\" *ngIf=\"config.settings?.showCodeSnippet\" (click)=\"onShowCodeSnippet()\" class=\"cc-icon-btn\"\r\n [attr.aria-label]=\"config.labels?.codeSnippetAriaLabel\">\r\n <span class=\"material-icons\">code</span>\r\n </button>\r\n </div>\r\n\r\n <!-- Content (Scrollable) -->\r\n <div class=\"cc-filter-content\" [class.hidden]=\"isCollapsed\">\r\n <ng-container *ngFor=\"let item of config.items; trackBy: trackByFn\">\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: item }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"cc-filter-footer\" *ngIf=\"config.footer?.visible !== false && !isCollapsed\">\r\n <lib-button variant=\"outline\" color=\"warn\" class=\"cc-btn-clear\"\r\n *ngIf=\"config.footer?.clearButton?.visible !== false\" (click)=\"clearFilters()\">\r\n {{ config.footer?.clearButton?.label }}\r\n </lib-button>\r\n <lib-button variant=\"primary\" color=\"warn\" class=\"cc-btn-apply\"\r\n *ngIf=\"config.footer?.applyButton?.visible !== false\" [disabled]=\"config.footer?.applyButton?.disabled ?? false\"\r\n (click)=\"applyFilters()\">\r\n {{ config.footer?.applyButton?.label }}\r\n </lib-button>\r\n </div>\r\n</div>\r\n\r\n<!-- Recursive Template for Items -->\r\n<ng-template #itemTemplate let-item=\"item\">\r\n <div class=\"cc-filter-item\" [ngClass]=\"['type-' + item.type]\"\r\n [style.display]=\"item.visible === false ? 'none' : 'block'\" [ngStyle]=\"item.styles\">\r\n\r\n <ng-container [ngSwitch]=\"item.type\">\r\n\r\n <!-- Input -->\r\n <lib-input *ngSwitchCase=\"'input'\" [config]=\"item.inputConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (valueChange)=\"onValueChange(item.key, $event)\">\r\n </lib-input>\r\n\r\n <!-- Dropdown -->\r\n <lib-dropdown *ngSwitchCase=\"'dropdown'\" [config]=\"item.dropdownConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (selectionChange)=\"onValueChange(item.key, $event)\">\r\n </lib-dropdown>\r\n\r\n <!-- Checkbox -->\r\n <lib-checkbox *ngSwitchCase=\"'checkbox'\" [config]=\"item.checkboxConfig\" [label]=\"item.label\"\r\n [checked]=\"!!filters[item.key]\" (checkedChange)=\"onValueChange(item.key, $event)\">\r\n </lib-checkbox>\r\n\r\n <!-- Radio -->\r\n <lib-radio *ngSwitchCase=\"'radio'\" [config]=\"item.radioConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (selectionChange)=\"onValueChange(item.key, $event)\">\r\n </lib-radio>\r\n\r\n <!-- Toggle -->\r\n <lib-toggle *ngSwitchCase=\"'toggle'\" [config]=\"item.toggleConfig\" [label]=\"item.label\"\r\n [checked]=\"filters[item.key]\" (toggleChange)=\"onValueChange(item.key, $event)\">\r\n </lib-toggle>\r\n\r\n <!-- Datepicker -->\r\n <lib-datepicker *ngSwitchCase=\"'datepicker'\" [config]=\"item.datepickerConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (dateChange)=\"onValueChange(item.key, $event)\">\r\n </lib-datepicker>\r\n\r\n <!-- Search -->\r\n <lib-search *ngSwitchCase=\"'active-search'\" [config]=\"item.searchConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (search)=\"onValueChange(item.key, $event)\">\r\n </lib-search>\r\n\r\n <!-- Group / Accordion -->\r\n <div *ngSwitchCase=\"'group'\" class=\"cc-accordion-item\" [class.expanded]=\"item.expanded\">\r\n <button type=\"button\" class=\"cc-accordion-header\" (click)=\"item.expanded = !item.expanded\">\r\n <span class=\"cc-accordion-title\">{{ item.label }}</span>\r\n <span class=\"material-icons cc-accordion-icon\">{{ item.expanded ? 'expand_less' : 'expand_more'\r\n }}</span>\r\n </button>\r\n\r\n <div class=\"cc-accordion-body\" *ngIf=\"item.expanded\">\r\n <ng-container *ngFor=\"let child of item.children; trackBy: trackByFn\">\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: child }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <!-- Custom Divider -->\r\n <div *ngSwitchCase=\"'divider'\" class=\"cc-filter-divider\"></div>\r\n\r\n </ng-container>\r\n </div>\r\n</ng-template>", styles: [":host{display:block;height:100%;--cc-filter-sidebar-width: 100%;--cc-filter-sidebar-padding: 1.5rem;--cc-filter-sidebar-gap: 1.5rem;--cc-filter-sidebar-bg: #FEFEFE;--cc-filter-sidebar-border-radius: 0;--cc-filter-header-height: 3rem;--cc-filter-header-padding: .5rem 1.5rem;--cc-filter-header-title-size: 1.125rem;--cc-filter-header-title-weight: 500;--cc-filter-header-title-color: #202124;--cc-filter-header-bg-dark: #262125;--cc-filter-header-title-color-dark: #FFFFFF;--cc-filter-header-icon-color-dark: rgba(255, 255, 255, .7);--cc-filter-header-bg-light: #FFFFFF;--cc-filter-header-border-bottom-light: 1px solid #E0E0E0;--cc-filter-footer-padding: 1rem 1.5rem 1.5rem;--cc-filter-footer-gap: 1rem}.cc-filter-sidebar{display:flex;flex-direction:column;height:100%;width:var(--cc-filter-sidebar-width, 280px);background-color:var(--cc-filter-sidebar-bg, #FEFEFE);box-sizing:border-box;overflow:hidden;border-radius:var(--cc-filter-sidebar-border-radius, 0);transition:width .3s ease,padding .3s ease}.cc-filter-sidebar.collapsed{width:60px!important}.cc-filter-sidebar.collapsed .cc-filter-header{justify-content:center;padding:1rem .5rem}.cc-filter-sidebar.collapsed .cc-filter-header .cc-header-content,.cc-filter-sidebar.collapsed .cc-filter-header .cc-close-btn,.cc-filter-sidebar.collapsed .cc-filter-content{display:none}.cc-filter-header{display:flex;align-items:center;justify-content:space-between;min-height:var(--cc-filter-header-height, 3rem);padding:var(--cc-filter-header-padding, .5rem 1.5rem);flex-shrink:0}.cc-filter-header .cc-header-content{display:flex;align-items:center;gap:.5rem;flex:1}.cc-filter-header .cc-header-title{font-size:var(--cc-filter-header-title-size, 1.125rem);font-weight:var(--cc-filter-header-title-weight, 500);color:var(--cc-filter-header-title-color, #202124);display:flex;align-items:center;gap:.5rem}.cc-filter-header--dark{background-color:var(--cc-filter-header-bg-dark, #262125);border-bottom:none}.cc-filter-header--dark .cc-header-title{color:var(--cc-filter-header-title-color-dark, #FFFFFF)}.cc-filter-header--dark .cc-icon-btn{color:var(--cc-filter-header-icon-color-dark, rgba(255, 255, 255, .7))}.cc-filter-header--dark .cc-icon-btn:hover{color:#fff;background-color:#ffffff1f}.cc-filter-header--light{background-color:var(--cc-filter-header-bg-light, #FFFFFF);border-bottom:var(--cc-filter-header-border-bottom-light, 1px solid #E0E0E0)}.cc-filter-header--light .cc-header-title{color:var(--cc-filter-header-title-color, #202124)}.cc-filter-content{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:var(--cc-filter-sidebar-gap, 1.5rem);padding:var(--cc-filter-sidebar-padding, 1.5rem)}.cc-filter-content::-webkit-scrollbar{width:6px}.cc-filter-content::-webkit-scrollbar-thumb{background-color:#e0e0e0;border-radius:3px}.cc-accordion-item{border-bottom:1px solid #E0E0E0}.cc-accordion-item:last-child{border-bottom:none}.cc-accordion-item .cc-accordion-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:.75rem 0;background:none;border:none;cursor:pointer;text-align:left;outline:none}.cc-accordion-item .cc-accordion-header .cc-accordion-title{font-size:.875rem;font-weight:500;color:#202124}.cc-accordion-item .cc-accordion-header .cc-accordion-icon{color:#5f6368;transition:transform .2s}.cc-accordion-item .cc-accordion-body{padding:0 0 1rem;display:flex;flex-direction:column;gap:1rem;animation:slideDown .2s ease-out}@keyframes slideDown{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.cc-group-content{display:flex;flex-direction:column;gap:1rem}.cc-filter-divider{height:1px;background-color:#e0e0e0;margin:.5rem 0}.cc-filter-footer{display:flex;align-items:center;justify-content:space-between;gap:var(--cc-filter-footer-gap, 1rem);padding:var(--cc-filter-footer-padding, 1rem 1.5rem 1.5rem);margin-top:auto;flex-shrink:0}.cc-filter-footer .cc-btn-apply{margin-left:auto}.cc-icon-btn{background:none;border:none;cursor:pointer;color:#5f6368;padding:.5rem;border-radius:50%;display:flex;align-items:center;justify-content:center}.cc-icon-btn:hover{background-color:#0000000a}.cc-close-btn{margin-right:-.5rem}\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.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: InputComponent, selector: "lib-input", inputs: ["config", "labels", "type", "label", "placeholder", "disabled", "required", "readonly", "clearable", "maxLength", "minLength", "min", "max", "pattern", "errorMessage", "helperText", "rows", "prefixIcon", "suffixIcon", "value", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["valueChange", "inputBlur", "inputFocus"] }, { kind: "component", type: DropdownComponent, selector: "lib-dropdown", inputs: ["config", "labels", "options", "selectedValue", "placeholder", "label", "multiple", "searchable", "clearable", "disabled", "required", "errorMessage", "serverSearch", "searchDebounceMs", "lazyLoad", "loading", "loadingMore", "hasMore", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["selectionChange", "searchTermChange", "loadMore", "opened"] }, { kind: "component", type: CheckboxComponent, selector: "lib-checkbox", inputs: ["config", "labels", "label", "checked", "disabled", "required", "indeterminate", "options", "labelPosition", "color", "borderRadius", "value", "errorMessage", "width", "height", "fontSize", "fontWeight", "labelColor", "labelFontSize", "labelFontWeight", "gap", "fontFamily", "backgroundColor", "borderColor", "borderWidth", "padding", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow", "size", "checkedColor", "uncheckedColor", "groupLabelColor", "groupLabelFontSize", "groupLabelFontWeight"], outputs: ["checkedChange"] }, { kind: "component", type: RadioComponent, selector: "lib-radio", inputs: ["config", "label", "options", "disabled", "required", "labelPosition", "color", "layout", "labels", "gap", "labelColor", "checkedColor", "uncheckedColor", "fontSize", "fontWeight", "fontFamily", "groupLabelColor", "groupLabelFontSize", "groupLabelFontWeight", "disabledColor", "errorColor", "size", "borderRadius", "labelFontSize", "labelFontWeight"], outputs: ["selectionChange"] }, { kind: "component", type: ToggleComponent, selector: "lib-toggle", inputs: ["config", "labels", "label", "checked", "disabled", "required", "labelPosition", "color", "labelColor", "uncheckedColor", "checkedColor", "thumbColor", "checkedThumbColor", "fontSize", "fontWeight", "fontFamily", "toggleWidth", "toggleHeight", "gap", "sliderColor", "labelFontSize", "labelFontWeight", "disabledColor"], outputs: ["toggleChange"] }, { kind: "component", type: DatepickerComponent, selector: "lib-datepicker", inputs: ["config", "labels", "label", "placeholder", "disabled", "required", "minDate", "maxDate", "isRange", "startView", "value", "startDate", "endDate", "errorMessage", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["dateChange"] }, { kind: "component", type: SearchComponent, selector: "lib-search", inputs: ["config", "labels", "placeholder", "label", "disabled", "debounceMs", "clearable", "value", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "border", "padding", "fontWeight", "color", "textColor", "iconColor", "placeholderColor", "focusBorderColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["search", "clear"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }] });
12173
12342
  }
12174
12343
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FilterSidebarComponent, decorators: [{
12175
12344
  type: Component,
@@ -12179,7 +12348,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
12179
12348
  useExisting: forwardRef(() => FilterSidebarComponent),
12180
12349
  multi: true
12181
12350
  }
12182
- ], template: "<div class=\"cc-filter-sidebar\" [ngStyle]=\"sidebarStyles\" [class.collapsed]=\"isCollapsed\">\r\n\r\n <!-- Header -->\r\n <div class=\"cc-filter-header\" *ngIf=\"config.header?.visible !== false\">\r\n <div class=\"cc-header-content\">\r\n <span class=\"cc-header-title\" *ngIf=\"config.header?.title\">\r\n <span *ngIf=\"config.header?.icon\" [class]=\"config.header?.icon\" class=\"cc-header-icon material-icons\">\r\n <!-- {{config.header?.icon }} -->\r\n </span>\r\n {{ config.header?.title }}\r\n </span>\r\n <ng-content select=\"[header-actions]\"></ng-content>\r\n </div>\r\n <button type=\"button\" *ngIf=\"config.settings?.collapsible\" (click)=\"toggleCollapse()\" class=\"cc-icon-btn\"\r\n [attr.aria-label]=\"isCollapsed ? config.labels?.expandAriaLabel : config.labels?.collapseAriaLabel\">\r\n <span class=\"material-icons\">{{ isCollapsed ? 'menu_open' : 'menu' }}</span>\r\n </button>\r\n <button type=\"button\" *ngIf=\"config.header?.showClose && !isCollapsed\" (click)=\"onClose()\"\r\n class=\"cc-icon-btn cc-close-btn\" [attr.aria-label]=\"config.labels?.closeAriaLabel\">\r\n <span class=\"material-icons\">close</span>\r\n </button>\r\n <!-- Code snippet debug (optional) -->\r\n <button type=\"button\" *ngIf=\"config.settings?.showCodeSnippet\" (click)=\"onShowCodeSnippet()\" class=\"cc-icon-btn\"\r\n [attr.aria-label]=\"config.labels?.codeSnippetAriaLabel\">\r\n <span class=\"material-icons\">code</span>\r\n </button>\r\n </div>\r\n\r\n <!-- Content (Scrollable) -->\r\n <div class=\"cc-filter-content\" [class.hidden]=\"isCollapsed\">\r\n <ng-container *ngFor=\"let item of config.items; trackBy: trackByFn\">\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: item }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"cc-filter-footer\" *ngIf=\"config.footer?.visible !== false && !isCollapsed\">\r\n <lib-button variant=\"outline\" color=\"warn\" class=\"cc-btn-clear\"\r\n *ngIf=\"config.footer?.clearButton?.visible !== false\" (click)=\"clearFilters()\">\r\n {{ config.footer?.clearButton?.label }}\r\n </lib-button>\r\n <lib-button variant=\"primary\" color=\"warn\" class=\"cc-btn-apply\"\r\n *ngIf=\"config.footer?.applyButton?.visible !== false\" [disabled]=\"config.footer?.applyButton?.disabled ?? false\"\r\n (click)=\"applyFilters()\">\r\n {{ config.footer?.applyButton?.label }}\r\n </lib-button>\r\n </div>\r\n</div>\r\n\r\n<!-- Recursive Template for Items -->\r\n<ng-template #itemTemplate let-item=\"item\">\r\n <div class=\"cc-filter-item\" [ngClass]=\"['type-' + item.type]\"\r\n [style.display]=\"item.visible === false ? 'none' : 'block'\" [ngStyle]=\"item.styles\">\r\n\r\n <ng-container [ngSwitch]=\"item.type\">\r\n\r\n <!-- Input -->\r\n <lib-input *ngSwitchCase=\"'input'\" [config]=\"item.inputConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (valueChange)=\"onValueChange(item.key, $event)\">\r\n </lib-input>\r\n\r\n <!-- Dropdown -->\r\n <lib-dropdown *ngSwitchCase=\"'dropdown'\" [config]=\"item.dropdownConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (selectionChange)=\"onValueChange(item.key, $event)\">\r\n </lib-dropdown>\r\n\r\n <!-- Checkbox -->\r\n <lib-checkbox *ngSwitchCase=\"'checkbox'\" [config]=\"item.checkboxConfig\" [label]=\"item.label\"\r\n [checked]=\"!!filters[item.key]\" (checkedChange)=\"onValueChange(item.key, $event)\">\r\n </lib-checkbox>\r\n\r\n <!-- Radio -->\r\n <lib-radio *ngSwitchCase=\"'radio'\" [config]=\"item.radioConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (selectionChange)=\"onValueChange(item.key, $event)\">\r\n </lib-radio>\r\n\r\n <!-- Toggle -->\r\n <lib-toggle *ngSwitchCase=\"'toggle'\" [config]=\"item.toggleConfig\" [label]=\"item.label\"\r\n [checked]=\"filters[item.key]\" (toggleChange)=\"onValueChange(item.key, $event)\">\r\n </lib-toggle>\r\n\r\n <!-- Datepicker -->\r\n <lib-datepicker *ngSwitchCase=\"'datepicker'\" [config]=\"item.datepickerConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (dateChange)=\"onValueChange(item.key, $event)\">\r\n </lib-datepicker>\r\n\r\n <!-- Search -->\r\n <lib-search *ngSwitchCase=\"'active-search'\" [config]=\"item.searchConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (search)=\"onValueChange(item.key, $event)\">\r\n </lib-search>\r\n\r\n <!-- Group / Accordion -->\r\n <div *ngSwitchCase=\"'group'\" class=\"cc-accordion-item\" [class.expanded]=\"item.expanded\">\r\n <button type=\"button\" class=\"cc-accordion-header\" (click)=\"item.expanded = !item.expanded\">\r\n <span class=\"cc-accordion-title\">{{ item.label }}</span>\r\n <span class=\"material-icons cc-accordion-icon\">{{ item.expanded ? 'expand_less' : 'expand_more'\r\n }}</span>\r\n </button>\r\n\r\n <div class=\"cc-accordion-body\" *ngIf=\"item.expanded\">\r\n <ng-container *ngFor=\"let child of item.children; trackBy: trackByFn\">\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: child }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <!-- Custom Divider -->\r\n <div *ngSwitchCase=\"'divider'\" class=\"cc-filter-divider\"></div>\r\n\r\n </ng-container>\r\n </div>\r\n</ng-template>", styles: [":host{display:block;height:100%;--cc-filter-sidebar-width: 100%;--cc-filter-sidebar-padding: 1.5rem;--cc-filter-sidebar-gap: 1.5rem;--cc-filter-sidebar-bg: #FEFEFE;--cc-filter-sidebar-border-radius: 0;--cc-filter-header-height: 3rem;--cc-filter-header-title-size: 1.125rem;--cc-filter-header-title-weight: 500;--cc-filter-header-title-color: #202124;--cc-filter-footer-padding: 1rem 0 0 0;--cc-filter-footer-gap: 1rem}.cc-filter-sidebar{display:flex;flex-direction:column;height:100%;width:var(--cc-filter-sidebar-width, 280px);background-color:var(--cc-filter-sidebar-bg, #FEFEFE);padding:var(--cc-filter-sidebar-padding, 1.5rem);box-sizing:border-box;overflow:hidden;border-radius:var(--cc-filter-sidebar-border-radius, 0);transition:width .3s ease,padding .3s ease}.cc-filter-sidebar.collapsed{width:60px!important;padding:1.5rem .5rem}.cc-filter-sidebar.collapsed .cc-filter-header{justify-content:center;margin-bottom:0}.cc-filter-sidebar.collapsed .cc-filter-header .cc-header-content,.cc-filter-sidebar.collapsed .cc-filter-header .cc-close-btn,.cc-filter-sidebar.collapsed .cc-filter-content{display:none}.cc-filter-header{display:flex;align-items:center;justify-content:space-between;min-height:var(--cc-filter-header-height, 3rem);margin-bottom:1rem;flex-shrink:0}.cc-filter-header .cc-header-content{display:flex;align-items:center;gap:.5rem;flex:1}.cc-filter-header .cc-header-title{font-size:var(--cc-filter-header-title-size, 1.125rem);font-weight:var(--cc-filter-header-title-weight, 500);color:var(--cc-filter-header-title-color, #202124);display:flex;align-items:center;gap:.5rem}.cc-filter-content{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:var(--cc-filter-sidebar-gap, 1.5rem);padding-right:.25rem}.cc-filter-content::-webkit-scrollbar{width:6px}.cc-filter-content::-webkit-scrollbar-thumb{background-color:#e0e0e0;border-radius:3px}.cc-accordion-item{border-bottom:1px solid #E0E0E0}.cc-accordion-item:last-child{border-bottom:none}.cc-accordion-item .cc-accordion-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:.75rem 0;background:none;border:none;cursor:pointer;text-align:left;outline:none}.cc-accordion-item .cc-accordion-header .cc-accordion-title{font-size:.875rem;font-weight:500;color:#202124}.cc-accordion-item .cc-accordion-header .cc-accordion-icon{color:#5f6368;transition:transform .2s}.cc-accordion-item .cc-accordion-body{padding:0 0 1rem;display:flex;flex-direction:column;gap:1rem;animation:slideDown .2s ease-out}@keyframes slideDown{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.cc-group-content{display:flex;flex-direction:column;gap:1rem}.cc-filter-divider{height:1px;background-color:#e0e0e0;margin:.5rem 0}.cc-filter-footer{display:flex;gap:var(--cc-filter-footer-gap, 1rem);padding:var(--cc-filter-footer-padding, 1rem 0 0 0);margin-top:auto;flex-shrink:0}.cc-filter-footer button{flex:1}.cc-icon-btn{background:none;border:none;cursor:pointer;color:#5f6368;padding:.5rem;border-radius:50%;display:flex;align-items:center;justify-content:center}.cc-icon-btn:hover{background-color:#0000000a}.cc-close-btn{margin-right:-.5rem}\n"] }]
12351
+ ], template: "<div class=\"cc-filter-sidebar\" [ngStyle]=\"sidebarStyles\" [class.collapsed]=\"isCollapsed\">\r\n\r\n <!-- Header -->\r\n <div [ngClass]=\"headerClass\" *ngIf=\"config.header?.visible !== false\"\r\n [style.background-color]=\"config.header?.backgroundColor\" [style.border-bottom]=\"config.header?.borderBottom\">\r\n <div class=\"cc-header-content\">\r\n <span class=\"cc-header-title\" *ngIf=\"config.header?.title\" [style.color]=\"config.header?.textColor\">\r\n <span *ngIf=\"config.header?.icon\" [class]=\"config.header?.icon\" class=\"cc-header-icon material-icons\">\r\n <!-- {{config.header?.icon }} -->\r\n </span>\r\n {{ config.header?.title }}\r\n </span>\r\n <ng-content select=\"[header-actions]\"></ng-content>\r\n </div>\r\n <button type=\"button\" *ngIf=\"config.settings?.collapsible\" (click)=\"toggleCollapse()\" class=\"cc-icon-btn\"\r\n [attr.aria-label]=\"isCollapsed ? config.labels?.expandAriaLabel : config.labels?.collapseAriaLabel\">\r\n <span class=\"material-icons\">{{ isCollapsed ? 'menu_open' : 'menu' }}</span>\r\n </button>\r\n <button type=\"button\" *ngIf=\"config.header?.showClose && !isCollapsed\" (click)=\"onClose()\"\r\n class=\"cc-icon-btn cc-close-btn\" [attr.aria-label]=\"config.labels?.closeAriaLabel\">\r\n <span class=\"material-icons\">close</span>\r\n </button>\r\n <!-- Code snippet debug (optional) -->\r\n <button type=\"button\" *ngIf=\"config.settings?.showCodeSnippet\" (click)=\"onShowCodeSnippet()\" class=\"cc-icon-btn\"\r\n [attr.aria-label]=\"config.labels?.codeSnippetAriaLabel\">\r\n <span class=\"material-icons\">code</span>\r\n </button>\r\n </div>\r\n\r\n <!-- Content (Scrollable) -->\r\n <div class=\"cc-filter-content\" [class.hidden]=\"isCollapsed\">\r\n <ng-container *ngFor=\"let item of config.items; trackBy: trackByFn\">\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: item }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"cc-filter-footer\" *ngIf=\"config.footer?.visible !== false && !isCollapsed\">\r\n <lib-button variant=\"outline\" color=\"warn\" class=\"cc-btn-clear\"\r\n *ngIf=\"config.footer?.clearButton?.visible !== false\" (click)=\"clearFilters()\">\r\n {{ config.footer?.clearButton?.label }}\r\n </lib-button>\r\n <lib-button variant=\"primary\" color=\"warn\" class=\"cc-btn-apply\"\r\n *ngIf=\"config.footer?.applyButton?.visible !== false\" [disabled]=\"config.footer?.applyButton?.disabled ?? false\"\r\n (click)=\"applyFilters()\">\r\n {{ config.footer?.applyButton?.label }}\r\n </lib-button>\r\n </div>\r\n</div>\r\n\r\n<!-- Recursive Template for Items -->\r\n<ng-template #itemTemplate let-item=\"item\">\r\n <div class=\"cc-filter-item\" [ngClass]=\"['type-' + item.type]\"\r\n [style.display]=\"item.visible === false ? 'none' : 'block'\" [ngStyle]=\"item.styles\">\r\n\r\n <ng-container [ngSwitch]=\"item.type\">\r\n\r\n <!-- Input -->\r\n <lib-input *ngSwitchCase=\"'input'\" [config]=\"item.inputConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (valueChange)=\"onValueChange(item.key, $event)\">\r\n </lib-input>\r\n\r\n <!-- Dropdown -->\r\n <lib-dropdown *ngSwitchCase=\"'dropdown'\" [config]=\"item.dropdownConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (selectionChange)=\"onValueChange(item.key, $event)\">\r\n </lib-dropdown>\r\n\r\n <!-- Checkbox -->\r\n <lib-checkbox *ngSwitchCase=\"'checkbox'\" [config]=\"item.checkboxConfig\" [label]=\"item.label\"\r\n [checked]=\"!!filters[item.key]\" (checkedChange)=\"onValueChange(item.key, $event)\">\r\n </lib-checkbox>\r\n\r\n <!-- Radio -->\r\n <lib-radio *ngSwitchCase=\"'radio'\" [config]=\"item.radioConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (selectionChange)=\"onValueChange(item.key, $event)\">\r\n </lib-radio>\r\n\r\n <!-- Toggle -->\r\n <lib-toggle *ngSwitchCase=\"'toggle'\" [config]=\"item.toggleConfig\" [label]=\"item.label\"\r\n [checked]=\"filters[item.key]\" (toggleChange)=\"onValueChange(item.key, $event)\">\r\n </lib-toggle>\r\n\r\n <!-- Datepicker -->\r\n <lib-datepicker *ngSwitchCase=\"'datepicker'\" [config]=\"item.datepickerConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (dateChange)=\"onValueChange(item.key, $event)\">\r\n </lib-datepicker>\r\n\r\n <!-- Search -->\r\n <lib-search *ngSwitchCase=\"'active-search'\" [config]=\"item.searchConfig\" [label]=\"item.label\"\r\n [ngModel]=\"filters[item.key]\" (search)=\"onValueChange(item.key, $event)\">\r\n </lib-search>\r\n\r\n <!-- Group / Accordion -->\r\n <div *ngSwitchCase=\"'group'\" class=\"cc-accordion-item\" [class.expanded]=\"item.expanded\">\r\n <button type=\"button\" class=\"cc-accordion-header\" (click)=\"item.expanded = !item.expanded\">\r\n <span class=\"cc-accordion-title\">{{ item.label }}</span>\r\n <span class=\"material-icons cc-accordion-icon\">{{ item.expanded ? 'expand_less' : 'expand_more'\r\n }}</span>\r\n </button>\r\n\r\n <div class=\"cc-accordion-body\" *ngIf=\"item.expanded\">\r\n <ng-container *ngFor=\"let child of item.children; trackBy: trackByFn\">\r\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: child }\"></ng-container>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n <!-- Custom Divider -->\r\n <div *ngSwitchCase=\"'divider'\" class=\"cc-filter-divider\"></div>\r\n\r\n </ng-container>\r\n </div>\r\n</ng-template>", styles: [":host{display:block;height:100%;--cc-filter-sidebar-width: 100%;--cc-filter-sidebar-padding: 1.5rem;--cc-filter-sidebar-gap: 1.5rem;--cc-filter-sidebar-bg: #FEFEFE;--cc-filter-sidebar-border-radius: 0;--cc-filter-header-height: 3rem;--cc-filter-header-padding: .5rem 1.5rem;--cc-filter-header-title-size: 1.125rem;--cc-filter-header-title-weight: 500;--cc-filter-header-title-color: #202124;--cc-filter-header-bg-dark: #262125;--cc-filter-header-title-color-dark: #FFFFFF;--cc-filter-header-icon-color-dark: rgba(255, 255, 255, .7);--cc-filter-header-bg-light: #FFFFFF;--cc-filter-header-border-bottom-light: 1px solid #E0E0E0;--cc-filter-footer-padding: 1rem 1.5rem 1.5rem;--cc-filter-footer-gap: 1rem}.cc-filter-sidebar{display:flex;flex-direction:column;height:100%;width:var(--cc-filter-sidebar-width, 280px);background-color:var(--cc-filter-sidebar-bg, #FEFEFE);box-sizing:border-box;overflow:hidden;border-radius:var(--cc-filter-sidebar-border-radius, 0);transition:width .3s ease,padding .3s ease}.cc-filter-sidebar.collapsed{width:60px!important}.cc-filter-sidebar.collapsed .cc-filter-header{justify-content:center;padding:1rem .5rem}.cc-filter-sidebar.collapsed .cc-filter-header .cc-header-content,.cc-filter-sidebar.collapsed .cc-filter-header .cc-close-btn,.cc-filter-sidebar.collapsed .cc-filter-content{display:none}.cc-filter-header{display:flex;align-items:center;justify-content:space-between;min-height:var(--cc-filter-header-height, 3rem);padding:var(--cc-filter-header-padding, .5rem 1.5rem);flex-shrink:0}.cc-filter-header .cc-header-content{display:flex;align-items:center;gap:.5rem;flex:1}.cc-filter-header .cc-header-title{font-size:var(--cc-filter-header-title-size, 1.125rem);font-weight:var(--cc-filter-header-title-weight, 500);color:var(--cc-filter-header-title-color, #202124);display:flex;align-items:center;gap:.5rem}.cc-filter-header--dark{background-color:var(--cc-filter-header-bg-dark, #262125);border-bottom:none}.cc-filter-header--dark .cc-header-title{color:var(--cc-filter-header-title-color-dark, #FFFFFF)}.cc-filter-header--dark .cc-icon-btn{color:var(--cc-filter-header-icon-color-dark, rgba(255, 255, 255, .7))}.cc-filter-header--dark .cc-icon-btn:hover{color:#fff;background-color:#ffffff1f}.cc-filter-header--light{background-color:var(--cc-filter-header-bg-light, #FFFFFF);border-bottom:var(--cc-filter-header-border-bottom-light, 1px solid #E0E0E0)}.cc-filter-header--light .cc-header-title{color:var(--cc-filter-header-title-color, #202124)}.cc-filter-content{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:var(--cc-filter-sidebar-gap, 1.5rem);padding:var(--cc-filter-sidebar-padding, 1.5rem)}.cc-filter-content::-webkit-scrollbar{width:6px}.cc-filter-content::-webkit-scrollbar-thumb{background-color:#e0e0e0;border-radius:3px}.cc-accordion-item{border-bottom:1px solid #E0E0E0}.cc-accordion-item:last-child{border-bottom:none}.cc-accordion-item .cc-accordion-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:.75rem 0;background:none;border:none;cursor:pointer;text-align:left;outline:none}.cc-accordion-item .cc-accordion-header .cc-accordion-title{font-size:.875rem;font-weight:500;color:#202124}.cc-accordion-item .cc-accordion-header .cc-accordion-icon{color:#5f6368;transition:transform .2s}.cc-accordion-item .cc-accordion-body{padding:0 0 1rem;display:flex;flex-direction:column;gap:1rem;animation:slideDown .2s ease-out}@keyframes slideDown{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.cc-group-content{display:flex;flex-direction:column;gap:1rem}.cc-filter-divider{height:1px;background-color:#e0e0e0;margin:.5rem 0}.cc-filter-footer{display:flex;align-items:center;justify-content:space-between;gap:var(--cc-filter-footer-gap, 1rem);padding:var(--cc-filter-footer-padding, 1rem 1.5rem 1.5rem);margin-top:auto;flex-shrink:0}.cc-filter-footer .cc-btn-apply{margin-left:auto}.cc-icon-btn{background:none;border:none;cursor:pointer;color:#5f6368;padding:.5rem;border-radius:50%;display:flex;align-items:center;justify-content:center}.cc-icon-btn:hover{background-color:#0000000a}.cc-close-btn{margin-right:-.5rem}\n"] }]
12183
12352
  }], ctorParameters: () => [{ type: i1$4.Router }, { type: i1$4.ActivatedRoute }], propDecorators: { config: [{
12184
12353
  type: Input
12185
12354
  }], initialFilters: [{
@@ -13573,6 +13742,12 @@ class SmartTableComponent {
13573
13742
  * external-data mode. Must be kept in sync by the parent.
13574
13743
  */
13575
13744
  totalItemsCount;
13745
+ /**
13746
+ * External loading state: when the table operates in external-data mode
13747
+ * (i.e. [tableData] is provided), the parent controls the loading spinner
13748
+ * via this input. Ignored in internal-data mode where loading is self-managed.
13749
+ */
13750
+ externalLoading = false;
13576
13751
  action = new EventEmitter();
13577
13752
  topAction = new EventEmitter(); // For top bar buttons
13578
13753
  filterChange = new EventEmitter();
@@ -13596,6 +13771,17 @@ class SmartTableComponent {
13596
13771
  currentPage = 1;
13597
13772
  loading = false;
13598
13773
  originalRowsCache = new Map();
13774
+ /**
13775
+ * Unified loading state used by the template. In external-data mode,
13776
+ * defers to `externalLoading` (parent-driven). In internal-data mode,
13777
+ * uses the internally managed `loading` flag.
13778
+ */
13779
+ get isTableLoading() {
13780
+ if (this.tableData !== undefined) {
13781
+ return this.externalLoading;
13782
+ }
13783
+ return this.loading;
13784
+ }
13599
13785
  // State
13600
13786
  activeSort = { key: '', direction: 'ASC' };
13601
13787
  isSortActive = false; // true only when orderBy is explicitly configured or user has sorted
@@ -14884,17 +15070,19 @@ class SmartTableComponent {
14884
15070
  });
14885
15071
  }
14886
15072
  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 });
14887
- 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", rowSave: "rowSave" }, 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 getVisibleActions(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\" *ngIf=\"isMultiSelect\" (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=\"getVisibleActions(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\" [class.editing-row]=\"row.isEditing\" [ngClass]=\"row.isEditing && config.editingRowClass ? config.editingRowClass : ''\">\r\n <td *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\r\n <input *ngIf=\"isMultiSelect\" type=\"checkbox\" [(ngModel)]=\"row.selected\" (change)=\"onRowSelect(row)\">\r\n <input *ngIf=\"!isMultiSelect\" type=\"radio\" [name]=\"radioGroupName\" [checked]=\"row.selected\" (change)=\"onRowSelectSingle(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 <!-- READ MODE -->\r\n <ng-container *ngIf=\"!row.isEditing\">\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) | safeHtml\"></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 </ng-container>\r\n\r\n <!-- EDIT/ADD MODE -->\r\n <ng-container *ngIf=\"row.isEditing\">\r\n <!-- Multi-field cell (stacked inputs) -->\r\n <div *ngIf=\"col.subFields\" class=\"st-stacked-inputs\">\r\n <ng-container *ngFor=\"let sub of col.subFields\">\r\n <select *ngIf=\"sub.dataType === 'select'\"\r\n [(ngModel)]=\"row[sub.key]\"\r\n [disabled]=\"sub.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-select\">\r\n <option *ngFor=\"let opt of sub.options\" [value]=\"opt.value\">{{ opt.label }}</option>\r\n </select>\r\n <!-- Date picker -->\r\n <div *ngIf=\"sub.dataType === 'date'\" class=\"st-date-cell\">\r\n <input matInput [matDatepicker]=\"subPicker\"\r\n [(ngModel)]=\"row[sub.key]\"\r\n [disabled]=\"sub.editable === false || col.editConfig?.disabled\"\r\n [placeholder]=\"sub.placeholder || ''\"\r\n class=\"st-cell-input\"\r\n (click)=\"subPicker.open()\">\r\n <mat-datepicker-toggle [for]=\"subPicker\"></mat-datepicker-toggle>\r\n <mat-datepicker #subPicker></mat-datepicker>\r\n </div>\r\n <input *ngIf=\"sub.dataType !== 'select' && sub.dataType !== 'date'\"\r\n [type]=\"sub.dataType\"\r\n [(ngModel)]=\"row[sub.key]\"\r\n [disabled]=\"sub.editable === false || col.editConfig?.disabled\"\r\n [placeholder]=\"sub.placeholder || ''\"\r\n class=\"st-cell-input\">\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Single field cell input -->\r\n <div *ngIf=\"!col.subFields\">\r\n <select *ngIf=\"col.dataType === 'select'\"\r\n [(ngModel)]=\"row[col.key]\"\r\n [disabled]=\"col.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-select\">\r\n <option *ngFor=\"let opt of col.options\" [value]=\"opt.value\">{{ opt.label }}</option>\r\n </select>\r\n <!-- Date picker -->\r\n <div *ngIf=\"col.dataType === 'date'\" class=\"st-date-cell\">\r\n <input matInput [matDatepicker]=\"colPicker\"\r\n [(ngModel)]=\"row[col.key]\"\r\n [disabled]=\"col.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-input\"\r\n (click)=\"colPicker.open()\">\r\n <mat-datepicker-toggle [for]=\"colPicker\"></mat-datepicker-toggle>\r\n <mat-datepicker #colPicker></mat-datepicker>\r\n </div>\r\n <input *ngIf=\"col.dataType !== 'select' && col.dataType !== 'date'\"\r\n [type]=\"col.dataType || 'text'\"\r\n [(ngModel)]=\"row[col.key]\"\r\n [disabled]=\"col.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-input\">\r\n </div>\r\n </ng-container>\r\n </td>\r\n\r\n <!-- Row Actions -->\r\n <td *ngIf=\"getVisibleActions(config.actions).length > 0\" class=\"st-row-actions st-actions-col\">\r\n <div class=\"action-buttons\">\r\n <!-- EDIT MODE ACTIONS -->\r\n <ng-container *ngIf=\"row.isEditing\">\r\n <div class=\"edit-mode-actions\">\r\n <lib-button variant=\"outline\" (click)=\"onCancelRow(row, rowIndex); $event.stopPropagation()\">\r\n {{ config.labels?.cancelLabel || 'Cancel' }}\r\n </lib-button>\r\n <lib-button [variant]=\"row.isNew ? (config.labels?.addButtonVariant || 'danger') : (config.labels?.saveButtonVariant || 'primary')\" (click)=\"onSaveRow(row); $event.stopPropagation()\">\r\n {{ row.isNew ? (config.labels?.addLabel || 'Add') : (config.labels?.saveLabel || 'Save') }}\r\n </lib-button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- READ MODE ACTIONS -->\r\n <ng-container *ngIf=\"!row.isEditing\">\r\n <ng-container *ngFor=\"let action of getVisibleActions(config.actions, row); 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, getVisibleActions(action.items, row), 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 </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) + (getVisibleActions(config.actions).length > 0 ? 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 <!-- Pagination \u2014 inside the card, sticks to the bottom -->\r\n <div class=\"st-pagination\" *ngIf=\"showPagination\">\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\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</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;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))}.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],input[type=radio]{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)}}.st-stacked-inputs{display:flex;flex-direction:column;gap:.35rem;width:100%}.st-cell-input,.st-cell-select{width:100%;box-sizing:border-box;padding:.35rem .5rem;font-family:inherit;font-size:var(--st-font-size, 14px);color:var(--st-text-color, #333);background-color:var(--st-cell-input-bg, #fcfcfc);border:var(--st-cell-input-border, 1px solid #dee2e6);border-radius:var(--st-border-radius, 8px);outline:none;transition:border-color .15s,box-shadow .15s}.st-cell-input:focus,.st-cell-select:focus{border-color:var(--st-cell-input-focus-outline-color, #00bcd4)}.st-cell-input:disabled,.st-cell-select:disabled{background-color:#f1f3f5;color:#6c757d;cursor:not-allowed;border-color:#dee2e6}.edit-mode-actions{display:flex;gap:.5rem;align-items:center;justify-content:center;white-space:nowrap}.st-date-cell{display:flex;align-items:center;gap:.25rem;width:100%}.st-date-cell .st-cell-input{flex:1;min-width:0}.st-date-cell mat-datepicker-toggle{flex-shrink:0}.st-date-cell mat-datepicker-toggle ::ng-deep .mat-mdc-icon-button{width:28px;height:28px;padding:2px;color:var(--st-text-color, #333)}\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$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.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: i7$1.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i7$1.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i7$1.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "directive", type: i8.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: ConfirmationModalComponent, selector: "cc-confirmation-modal", inputs: ["config", "isOpen", "confirmDisabled", "confirmLoading"], outputs: ["confirm", "cancel", "close", "showCodeSnippet"] }, { kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }] });
15073
+ 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", externalLoading: "externalLoading", selectedRows: "selectedRows" }, outputs: { action: "action", topAction: "topAction", filterChange: "filterChange", rowSelect: "rowSelect", columnClick: "columnClick", sortChange: "sortChange", pageChange: "pageChange", searchChange: "searchChange", rowSave: "rowSave" }, 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 getVisibleActions(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=\"isTableLoading\">\r\n <div class=\"spinner\"></div>\r\n </div>\r\n <table class=\"st-table\" [class.loading-data]=\"isTableLoading\" [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\" *ngIf=\"isMultiSelect\" (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=\"getVisibleActions(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\" [class.editing-row]=\"row.isEditing\" [ngClass]=\"row.isEditing && config.editingRowClass ? config.editingRowClass : ''\">\r\n <td *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\r\n <input *ngIf=\"isMultiSelect\" type=\"checkbox\" [(ngModel)]=\"row.selected\" (change)=\"onRowSelect(row)\">\r\n <input *ngIf=\"!isMultiSelect\" type=\"radio\" [name]=\"radioGroupName\" [checked]=\"row.selected\" (change)=\"onRowSelectSingle(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 <!-- READ MODE -->\r\n <ng-container *ngIf=\"!row.isEditing\">\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) | safeHtml\"></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 </ng-container>\r\n\r\n <!-- EDIT/ADD MODE -->\r\n <ng-container *ngIf=\"row.isEditing\">\r\n <!-- Multi-field cell (stacked inputs) -->\r\n <div *ngIf=\"col.subFields\" class=\"st-stacked-inputs\">\r\n <ng-container *ngFor=\"let sub of col.subFields\">\r\n <select *ngIf=\"sub.dataType === 'select'\"\r\n [(ngModel)]=\"row[sub.key]\"\r\n [disabled]=\"sub.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-select\">\r\n <option *ngFor=\"let opt of sub.options\" [value]=\"opt.value\">{{ opt.label }}</option>\r\n </select>\r\n <!-- Date picker -->\r\n <div *ngIf=\"sub.dataType === 'date'\" class=\"st-date-cell\">\r\n <input matInput [matDatepicker]=\"subPicker\"\r\n [(ngModel)]=\"row[sub.key]\"\r\n [disabled]=\"sub.editable === false || col.editConfig?.disabled\"\r\n [placeholder]=\"sub.placeholder || ''\"\r\n class=\"st-cell-input\"\r\n (click)=\"subPicker.open()\">\r\n <mat-datepicker-toggle [for]=\"subPicker\"></mat-datepicker-toggle>\r\n <mat-datepicker #subPicker></mat-datepicker>\r\n </div>\r\n <input *ngIf=\"sub.dataType !== 'select' && sub.dataType !== 'date'\"\r\n [type]=\"sub.dataType\"\r\n [(ngModel)]=\"row[sub.key]\"\r\n [disabled]=\"sub.editable === false || col.editConfig?.disabled\"\r\n [placeholder]=\"sub.placeholder || ''\"\r\n class=\"st-cell-input\">\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Single field cell input -->\r\n <div *ngIf=\"!col.subFields\">\r\n <select *ngIf=\"col.dataType === 'select'\"\r\n [(ngModel)]=\"row[col.key]\"\r\n [disabled]=\"col.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-select\">\r\n <option *ngFor=\"let opt of col.options\" [value]=\"opt.value\">{{ opt.label }}</option>\r\n </select>\r\n <!-- Date picker -->\r\n <div *ngIf=\"col.dataType === 'date'\" class=\"st-date-cell\">\r\n <input matInput [matDatepicker]=\"colPicker\"\r\n [(ngModel)]=\"row[col.key]\"\r\n [disabled]=\"col.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-input\"\r\n (click)=\"colPicker.open()\">\r\n <mat-datepicker-toggle [for]=\"colPicker\"></mat-datepicker-toggle>\r\n <mat-datepicker #colPicker></mat-datepicker>\r\n </div>\r\n <input *ngIf=\"col.dataType !== 'select' && col.dataType !== 'date'\"\r\n [type]=\"col.dataType || 'text'\"\r\n [(ngModel)]=\"row[col.key]\"\r\n [disabled]=\"col.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-input\">\r\n </div>\r\n </ng-container>\r\n </td>\r\n\r\n <!-- Row Actions -->\r\n <td *ngIf=\"getVisibleActions(config.actions).length > 0\" class=\"st-row-actions st-actions-col\">\r\n <div class=\"action-buttons\">\r\n <!-- EDIT MODE ACTIONS -->\r\n <ng-container *ngIf=\"row.isEditing\">\r\n <div class=\"edit-mode-actions\">\r\n <lib-button variant=\"outline\" (click)=\"onCancelRow(row, rowIndex); $event.stopPropagation()\">\r\n {{ config.labels?.cancelLabel || 'Cancel' }}\r\n </lib-button>\r\n <lib-button [variant]=\"row.isNew ? (config.labels?.addButtonVariant || 'danger') : (config.labels?.saveButtonVariant || 'primary')\" (click)=\"onSaveRow(row); $event.stopPropagation()\">\r\n {{ row.isNew ? (config.labels?.addLabel || 'Add') : (config.labels?.saveLabel || 'Save') }}\r\n </lib-button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- READ MODE ACTIONS -->\r\n <ng-container *ngIf=\"!row.isEditing\">\r\n <ng-container *ngFor=\"let action of getVisibleActions(config.actions, row); 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, getVisibleActions(action.items, row), 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 </ng-container>\r\n </div>\r\n </td>\r\n </tr>\r\n <tr *ngIf=\"data.length === 0 && !isTableLoading\">\r\n <td [attr.colspan]=\"columnCount + (config.selectable ? 1 : 0) + (getVisibleActions(config.actions).length > 0 ? 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 <!-- Pagination \u2014 inside the card, sticks to the bottom -->\r\n <div class=\"st-pagination\" *ngIf=\"showPagination\">\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\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</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;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))}.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],input[type=radio]{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)}}.st-stacked-inputs{display:flex;flex-direction:column;gap:.35rem;width:100%}.st-cell-input,.st-cell-select{width:100%;box-sizing:border-box;padding:.35rem .5rem;font-family:inherit;font-size:var(--st-font-size, 14px);color:var(--st-text-color, #333);background-color:var(--st-cell-input-bg, #fcfcfc);border:var(--st-cell-input-border, 1px solid #dee2e6);border-radius:var(--st-border-radius, 8px);outline:none;transition:border-color .15s,box-shadow .15s}.st-cell-input:focus,.st-cell-select:focus{border-color:var(--st-cell-input-focus-outline-color, #00bcd4)}.st-cell-input:disabled,.st-cell-select:disabled{background-color:#f1f3f5;color:#6c757d;cursor:not-allowed;border-color:#dee2e6}.edit-mode-actions{display:flex;gap:.5rem;align-items:center;justify-content:center;white-space:nowrap}.st-date-cell{display:flex;align-items:center;gap:.25rem;width:100%}.st-date-cell .st-cell-input{flex:1;min-width:0}.st-date-cell mat-datepicker-toggle{flex-shrink:0}.st-date-cell mat-datepicker-toggle ::ng-deep .mat-mdc-icon-button{width:28px;height:28px;padding:2px;color:var(--st-text-color, #333)}\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$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.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: i7$1.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i7$1.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i7$1.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "directive", type: i8.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: ConfirmationModalComponent, selector: "cc-confirmation-modal", inputs: ["config", "isOpen", "confirmDisabled", "confirmLoading"], outputs: ["confirm", "cancel", "close", "showCodeSnippet"] }, { kind: "pipe", type: SafeHtmlPipe, name: "safeHtml" }] });
14888
15074
  }
14889
15075
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartTableComponent, decorators: [{
14890
15076
  type: Component,
14891
- 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 getVisibleActions(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\" *ngIf=\"isMultiSelect\" (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=\"getVisibleActions(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\" [class.editing-row]=\"row.isEditing\" [ngClass]=\"row.isEditing && config.editingRowClass ? config.editingRowClass : ''\">\r\n <td *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\r\n <input *ngIf=\"isMultiSelect\" type=\"checkbox\" [(ngModel)]=\"row.selected\" (change)=\"onRowSelect(row)\">\r\n <input *ngIf=\"!isMultiSelect\" type=\"radio\" [name]=\"radioGroupName\" [checked]=\"row.selected\" (change)=\"onRowSelectSingle(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 <!-- READ MODE -->\r\n <ng-container *ngIf=\"!row.isEditing\">\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) | safeHtml\"></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 </ng-container>\r\n\r\n <!-- EDIT/ADD MODE -->\r\n <ng-container *ngIf=\"row.isEditing\">\r\n <!-- Multi-field cell (stacked inputs) -->\r\n <div *ngIf=\"col.subFields\" class=\"st-stacked-inputs\">\r\n <ng-container *ngFor=\"let sub of col.subFields\">\r\n <select *ngIf=\"sub.dataType === 'select'\"\r\n [(ngModel)]=\"row[sub.key]\"\r\n [disabled]=\"sub.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-select\">\r\n <option *ngFor=\"let opt of sub.options\" [value]=\"opt.value\">{{ opt.label }}</option>\r\n </select>\r\n <!-- Date picker -->\r\n <div *ngIf=\"sub.dataType === 'date'\" class=\"st-date-cell\">\r\n <input matInput [matDatepicker]=\"subPicker\"\r\n [(ngModel)]=\"row[sub.key]\"\r\n [disabled]=\"sub.editable === false || col.editConfig?.disabled\"\r\n [placeholder]=\"sub.placeholder || ''\"\r\n class=\"st-cell-input\"\r\n (click)=\"subPicker.open()\">\r\n <mat-datepicker-toggle [for]=\"subPicker\"></mat-datepicker-toggle>\r\n <mat-datepicker #subPicker></mat-datepicker>\r\n </div>\r\n <input *ngIf=\"sub.dataType !== 'select' && sub.dataType !== 'date'\"\r\n [type]=\"sub.dataType\"\r\n [(ngModel)]=\"row[sub.key]\"\r\n [disabled]=\"sub.editable === false || col.editConfig?.disabled\"\r\n [placeholder]=\"sub.placeholder || ''\"\r\n class=\"st-cell-input\">\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Single field cell input -->\r\n <div *ngIf=\"!col.subFields\">\r\n <select *ngIf=\"col.dataType === 'select'\"\r\n [(ngModel)]=\"row[col.key]\"\r\n [disabled]=\"col.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-select\">\r\n <option *ngFor=\"let opt of col.options\" [value]=\"opt.value\">{{ opt.label }}</option>\r\n </select>\r\n <!-- Date picker -->\r\n <div *ngIf=\"col.dataType === 'date'\" class=\"st-date-cell\">\r\n <input matInput [matDatepicker]=\"colPicker\"\r\n [(ngModel)]=\"row[col.key]\"\r\n [disabled]=\"col.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-input\"\r\n (click)=\"colPicker.open()\">\r\n <mat-datepicker-toggle [for]=\"colPicker\"></mat-datepicker-toggle>\r\n <mat-datepicker #colPicker></mat-datepicker>\r\n </div>\r\n <input *ngIf=\"col.dataType !== 'select' && col.dataType !== 'date'\"\r\n [type]=\"col.dataType || 'text'\"\r\n [(ngModel)]=\"row[col.key]\"\r\n [disabled]=\"col.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-input\">\r\n </div>\r\n </ng-container>\r\n </td>\r\n\r\n <!-- Row Actions -->\r\n <td *ngIf=\"getVisibleActions(config.actions).length > 0\" class=\"st-row-actions st-actions-col\">\r\n <div class=\"action-buttons\">\r\n <!-- EDIT MODE ACTIONS -->\r\n <ng-container *ngIf=\"row.isEditing\">\r\n <div class=\"edit-mode-actions\">\r\n <lib-button variant=\"outline\" (click)=\"onCancelRow(row, rowIndex); $event.stopPropagation()\">\r\n {{ config.labels?.cancelLabel || 'Cancel' }}\r\n </lib-button>\r\n <lib-button [variant]=\"row.isNew ? (config.labels?.addButtonVariant || 'danger') : (config.labels?.saveButtonVariant || 'primary')\" (click)=\"onSaveRow(row); $event.stopPropagation()\">\r\n {{ row.isNew ? (config.labels?.addLabel || 'Add') : (config.labels?.saveLabel || 'Save') }}\r\n </lib-button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- READ MODE ACTIONS -->\r\n <ng-container *ngIf=\"!row.isEditing\">\r\n <ng-container *ngFor=\"let action of getVisibleActions(config.actions, row); 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, getVisibleActions(action.items, row), 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 </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) + (getVisibleActions(config.actions).length > 0 ? 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 <!-- Pagination \u2014 inside the card, sticks to the bottom -->\r\n <div class=\"st-pagination\" *ngIf=\"showPagination\">\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\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</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;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))}.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],input[type=radio]{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)}}.st-stacked-inputs{display:flex;flex-direction:column;gap:.35rem;width:100%}.st-cell-input,.st-cell-select{width:100%;box-sizing:border-box;padding:.35rem .5rem;font-family:inherit;font-size:var(--st-font-size, 14px);color:var(--st-text-color, #333);background-color:var(--st-cell-input-bg, #fcfcfc);border:var(--st-cell-input-border, 1px solid #dee2e6);border-radius:var(--st-border-radius, 8px);outline:none;transition:border-color .15s,box-shadow .15s}.st-cell-input:focus,.st-cell-select:focus{border-color:var(--st-cell-input-focus-outline-color, #00bcd4)}.st-cell-input:disabled,.st-cell-select:disabled{background-color:#f1f3f5;color:#6c757d;cursor:not-allowed;border-color:#dee2e6}.edit-mode-actions{display:flex;gap:.5rem;align-items:center;justify-content:center;white-space:nowrap}.st-date-cell{display:flex;align-items:center;gap:.25rem;width:100%}.st-date-cell .st-cell-input{flex:1;min-width:0}.st-date-cell mat-datepicker-toggle{flex-shrink:0}.st-date-cell mat-datepicker-toggle ::ng-deep .mat-mdc-icon-button{width:28px;height:28px;padding:2px;color:var(--st-text-color, #333)}\n"] }]
15077
+ 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 getVisibleActions(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=\"isTableLoading\">\r\n <div class=\"spinner\"></div>\r\n </div>\r\n <table class=\"st-table\" [class.loading-data]=\"isTableLoading\" [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\" *ngIf=\"isMultiSelect\" (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=\"getVisibleActions(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\" [class.editing-row]=\"row.isEditing\" [ngClass]=\"row.isEditing && config.editingRowClass ? config.editingRowClass : ''\">\r\n <td *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\r\n <input *ngIf=\"isMultiSelect\" type=\"checkbox\" [(ngModel)]=\"row.selected\" (change)=\"onRowSelect(row)\">\r\n <input *ngIf=\"!isMultiSelect\" type=\"radio\" [name]=\"radioGroupName\" [checked]=\"row.selected\" (change)=\"onRowSelectSingle(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 <!-- READ MODE -->\r\n <ng-container *ngIf=\"!row.isEditing\">\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) | safeHtml\"></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 </ng-container>\r\n\r\n <!-- EDIT/ADD MODE -->\r\n <ng-container *ngIf=\"row.isEditing\">\r\n <!-- Multi-field cell (stacked inputs) -->\r\n <div *ngIf=\"col.subFields\" class=\"st-stacked-inputs\">\r\n <ng-container *ngFor=\"let sub of col.subFields\">\r\n <select *ngIf=\"sub.dataType === 'select'\"\r\n [(ngModel)]=\"row[sub.key]\"\r\n [disabled]=\"sub.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-select\">\r\n <option *ngFor=\"let opt of sub.options\" [value]=\"opt.value\">{{ opt.label }}</option>\r\n </select>\r\n <!-- Date picker -->\r\n <div *ngIf=\"sub.dataType === 'date'\" class=\"st-date-cell\">\r\n <input matInput [matDatepicker]=\"subPicker\"\r\n [(ngModel)]=\"row[sub.key]\"\r\n [disabled]=\"sub.editable === false || col.editConfig?.disabled\"\r\n [placeholder]=\"sub.placeholder || ''\"\r\n class=\"st-cell-input\"\r\n (click)=\"subPicker.open()\">\r\n <mat-datepicker-toggle [for]=\"subPicker\"></mat-datepicker-toggle>\r\n <mat-datepicker #subPicker></mat-datepicker>\r\n </div>\r\n <input *ngIf=\"sub.dataType !== 'select' && sub.dataType !== 'date'\"\r\n [type]=\"sub.dataType\"\r\n [(ngModel)]=\"row[sub.key]\"\r\n [disabled]=\"sub.editable === false || col.editConfig?.disabled\"\r\n [placeholder]=\"sub.placeholder || ''\"\r\n class=\"st-cell-input\">\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Single field cell input -->\r\n <div *ngIf=\"!col.subFields\">\r\n <select *ngIf=\"col.dataType === 'select'\"\r\n [(ngModel)]=\"row[col.key]\"\r\n [disabled]=\"col.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-select\">\r\n <option *ngFor=\"let opt of col.options\" [value]=\"opt.value\">{{ opt.label }}</option>\r\n </select>\r\n <!-- Date picker -->\r\n <div *ngIf=\"col.dataType === 'date'\" class=\"st-date-cell\">\r\n <input matInput [matDatepicker]=\"colPicker\"\r\n [(ngModel)]=\"row[col.key]\"\r\n [disabled]=\"col.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-input\"\r\n (click)=\"colPicker.open()\">\r\n <mat-datepicker-toggle [for]=\"colPicker\"></mat-datepicker-toggle>\r\n <mat-datepicker #colPicker></mat-datepicker>\r\n </div>\r\n <input *ngIf=\"col.dataType !== 'select' && col.dataType !== 'date'\"\r\n [type]=\"col.dataType || 'text'\"\r\n [(ngModel)]=\"row[col.key]\"\r\n [disabled]=\"col.editable === false || col.editConfig?.disabled\"\r\n class=\"st-cell-input\">\r\n </div>\r\n </ng-container>\r\n </td>\r\n\r\n <!-- Row Actions -->\r\n <td *ngIf=\"getVisibleActions(config.actions).length > 0\" class=\"st-row-actions st-actions-col\">\r\n <div class=\"action-buttons\">\r\n <!-- EDIT MODE ACTIONS -->\r\n <ng-container *ngIf=\"row.isEditing\">\r\n <div class=\"edit-mode-actions\">\r\n <lib-button variant=\"outline\" (click)=\"onCancelRow(row, rowIndex); $event.stopPropagation()\">\r\n {{ config.labels?.cancelLabel || 'Cancel' }}\r\n </lib-button>\r\n <lib-button [variant]=\"row.isNew ? (config.labels?.addButtonVariant || 'danger') : (config.labels?.saveButtonVariant || 'primary')\" (click)=\"onSaveRow(row); $event.stopPropagation()\">\r\n {{ row.isNew ? (config.labels?.addLabel || 'Add') : (config.labels?.saveLabel || 'Save') }}\r\n </lib-button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- READ MODE ACTIONS -->\r\n <ng-container *ngIf=\"!row.isEditing\">\r\n <ng-container *ngFor=\"let action of getVisibleActions(config.actions, row); 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, getVisibleActions(action.items, row), 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 </ng-container>\r\n </div>\r\n </td>\r\n </tr>\r\n <tr *ngIf=\"data.length === 0 && !isTableLoading\">\r\n <td [attr.colspan]=\"columnCount + (config.selectable ? 1 : 0) + (getVisibleActions(config.actions).length > 0 ? 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 <!-- Pagination \u2014 inside the card, sticks to the bottom -->\r\n <div class=\"st-pagination\" *ngIf=\"showPagination\">\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\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</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;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))}.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],input[type=radio]{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)}}.st-stacked-inputs{display:flex;flex-direction:column;gap:.35rem;width:100%}.st-cell-input,.st-cell-select{width:100%;box-sizing:border-box;padding:.35rem .5rem;font-family:inherit;font-size:var(--st-font-size, 14px);color:var(--st-text-color, #333);background-color:var(--st-cell-input-bg, #fcfcfc);border:var(--st-cell-input-border, 1px solid #dee2e6);border-radius:var(--st-border-radius, 8px);outline:none;transition:border-color .15s,box-shadow .15s}.st-cell-input:focus,.st-cell-select:focus{border-color:var(--st-cell-input-focus-outline-color, #00bcd4)}.st-cell-input:disabled,.st-cell-select:disabled{background-color:#f1f3f5;color:#6c757d;cursor:not-allowed;border-color:#dee2e6}.edit-mode-actions{display:flex;gap:.5rem;align-items:center;justify-content:center;white-space:nowrap}.st-date-cell{display:flex;align-items:center;gap:.25rem;width:100%}.st-date-cell .st-cell-input{flex:1;min-width:0}.st-date-cell mat-datepicker-toggle{flex-shrink:0}.st-date-cell mat-datepicker-toggle ::ng-deep .mat-mdc-icon-button{width:28px;height:28px;padding:2px;color:var(--st-text-color, #333)}\n"] }]
14892
15078
  }], ctorParameters: () => [{ type: i3.HttpClient }, { type: i1$4.Router }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }], propDecorators: { config: [{
14893
15079
  type: Input
14894
15080
  }], tableData: [{
14895
15081
  type: Input
14896
15082
  }], totalItemsCount: [{
14897
15083
  type: Input
15084
+ }], externalLoading: [{
15085
+ type: Input
14898
15086
  }], action: [{
14899
15087
  type: Output
14900
15088
  }], topAction: [{
@@ -15239,7 +15427,7 @@ class FilterTableSelectorComponent {
15239
15427
  return Object.keys(this.activeFilterParams).length > 0;
15240
15428
  }
15241
15429
  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 });
15242
- 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", "selectedOptionsData"], outputs: ["submit", "draftSave", "actionClick", "valueChange", "fileAdded", "fileUploadFinished", "fileRemoved", "suffixActionClick", "stepChange"] }, { kind: "component", type: SmartTableComponent, selector: "lib-smart-table", inputs: ["config", "tableData", "totalItemsCount", "selectedRows"], outputs: ["action", "topAction", "filterChange", "rowSelect", "columnClick", "sortChange", "pageChange", "searchChange", "rowSave"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }] });
15430
+ 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", "selectedOptionsData"], outputs: ["submit", "draftSave", "actionClick", "valueChange", "fileAdded", "fileUploadFinished", "fileRemoved", "suffixActionClick", "stepChange"] }, { kind: "component", type: SmartTableComponent, selector: "lib-smart-table", inputs: ["config", "tableData", "totalItemsCount", "externalLoading", "selectedRows"], outputs: ["action", "topAction", "filterChange", "rowSelect", "columnClick", "sortChange", "pageChange", "searchChange", "rowSave"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }] });
15243
15431
  }
15244
15432
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FilterTableSelectorComponent, decorators: [{
15245
15433
  type: Component,