commons-shared-web-ui 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { NgModule, EventEmitter, Output, Input, Component, HostListener, forwardRef, inject, LOCALE_ID, ViewChildren } from '@angular/core';
2
+ import { NgModule, Input, Component, EventEmitter, HostListener, Output, forwardRef, Injectable, inject, LOCALE_ID, ViewChildren } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { CommonModule, formatDate } from '@angular/common';
5
5
  import { MatCardModule } from '@angular/material/card';
@@ -45,8 +45,8 @@ import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, Validators } from
45
45
  import * as i1$1 from '@angular/router';
46
46
  import * as i3 from '@angular/common/http';
47
47
  import { HttpParams } from '@angular/common/http';
48
- import { debounceTime, distinctUntilChanged, map, finalize, catchError } from 'rxjs/operators';
49
- import { Subject, forkJoin, of } from 'rxjs';
48
+ import { BehaviorSubject, Subject, combineLatest, forkJoin, of } from 'rxjs';
49
+ import { takeUntil, debounceTime, distinctUntilChanged, map, finalize, catchError } from 'rxjs/operators';
50
50
 
51
51
  class MaterialModule {
52
52
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MaterialModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
@@ -227,81 +227,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
227
227
  }]
228
228
  }] });
229
229
 
230
- class CardType1Component {
231
- config;
232
- theme = 'theme-1';
233
- cardClick = new EventEmitter();
234
- get valueStyle() {
235
- return this.config.valueColor ? { color: this.config.valueColor } : {};
236
- }
237
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CardType1Component, deps: [], target: i0.ɵɵFactoryTarget.Component });
238
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: CardType1Component, isStandalone: false, selector: "lib-card-type-1", inputs: { config: "config", theme: "theme" }, outputs: { cardClick: "cardClick" }, ngImport: i0, template: "<div [class]=\"'cc-card ' + theme\" [style.width]=\"config.width || '100%'\" [style.height]=\"config.height || '100%'\" (click)=\"cardClick.emit()\">\n <div class=\"card-content\">\n <div class=\"card-label\">{{ config.label }}</div>\n <div class=\"card-value\" [ngStyle]=\"valueStyle\">{{ config.value }}</div>\n <div\n *ngIf=\"config.subtext\"\n class=\"card-subtext\"\n [ngClass]=\"config.subtextClass\"\n >\n {{ config.subtext }}\n </div>\n </div>\n</div>\n", styles: [".cc-card{display:flex;flex-direction:column;justify-content:center;max-width:100%;box-sizing:border-box;overflow:hidden}.card-label{font-family:var(--cc-font-family);font-size:var(--cc-text-size-label);color:var(--cc-text-color-secondary);text-transform:uppercase;font-weight:var(--cc-text-weight-medium);margin-bottom:8px}.card-value{font-family:var(--cc-font-family);font-size:var(--cc-text-size-value);color:var(--cc-text-color-primary);font-weight:var(--cc-text-weight-bold);margin-bottom:4px}.card-subtext{font-family:var(--cc-font-family);font-size:12px;color:var(--cc-text-color-secondary)}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }] });
239
- }
240
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CardType1Component, decorators: [{
241
- type: Component,
242
- args: [{ selector: 'lib-card-type-1', standalone: false, template: "<div [class]=\"'cc-card ' + theme\" [style.width]=\"config.width || '100%'\" [style.height]=\"config.height || '100%'\" (click)=\"cardClick.emit()\">\n <div class=\"card-content\">\n <div class=\"card-label\">{{ config.label }}</div>\n <div class=\"card-value\" [ngStyle]=\"valueStyle\">{{ config.value }}</div>\n <div\n *ngIf=\"config.subtext\"\n class=\"card-subtext\"\n [ngClass]=\"config.subtextClass\"\n >\n {{ config.subtext }}\n </div>\n </div>\n</div>\n", styles: [".cc-card{display:flex;flex-direction:column;justify-content:center;max-width:100%;box-sizing:border-box;overflow:hidden}.card-label{font-family:var(--cc-font-family);font-size:var(--cc-text-size-label);color:var(--cc-text-color-secondary);text-transform:uppercase;font-weight:var(--cc-text-weight-medium);margin-bottom:8px}.card-value{font-family:var(--cc-font-family);font-size:var(--cc-text-size-value);color:var(--cc-text-color-primary);font-weight:var(--cc-text-weight-bold);margin-bottom:4px}.card-subtext{font-family:var(--cc-font-family);font-size:12px;color:var(--cc-text-color-secondary)}\n"] }]
243
- }], propDecorators: { config: [{
244
- type: Input
245
- }], theme: [{
246
- type: Input
247
- }], cardClick: [{
248
- type: Output
249
- }] } });
250
-
251
- class CardType2Component {
252
- config;
253
- theme = 'theme-1';
254
- cardClick = new EventEmitter();
255
- get iconBoxStyle() {
256
- if (this.config.iconUrl) {
257
- return { backgroundColor: 'transparent' };
258
- }
259
- return this.config.iconBackgroundColor ? { backgroundColor: this.config.iconBackgroundColor } : {};
260
- }
261
- get iconStyle() {
262
- return this.config.iconColor ? { color: this.config.iconColor } : {};
263
- }
264
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CardType2Component, deps: [], target: i0.ɵɵFactoryTarget.Component });
265
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: CardType2Component, isStandalone: false, selector: "lib-card-type-2", inputs: { config: "config", theme: "theme" }, outputs: { cardClick: "cardClick" }, ngImport: i0, template: "<div [class]=\"'cc-card ' + theme\" [style.width]=\"config.width || '100%'\" [style.height]=\"config.height || '100%'\" (click)=\"cardClick.emit()\">\n <div class=\"card-content-wrapper\">\n <div class=\"icon-box\" [ngStyle]=\"iconBoxStyle\">\n <img *ngIf=\"config.iconUrl; else matIconTpl\" [src]=\"config.iconUrl\" class=\"icon-image\" alt=\"icon\">\n <ng-template #matIconTpl>\n <mat-icon [ngStyle]=\"iconStyle\">{{ config.iconName }}</mat-icon>\n </ng-template>\n </div>\n <div class=\"text-content\">\n <div class=\"card-label\">{{ config.label }}</div>\n <div class=\"value-wrapper\">\n <span class=\"card-value\">{{ config.value }}</span>\n <span *ngIf=\"config.targetText\" class=\"card-target\">{{ config.targetText }}</span>\n </div>\n </div>\n </div>\n</div>\n", styles: [".cc-card{display:flex;flex-direction:column;justify-content:center;max-width:100%;box-sizing:border-box;overflow:hidden}.card-content-wrapper{display:flex;align-items:center;gap:16px}.icon-box{width:48px;height:48px;border-radius:8px;display:flex;align-items:center;justify-content:center;background-color:#fce8e6;color:#d32f2f}.icon-box mat-icon{font-size:24px;width:24px;height:24px}.icon-box .icon-image{object-fit:contain;border-radius:.5rem}.text-content{display:flex;flex-direction:column}.card-label{font-family:var(--cc-font-family);font-size:var(--cc-text-size-label);color:var(--cc-text-color-secondary);text-transform:uppercase;font-weight:var(--cc-text-weight-medium);margin-bottom:4px}.value-wrapper{display:flex;align-items:baseline;gap:8px}.card-value{font-family:var(--cc-font-family);font-size:var(--cc-text-size-value);color:var(--cc-text-color-primary);font-weight:var(--cc-text-weight-bold)}.card-target{font-family:var(--cc-font-family);font-size:12px;color:var(--cc-text-color-secondary)}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
266
- }
267
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CardType2Component, decorators: [{
268
- type: Component,
269
- args: [{ selector: 'lib-card-type-2', standalone: false, template: "<div [class]=\"'cc-card ' + theme\" [style.width]=\"config.width || '100%'\" [style.height]=\"config.height || '100%'\" (click)=\"cardClick.emit()\">\n <div class=\"card-content-wrapper\">\n <div class=\"icon-box\" [ngStyle]=\"iconBoxStyle\">\n <img *ngIf=\"config.iconUrl; else matIconTpl\" [src]=\"config.iconUrl\" class=\"icon-image\" alt=\"icon\">\n <ng-template #matIconTpl>\n <mat-icon [ngStyle]=\"iconStyle\">{{ config.iconName }}</mat-icon>\n </ng-template>\n </div>\n <div class=\"text-content\">\n <div class=\"card-label\">{{ config.label }}</div>\n <div class=\"value-wrapper\">\n <span class=\"card-value\">{{ config.value }}</span>\n <span *ngIf=\"config.targetText\" class=\"card-target\">{{ config.targetText }}</span>\n </div>\n </div>\n </div>\n</div>\n", styles: [".cc-card{display:flex;flex-direction:column;justify-content:center;max-width:100%;box-sizing:border-box;overflow:hidden}.card-content-wrapper{display:flex;align-items:center;gap:16px}.icon-box{width:48px;height:48px;border-radius:8px;display:flex;align-items:center;justify-content:center;background-color:#fce8e6;color:#d32f2f}.icon-box mat-icon{font-size:24px;width:24px;height:24px}.icon-box .icon-image{object-fit:contain;border-radius:.5rem}.text-content{display:flex;flex-direction:column}.card-label{font-family:var(--cc-font-family);font-size:var(--cc-text-size-label);color:var(--cc-text-color-secondary);text-transform:uppercase;font-weight:var(--cc-text-weight-medium);margin-bottom:4px}.value-wrapper{display:flex;align-items:baseline;gap:8px}.card-value{font-family:var(--cc-font-family);font-size:var(--cc-text-size-value);color:var(--cc-text-color-primary);font-weight:var(--cc-text-weight-bold)}.card-target{font-family:var(--cc-font-family);font-size:12px;color:var(--cc-text-color-secondary)}\n"] }]
270
- }], propDecorators: { config: [{
271
- type: Input
272
- }], theme: [{
273
- type: Input
274
- }], cardClick: [{
275
- type: Output
276
- }] } });
277
-
278
- class CardsModule {
279
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CardsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
280
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: CardsModule, declarations: [CardType1Component,
281
- CardType2Component], imports: [CommonModule,
282
- MaterialModule], exports: [CardType1Component,
283
- CardType2Component] });
284
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CardsModule, imports: [CommonModule,
285
- MaterialModule] });
286
- }
287
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CardsModule, decorators: [{
288
- type: NgModule,
289
- args: [{
290
- declarations: [
291
- CardType1Component,
292
- CardType2Component
293
- ],
294
- imports: [
295
- CommonModule,
296
- MaterialModule
297
- ],
298
- exports: [
299
- CardType1Component,
300
- CardType2Component
301
- ]
302
- }]
303
- }] });
304
-
305
230
  class AlertComponent {
306
231
  variant = 'info';
307
232
  title = '';
@@ -1578,24 +1503,798 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
1578
1503
  }]
1579
1504
  }] });
1580
1505
 
1506
+ class SmartFormController {
1507
+ formData = {};
1508
+ fieldSubjects = new Map();
1509
+ initialize(initialData) {
1510
+ this.formData = { ...initialData };
1511
+ this.fieldSubjects.forEach((subject, key) => {
1512
+ subject.next(this.formData[key]);
1513
+ });
1514
+ }
1515
+ updateField(name, value) {
1516
+ this.formData[name] = value;
1517
+ if (!this.fieldSubjects.has(name)) {
1518
+ this.fieldSubjects.set(name, new BehaviorSubject(value));
1519
+ }
1520
+ else {
1521
+ this.fieldSubjects.get(name).next(value);
1522
+ }
1523
+ }
1524
+ getFieldValue(name) {
1525
+ return this.formData[name];
1526
+ }
1527
+ getFieldObservable(name) {
1528
+ if (!this.fieldSubjects.has(name)) {
1529
+ this.fieldSubjects.set(name, new BehaviorSubject(this.formData[name]));
1530
+ }
1531
+ return this.fieldSubjects.get(name).asObservable();
1532
+ }
1533
+ getAllData() {
1534
+ return { ...this.formData };
1535
+ }
1536
+ reset() {
1537
+ this.formData = {};
1538
+ this.fieldSubjects.forEach(subject => subject.next(undefined));
1539
+ }
1540
+ destroy() {
1541
+ this.fieldSubjects.forEach(subject => subject.complete());
1542
+ this.fieldSubjects.clear();
1543
+ }
1544
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartFormController, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1545
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartFormController });
1546
+ }
1547
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartFormController, decorators: [{
1548
+ type: Injectable
1549
+ }] });
1550
+
1551
+ class ExpressionService {
1552
+ loadedFunctions = new Map();
1553
+ evaluate(expression, context, variables) {
1554
+ try {
1555
+ const filteredContext = variables
1556
+ ? Object.fromEntries(Object.entries(context).filter(([key]) => variables.includes(key)))
1557
+ : context;
1558
+ const func = new Function(...Object.keys(filteredContext), `return ${expression};`);
1559
+ return func(...Object.values(filteredContext));
1560
+ }
1561
+ catch (e) {
1562
+ console.error('Expression evaluation error:', e);
1563
+ return null;
1564
+ }
1565
+ }
1566
+ evaluateCondition(expression, context) {
1567
+ try {
1568
+ const func = new Function(...Object.keys(context), `return ${expression};`);
1569
+ const result = func(...Object.values(context));
1570
+ return !!result;
1571
+ }
1572
+ catch (e) {
1573
+ console.error('Condition evaluation error:', e);
1574
+ return false;
1575
+ }
1576
+ }
1577
+ evaluateFormula(formula, functionName, context, variables) {
1578
+ try {
1579
+ if (!this.loadedFunctions.has(functionName)) {
1580
+ const func = new Function('context', `
1581
+ ${formula}
1582
+ return ${functionName};
1583
+ `)(context);
1584
+ this.loadedFunctions.set(functionName, func);
1585
+ }
1586
+ const fn = this.loadedFunctions.get(functionName);
1587
+ if (!fn)
1588
+ return null;
1589
+ const args = variables ? variables.map(v => context[v]) : [];
1590
+ return fn(...args);
1591
+ }
1592
+ catch (e) {
1593
+ console.error('Formula evaluation error:', e);
1594
+ return null;
1595
+ }
1596
+ }
1597
+ extractVariables(expression) {
1598
+ const regex = /\b[a-zA-Z_]\w*\b/g;
1599
+ const matches = expression.match(regex) || [];
1600
+ const keywords = ['true', 'false', 'null', 'undefined', 'return', 'if', 'else', 'for', 'while', 'function', 'var', 'let', 'const'];
1601
+ return [...new Set(matches.filter(m => !keywords.includes(m)))];
1602
+ }
1603
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ExpressionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1604
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ExpressionService, providedIn: 'root' });
1605
+ }
1606
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ExpressionService, decorators: [{
1607
+ type: Injectable,
1608
+ args: [{
1609
+ providedIn: 'root'
1610
+ }]
1611
+ }] });
1612
+
1613
+ class ValidationUtils {
1614
+ static email() {
1615
+ return (control) => {
1616
+ if (!control.value)
1617
+ return null;
1618
+ const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
1619
+ return emailRegex.test(control.value) ? null : { email: true };
1620
+ };
1621
+ }
1622
+ static phone() {
1623
+ return (control) => {
1624
+ if (!control.value)
1625
+ return null;
1626
+ const phoneRegex = /^\+?[\d\s\-\(\)]{10,}$/;
1627
+ return phoneRegex.test(control.value) ? null : { phone: true };
1628
+ };
1629
+ }
1630
+ static url() {
1631
+ return (control) => {
1632
+ if (!control.value)
1633
+ return null;
1634
+ const urlRegex = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/;
1635
+ return urlRegex.test(control.value) ? null : { url: true };
1636
+ };
1637
+ }
1638
+ static minLength(min) {
1639
+ return (control) => {
1640
+ if (!control.value)
1641
+ return null;
1642
+ return control.value.length >= min ? null : { minLength: { min, actual: control.value.length } };
1643
+ };
1644
+ }
1645
+ static maxLength(max) {
1646
+ return (control) => {
1647
+ if (!control.value)
1648
+ return null;
1649
+ return control.value.length <= max ? null : { maxLength: { max, actual: control.value.length } };
1650
+ };
1651
+ }
1652
+ static pattern(pattern, message) {
1653
+ return (control) => {
1654
+ if (!control.value)
1655
+ return null;
1656
+ const regex = new RegExp(pattern);
1657
+ return regex.test(control.value) ? null : { pattern: { message: message || 'Invalid format' } };
1658
+ };
1659
+ }
1660
+ static numberRange(min, max) {
1661
+ return (control) => {
1662
+ if (!control.value && control.value !== 0)
1663
+ return null;
1664
+ const value = Number(control.value);
1665
+ if (min !== undefined && value < min) {
1666
+ return { min: { min, actual: value } };
1667
+ }
1668
+ if (max !== undefined && value > max) {
1669
+ return { max: { max, actual: value } };
1670
+ }
1671
+ return null;
1672
+ };
1673
+ }
1674
+ static dateRange(minDate, maxDate) {
1675
+ return (control) => {
1676
+ if (!control.value)
1677
+ return null;
1678
+ const date = new Date(control.value);
1679
+ if (minDate && date < new Date(minDate)) {
1680
+ return { minDate: { minDate } };
1681
+ }
1682
+ if (maxDate && date > new Date(maxDate)) {
1683
+ return { maxDate: { maxDate } };
1684
+ }
1685
+ return null;
1686
+ };
1687
+ }
1688
+ static getErrorMessage(errors) {
1689
+ if (!errors)
1690
+ return '';
1691
+ if (errors['required'])
1692
+ return 'This field is required';
1693
+ if (errors['email'])
1694
+ return 'Please enter a valid email address';
1695
+ if (errors['phone'])
1696
+ return 'Please enter a valid phone number';
1697
+ if (errors['url'])
1698
+ return 'Please enter a valid URL';
1699
+ if (errors['minLength'])
1700
+ return `Minimum length is ${errors['minLength'].min} characters`;
1701
+ if (errors['maxLength'])
1702
+ return `Maximum length is ${errors['maxLength'].max} characters`;
1703
+ if (errors['min'])
1704
+ return `Minimum value is ${errors['min'].min}`;
1705
+ if (errors['max'])
1706
+ return `Maximum value is ${errors['max'].max}`;
1707
+ if (errors['minDate'])
1708
+ return `Date must be after ${errors['minDate'].minDate}`;
1709
+ if (errors['maxDate'])
1710
+ return `Date must be before ${errors['maxDate'].maxDate}`;
1711
+ if (errors['pattern'])
1712
+ return errors['pattern'].message || 'Invalid format';
1713
+ return 'Invalid value';
1714
+ }
1715
+ }
1716
+
1717
+ class FormFieldComponent {
1718
+ expressionService;
1719
+ http;
1720
+ config;
1721
+ controller;
1722
+ sectionIndex;
1723
+ value;
1724
+ isVisible = true;
1725
+ errorMessage = '';
1726
+ destroy$ = new Subject();
1727
+ constructor(expressionService, http) {
1728
+ this.expressionService = expressionService;
1729
+ this.http = http;
1730
+ }
1731
+ ngOnInit() {
1732
+ this.setupField();
1733
+ this.setupVisibility();
1734
+ this.setupGeneratedField();
1735
+ this.setupDependencies();
1736
+ // Initial load if no dependencies or if dependencies have initial values
1737
+ if (!this.config.optionConfig?.dependencies) {
1738
+ this.loadDropdownOptions();
1739
+ }
1740
+ }
1741
+ ngOnDestroy() {
1742
+ this.destroy$.next();
1743
+ this.destroy$.complete();
1744
+ }
1745
+ setupField() {
1746
+ if (this.config.name) {
1747
+ const fieldName = this.getFieldName();
1748
+ this.controller.getFieldObservable(fieldName)
1749
+ .pipe(takeUntil(this.destroy$))
1750
+ .subscribe(val => {
1751
+ this.value = val;
1752
+ });
1753
+ }
1754
+ }
1755
+ setupVisibility() {
1756
+ if (this.config.visibilityExpression) {
1757
+ const variables = this.expressionService.extractVariables(this.config.visibilityExpression);
1758
+ const observables = variables.map(v => this.controller.getFieldObservable(v));
1759
+ combineLatest(observables)
1760
+ .pipe(takeUntil(this.destroy$))
1761
+ .subscribe(() => {
1762
+ const context = this.controller.getAllData();
1763
+ this.isVisible = this.expressionService.evaluateCondition(this.config.visibilityExpression, context);
1764
+ });
1765
+ }
1766
+ }
1767
+ setupGeneratedField() {
1768
+ if (this.config.type === 'GENERATED' && this.config.generatedConfig) {
1769
+ const variables = this.config.generatedConfig.variables || [];
1770
+ const observables = variables.map(v => this.controller.getFieldObservable(v));
1771
+ combineLatest(observables)
1772
+ .pipe(takeUntil(this.destroy$))
1773
+ .subscribe(() => {
1774
+ const context = this.controller.getAllData();
1775
+ const result = this.evaluateFormula(context);
1776
+ if (result !== null && this.config.name) {
1777
+ this.controller.updateField(this.getFieldName(), result);
1778
+ }
1779
+ });
1780
+ }
1781
+ }
1782
+ evaluateFormula(context) {
1783
+ if (!this.config.generatedConfig)
1784
+ return null;
1785
+ const formula = this.config.generatedConfig.formula;
1786
+ const functionName = this.extractFunctionName(formula);
1787
+ if (functionName) {
1788
+ return this.expressionService.evaluateFormula(formula, functionName, context, this.config.generatedConfig.variables);
1789
+ }
1790
+ return null;
1791
+ }
1792
+ extractFunctionName(formula) {
1793
+ const match = formula.match(/(?:function|fun)\s+(\w+)\s*\(/);
1794
+ return match ? match[1] : null;
1795
+ }
1796
+ setupDependencies() {
1797
+ if (!this.config.optionConfig?.dependencies)
1798
+ return;
1799
+ const dependencies = this.config.optionConfig.dependencies;
1800
+ const observables = Object.values(dependencies).map(fieldName => this.controller.getFieldObservable(fieldName));
1801
+ combineLatest(observables)
1802
+ .pipe(takeUntil(this.destroy$))
1803
+ .subscribe((values) => {
1804
+ // Create a map of dependency values
1805
+ const dependencyValues = {};
1806
+ Object.keys(dependencies).forEach((paramKey, index) => {
1807
+ dependencyValues[paramKey] = values[index];
1808
+ });
1809
+ // Check if all required dependencies have values (optional: could skip this if partial dependencies are allowed)
1810
+ const allPresent = Object.values(dependencyValues).every(v => v !== null && v !== undefined && v !== '');
1811
+ if (allPresent) {
1812
+ this.loadDropdownOptions(dependencyValues);
1813
+ }
1814
+ else {
1815
+ // Clear options if dependencies are missing
1816
+ if (this.config.optionConfig) {
1817
+ this.config.optionConfig.optionList = [];
1818
+ }
1819
+ }
1820
+ });
1821
+ }
1822
+ loadDropdownOptions(queryParams = {}) {
1823
+ const optionConfig = this.config.optionConfig;
1824
+ const urls = optionConfig?.apiUrls || (optionConfig?.apiUrl ? [optionConfig.apiUrl] : optionConfig?.optionUrl ? [optionConfig.optionUrl] : []);
1825
+ if (!urls || urls.length === 0)
1826
+ return;
1827
+ // Create observables for each URL with query params
1828
+ const observables = urls.map(url => {
1829
+ let fullUrl = url;
1830
+ const params = new URLSearchParams();
1831
+ Object.entries(queryParams).forEach(([key, value]) => {
1832
+ if (value !== null && value !== undefined) {
1833
+ params.append(key, String(value));
1834
+ }
1835
+ });
1836
+ const queryString = params.toString();
1837
+ if (queryString) {
1838
+ fullUrl += (fullUrl.includes('?') ? '&' : '?') + queryString;
1839
+ }
1840
+ return this.http.get(fullUrl);
1841
+ });
1842
+ forkJoin(observables)
1843
+ .pipe(takeUntil(this.destroy$))
1844
+ .subscribe({
1845
+ next: (responses) => {
1846
+ let mergedData = [];
1847
+ responses.forEach(response => {
1848
+ // Identify array source for each response
1849
+ const data = optionConfig?.dataPath
1850
+ ? this.getValueByPath(response, optionConfig.dataPath)
1851
+ : (Array.isArray(response) ? response : response.data || response.items || []);
1852
+ if (Array.isArray(data)) {
1853
+ mergedData = [...mergedData, ...data];
1854
+ }
1855
+ });
1856
+ // Sort merged data BEFORE mapping
1857
+ if (optionConfig?.sortBy) {
1858
+ const sortKey = optionConfig.sortBy;
1859
+ const direction = optionConfig.sortDirection === 'DESC' ? -1 : 1;
1860
+ mergedData.sort((a, b) => {
1861
+ const valA = this.getValueByPath(a, sortKey);
1862
+ const valB = this.getValueByPath(b, sortKey);
1863
+ if (typeof valA === 'string' && typeof valB === 'string') {
1864
+ return direction * valA.localeCompare(valB);
1865
+ }
1866
+ if (typeof valA === 'number' && typeof valB === 'number') {
1867
+ return direction * (valA - valB);
1868
+ }
1869
+ return 0;
1870
+ });
1871
+ }
1872
+ this.config.optionConfig.optionList = mergedData.map((item) => {
1873
+ const label = optionConfig?.labelPath
1874
+ ? this.getValueByPath(item, optionConfig.labelPath)
1875
+ : (item.label || item.name);
1876
+ const code = optionConfig?.valuePath
1877
+ ? this.getValueByPath(item, optionConfig.valuePath)
1878
+ : (item.code || item.id || item.value);
1879
+ return {
1880
+ label: String(label),
1881
+ code: code,
1882
+ value: item
1883
+ };
1884
+ });
1885
+ },
1886
+ error: (err) => console.error('Failed to load dropdown options:', err)
1887
+ });
1888
+ }
1889
+ getValueByPath(obj, path) {
1890
+ if (!path || path === '')
1891
+ return obj;
1892
+ return path.split('.').reduce((acc, part) => {
1893
+ const match = part.match(/(\w+)\[(\d+)\]/);
1894
+ if (match) {
1895
+ return acc?.[match[1]]?.[parseInt(match[2])];
1896
+ }
1897
+ return acc?.[part];
1898
+ }, obj);
1899
+ }
1900
+ onValueChange(event) {
1901
+ if (!this.config.name)
1902
+ return;
1903
+ const fieldName = this.getFieldName();
1904
+ let newValue;
1905
+ if (event?.target) {
1906
+ const target = event.target;
1907
+ if (target.type === 'checkbox') {
1908
+ newValue = target.checked;
1909
+ }
1910
+ else if (target.type === 'number') {
1911
+ newValue = target.value ? Number(target.value) : null;
1912
+ }
1913
+ else {
1914
+ newValue = target.value;
1915
+ }
1916
+ }
1917
+ else {
1918
+ newValue = event;
1919
+ }
1920
+ this.controller.updateField(fieldName, newValue);
1921
+ this.validate(newValue);
1922
+ }
1923
+ onCheckboxListChange(code, checked) {
1924
+ if (!this.config.name)
1925
+ return;
1926
+ const fieldName = this.getFieldName();
1927
+ const currentValue = this.controller.getFieldValue(fieldName) || [];
1928
+ let newValue;
1929
+ if (checked) {
1930
+ newValue = [...currentValue, code];
1931
+ }
1932
+ else {
1933
+ newValue = currentValue.filter((c) => c !== code);
1934
+ }
1935
+ this.controller.updateField(fieldName, newValue);
1936
+ this.validate(newValue);
1937
+ }
1938
+ isChecked(code) {
1939
+ const value = this.value || [];
1940
+ return Array.isArray(value) && value.includes(code);
1941
+ }
1942
+ validate(value) {
1943
+ this.errorMessage = '';
1944
+ if (this.config.required && !value) {
1945
+ this.errorMessage = 'This field is required';
1946
+ return;
1947
+ }
1948
+ if (!value)
1949
+ return;
1950
+ if (this.config.subType === 'EMAIL' && !this.config.textConfig?.pattern) {
1951
+ const validator = ValidationUtils.email();
1952
+ const errors = validator({ value });
1953
+ if (errors)
1954
+ this.errorMessage = ValidationUtils.getErrorMessage(errors);
1955
+ }
1956
+ if (this.config.subType === 'PHONE' && !this.config.textConfig?.pattern) {
1957
+ const validator = ValidationUtils.phone();
1958
+ const errors = validator({ value });
1959
+ if (errors)
1960
+ this.errorMessage = ValidationUtils.getErrorMessage(errors);
1961
+ }
1962
+ if (this.config.textConfig?.length) {
1963
+ const { min, max } = this.config.textConfig.length;
1964
+ if (min && value.length < min) {
1965
+ this.errorMessage = `Minimum length is ${min} characters`;
1966
+ }
1967
+ if (max && value.length > max) {
1968
+ this.errorMessage = `Maximum length is ${max} characters`;
1969
+ }
1970
+ }
1971
+ if (this.config.textConfig?.pattern) {
1972
+ try {
1973
+ const regex = new RegExp(this.config.textConfig.pattern);
1974
+ if (!regex.test(value)) {
1975
+ this.errorMessage = this.config.textConfig.patternMessage || 'Invalid format';
1976
+ }
1977
+ }
1978
+ catch (e) {
1979
+ console.error('Invalid regex pattern:', this.config.textConfig.pattern);
1980
+ }
1981
+ }
1982
+ if (this.config.numberConfig) {
1983
+ const { min, max } = this.config.numberConfig;
1984
+ const numValue = Number(value);
1985
+ if (min !== undefined && numValue < min) {
1986
+ this.errorMessage = `Minimum value is ${min}`;
1987
+ }
1988
+ if (max !== undefined && numValue > max) {
1989
+ this.errorMessage = `Maximum value is ${max}`;
1990
+ }
1991
+ }
1992
+ }
1993
+ getFieldName() {
1994
+ if (this.sectionIndex !== undefined && this.config.name) {
1995
+ return `${this.config.name}_${this.sectionIndex}`;
1996
+ }
1997
+ return this.config.name || '';
1998
+ }
1999
+ get isTextField() {
2000
+ return this.config.type === 'TEXT_INPUT';
2001
+ }
2002
+ get isNumberField() {
2003
+ return this.config.type === 'NUMBER_INPUT';
2004
+ }
2005
+ get isDateField() {
2006
+ return this.config.type === 'DATE';
2007
+ }
2008
+ get isDropdown() {
2009
+ return this.config.type === 'DROPDOWN';
2010
+ }
2011
+ get isRadio() {
2012
+ return this.config.type === 'RADIO';
2013
+ }
2014
+ get isCheckbox() {
2015
+ return this.config.type === 'CHECKBOX';
2016
+ }
2017
+ get isChip() {
2018
+ return this.config.type === 'CHIP';
2019
+ }
2020
+ get isSwitch() {
2021
+ return this.config.type === 'SWITCH';
2022
+ }
2023
+ get isRating() {
2024
+ return this.config.type === 'RATING';
2025
+ }
2026
+ onRatingChange(star, event) {
2027
+ if (!this.config.name || this.config.disabled)
2028
+ return;
2029
+ let newValue = star;
2030
+ if (this.config.ratingConfig?.allowHalf && event) {
2031
+ const target = event.target;
2032
+ const rect = target.getBoundingClientRect();
2033
+ const x = event.clientX - rect.left;
2034
+ // If click is in the first 50% of the star, it's a half star
2035
+ if (x < rect.width / 2) {
2036
+ newValue = star - 0.5;
2037
+ }
2038
+ }
2039
+ // Toggle off if clicking same value
2040
+ if (this.value === newValue) {
2041
+ newValue = 0;
2042
+ }
2043
+ this.controller.updateField(this.getFieldName(), newValue);
2044
+ this.validate(newValue);
2045
+ }
2046
+ getStarArray() {
2047
+ const max = this.config.ratingConfig?.maxRating || 5;
2048
+ return Array.from({ length: max }, (_, i) => i + 1);
2049
+ }
2050
+ isStarHalf(star) {
2051
+ const value = this.value || 0;
2052
+ return value === star - 0.5;
2053
+ }
2054
+ isStarFilled(star) {
2055
+ const value = this.value || 0;
2056
+ return value >= star;
2057
+ }
2058
+ get isGenerated() {
2059
+ return this.config.type === 'GENERATED';
2060
+ }
2061
+ get isRow() {
2062
+ return this.config.type === 'ROW';
2063
+ }
2064
+ get isGroup() {
2065
+ return this.config.type === 'GROUP';
2066
+ }
2067
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldComponent, deps: [{ token: ExpressionService }, { token: i3.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
2068
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FormFieldComponent, isStandalone: false, selector: "lib-form-field", inputs: { config: "config", controller: "controller", sectionIndex: "sectionIndex" }, ngImport: i0, template: "<div class=\"form-field\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\n <!-- ROW Layout -->\n <div *ngIf=\"isRow\" class=\"form-row\" [class.horizontal]=\"config.subType === 'HORIZONTAL'\">\n <ng-container *ngFor=\"let child of config.children\">\n <div class=\"row-field\">\n <lib-form-field\n [config]=\"child\"\n [controller]=\"controller\"\n [sectionIndex]=\"sectionIndex\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n\n <!-- GROUP (Section) -->\n <div *ngIf=\"isGroup && config.sectionConfig\" class=\"form-group\">\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\n <lib-form-field\n [config]=\"field\"\n [controller]=\"controller\"\n [sectionIndex]=\"sectionIndex\">\n </lib-form-field>\n </ng-container>\n </div>\n\n <!-- Text Input -->\n <div *ngIf=\"isTextField\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <textarea\n *ngIf=\"config.subType === 'LONG'\"\n class=\"field-input textarea\"\n [placeholder]=\"config.hint || ''\"\n [value]=\"value || ''\"\n [disabled]=\"config.disabled\"\n (input)=\"onValueChange($event)\"\n rows=\"4\">\n </textarea>\n \n <input\n *ngIf=\"config.subType !== 'LONG'\"\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\"\n class=\"field-input\"\n [placeholder]=\"config.hint || ''\"\n [value]=\"value || ''\"\n [disabled]=\"config.disabled\"\n (input)=\"onValueChange($event)\">\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Number Input -->\n <div *ngIf=\"isNumberField\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <input\n type=\"number\"\n class=\"field-input\"\n [placeholder]=\"config.hint || ''\"\n [value]=\"value || ''\"\n [disabled]=\"config.disabled\"\n [min]=\"config.numberConfig?.min\"\n [max]=\"config.numberConfig?.max\"\n [step]=\"config.numberConfig?.step || 1\"\n (input)=\"onValueChange($event)\">\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Date Input -->\n <div *ngIf=\"isDateField\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <input\n type=\"date\"\n class=\"field-input\"\n [value]=\"value || ''\"\n [disabled]=\"config.disabled\"\n [min]=\"config.dateConfig?.minDate\"\n [max]=\"config.dateConfig?.maxDate\"\n (input)=\"onValueChange($event)\">\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Dropdown -->\n <div *ngIf=\"isDropdown\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <select\n *ngIf=\"config.subType === 'SINGLE'\"\n class=\"field-input\"\n [value]=\"value || ''\"\n [disabled]=\"config.disabled\"\n (change)=\"onValueChange($event)\">\n <option value=\"\">Select...</option>\n <option *ngFor=\"let option of config.optionConfig?.optionList\" [value]=\"option.code\">\n {{ option.label }}\n </option>\n </select>\n \n <select\n *ngIf=\"config.subType === 'MULTIPLE'\"\n class=\"field-input\"\n multiple\n [disabled]=\"config.disabled\"\n (change)=\"onValueChange($event)\">\n <option *ngFor=\"let option of config.optionConfig?.optionList\" [value]=\"option.code\">\n {{ option.label }}\n </option>\n </select>\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Radio -->\n <div *ngIf=\"isRadio\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <div class=\"radio-group\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\" class=\"radio-label\">\n <input\n type=\"radio\"\n [name]=\"getFieldName()\"\n [value]=\"option.code\"\n [checked]=\"value === option.code\"\n [disabled]=\"config.disabled\"\n (change)=\"onValueChange(option.code)\">\n <span>{{ option.label }}</span>\n </label>\n </div>\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Checkbox -->\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper\">\n <label *ngIf=\"config.label && config.subType === 'LIST'\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\n <label class=\"checkbox-label\">\n <input\n type=\"checkbox\"\n [checked]=\"value === true\"\n [disabled]=\"config.disabled\"\n (change)=\"onValueChange($event)\">\n <span>{{ config.label }}</span>\n </label>\n </div>\n \n <div *ngIf=\"config.subType === 'LIST'\" class=\"checkbox-group\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\" class=\"checkbox-label\">\n <input\n type=\"checkbox\"\n [checked]=\"isChecked(option.code)\"\n [disabled]=\"config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\n <span>{{ option.label }}</span>\n </label>\n </div>\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Chip -->\n <div *ngIf=\"isChip\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <div class=\"chip-group\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\" \n class=\"chip-label\"\n [class.selected]=\"isChecked(option.code)\">\n <input\n type=\"checkbox\"\n [checked]=\"isChecked(option.code)\"\n [disabled]=\"config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\"\n style=\"display: none;\">\n <span>{{ option.label }}</span>\n </label>\n </div>\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Switch -->\n <div *ngIf=\"isSwitch\" class=\"field-wrapper\">\n <label class=\"switch-container\">\n <span class=\"field-label\">{{ config.label }}</span>\n <div class=\"switch\">\n <input\n type=\"checkbox\"\n [checked]=\"value === true\"\n [disabled]=\"config.disabled\"\n (change)=\"onValueChange($event)\">\n <span class=\"slider\"></span>\n </div>\n </label>\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Rating -->\n <div *ngIf=\"isRating\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <div class=\"rating-group\">\n <span *ngFor=\"let star of getStarArray()\" \n class=\"star\"\n [class.filled]=\"isStarFilled(star)\"\n [class.half]=\"isStarHalf(star)\"\n (click)=\"onRatingChange(star, $event)\">\n \u2605\n </span>\n </div>\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Generated Field (Read-only) -->\n <div *ngIf=\"isGenerated\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">{{ config.label }}</label>\n <div class=\"generated-value\">{{ value || '-' }}</div>\n <span class=\"field-hint\" *ngIf=\"config.hint\">{{ config.hint }}</span>\n </div>\n</div>\n", styles: [".form-field{margin-bottom:16px}.form-field.has-error .field-input{border-color:#f44336}.form-row{display:flex;gap:16px}.form-row.horizontal{flex-direction:row}.form-row.horizontal>*{flex:1}.form-row:not(.horizontal){flex-direction:column}.field-wrapper{display:flex;flex-direction:column;gap:6px}.field-label{font-size:14px;font-weight:500;color:#333}.field-label .required{color:#f44336;margin-left:4px}.field-input{padding:10px 12px;border:1px solid #ddd;border-radius:4px;font-size:14px;transition:border-color .3s}.field-input:focus{outline:none;border-color:#2196f3}.field-input:disabled{background:#f5f5f5;cursor:not-allowed}.field-input.textarea{resize:vertical;font-family:inherit}.field-hint{font-size:12px;color:#666}.field-error{font-size:12px;color:#f44336}.radio-group,.checkbox-group{display:flex;flex-direction:column;gap:8px}.radio-label,.checkbox-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-size:14px}.radio-label input,.checkbox-label input{cursor:pointer}.checkbox-single .checkbox-label{font-weight:500}.chip-group{display:flex;flex-wrap:wrap;gap:8px}.chip-label{padding:8px 16px;border:1px solid #ddd;border-radius:20px;cursor:pointer;font-size:14px;transition:all .3s}.chip-label:hover{background:#f5f5f5}.chip-label.selected{background:#2196f3;color:#fff;border-color:#2196f3}.switch-container{display:flex;justify-content:space-between;align-items:center;cursor:pointer}.switch{position:relative;width:50px;height:24px}.switch input{opacity:0;width:0;height:0}.switch input:checked+.slider{background-color:#2196f3}.switch input:checked+.slider:before{transform:translate(26px)}.switch .slider{position:absolute;cursor:pointer;inset:0;background-color:#ccc;transition:.4s;border-radius:24px}.switch .slider:before{position:absolute;content:\"\";height:18px;width:18px;left:3px;bottom:3px;background-color:#fff;transition:.4s;border-radius:50%}.rating-group{display:flex;gap:4px}.rating-group .star{font-size:28px;display:inline-block;background:#ddd;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.rating-group .star.filled{background:#ffc107;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.rating-group .star.half{background:linear-gradient(90deg,#ffc107 50%,#ddd 50%);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.rating-group .star:hover{background:#ffc107;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.generated-value{padding:10px 12px;background:#f5f5f5;border:1px solid #ddd;border-radius:4px;font-size:14px;color:#666}select[multiple]{min-height:120px}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "component", type: FormFieldComponent, selector: "lib-form-field", inputs: ["config", "controller", "sectionIndex"] }] });
2069
+ }
2070
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldComponent, decorators: [{
2071
+ type: Component,
2072
+ args: [{ selector: 'lib-form-field', standalone: false, template: "<div class=\"form-field\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\n <!-- ROW Layout -->\n <div *ngIf=\"isRow\" class=\"form-row\" [class.horizontal]=\"config.subType === 'HORIZONTAL'\">\n <ng-container *ngFor=\"let child of config.children\">\n <div class=\"row-field\">\n <lib-form-field\n [config]=\"child\"\n [controller]=\"controller\"\n [sectionIndex]=\"sectionIndex\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n\n <!-- GROUP (Section) -->\n <div *ngIf=\"isGroup && config.sectionConfig\" class=\"form-group\">\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\n <lib-form-field\n [config]=\"field\"\n [controller]=\"controller\"\n [sectionIndex]=\"sectionIndex\">\n </lib-form-field>\n </ng-container>\n </div>\n\n <!-- Text Input -->\n <div *ngIf=\"isTextField\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <textarea\n *ngIf=\"config.subType === 'LONG'\"\n class=\"field-input textarea\"\n [placeholder]=\"config.hint || ''\"\n [value]=\"value || ''\"\n [disabled]=\"config.disabled\"\n (input)=\"onValueChange($event)\"\n rows=\"4\">\n </textarea>\n \n <input\n *ngIf=\"config.subType !== 'LONG'\"\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\"\n class=\"field-input\"\n [placeholder]=\"config.hint || ''\"\n [value]=\"value || ''\"\n [disabled]=\"config.disabled\"\n (input)=\"onValueChange($event)\">\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Number Input -->\n <div *ngIf=\"isNumberField\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <input\n type=\"number\"\n class=\"field-input\"\n [placeholder]=\"config.hint || ''\"\n [value]=\"value || ''\"\n [disabled]=\"config.disabled\"\n [min]=\"config.numberConfig?.min\"\n [max]=\"config.numberConfig?.max\"\n [step]=\"config.numberConfig?.step || 1\"\n (input)=\"onValueChange($event)\">\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Date Input -->\n <div *ngIf=\"isDateField\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <input\n type=\"date\"\n class=\"field-input\"\n [value]=\"value || ''\"\n [disabled]=\"config.disabled\"\n [min]=\"config.dateConfig?.minDate\"\n [max]=\"config.dateConfig?.maxDate\"\n (input)=\"onValueChange($event)\">\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Dropdown -->\n <div *ngIf=\"isDropdown\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <select\n *ngIf=\"config.subType === 'SINGLE'\"\n class=\"field-input\"\n [value]=\"value || ''\"\n [disabled]=\"config.disabled\"\n (change)=\"onValueChange($event)\">\n <option value=\"\">Select...</option>\n <option *ngFor=\"let option of config.optionConfig?.optionList\" [value]=\"option.code\">\n {{ option.label }}\n </option>\n </select>\n \n <select\n *ngIf=\"config.subType === 'MULTIPLE'\"\n class=\"field-input\"\n multiple\n [disabled]=\"config.disabled\"\n (change)=\"onValueChange($event)\">\n <option *ngFor=\"let option of config.optionConfig?.optionList\" [value]=\"option.code\">\n {{ option.label }}\n </option>\n </select>\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Radio -->\n <div *ngIf=\"isRadio\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <div class=\"radio-group\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\" class=\"radio-label\">\n <input\n type=\"radio\"\n [name]=\"getFieldName()\"\n [value]=\"option.code\"\n [checked]=\"value === option.code\"\n [disabled]=\"config.disabled\"\n (change)=\"onValueChange(option.code)\">\n <span>{{ option.label }}</span>\n </label>\n </div>\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Checkbox -->\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper\">\n <label *ngIf=\"config.label && config.subType === 'LIST'\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\n <label class=\"checkbox-label\">\n <input\n type=\"checkbox\"\n [checked]=\"value === true\"\n [disabled]=\"config.disabled\"\n (change)=\"onValueChange($event)\">\n <span>{{ config.label }}</span>\n </label>\n </div>\n \n <div *ngIf=\"config.subType === 'LIST'\" class=\"checkbox-group\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\" class=\"checkbox-label\">\n <input\n type=\"checkbox\"\n [checked]=\"isChecked(option.code)\"\n [disabled]=\"config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\n <span>{{ option.label }}</span>\n </label>\n </div>\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Chip -->\n <div *ngIf=\"isChip\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <div class=\"chip-group\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\" \n class=\"chip-label\"\n [class.selected]=\"isChecked(option.code)\">\n <input\n type=\"checkbox\"\n [checked]=\"isChecked(option.code)\"\n [disabled]=\"config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\"\n style=\"display: none;\">\n <span>{{ option.label }}</span>\n </label>\n </div>\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Switch -->\n <div *ngIf=\"isSwitch\" class=\"field-wrapper\">\n <label class=\"switch-container\">\n <span class=\"field-label\">{{ config.label }}</span>\n <div class=\"switch\">\n <input\n type=\"checkbox\"\n [checked]=\"value === true\"\n [disabled]=\"config.disabled\"\n (change)=\"onValueChange($event)\">\n <span class=\"slider\"></span>\n </div>\n </label>\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Rating -->\n <div *ngIf=\"isRating\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">\n {{ config.label }}\n <span class=\"required\" *ngIf=\"config.required\">*</span>\n </label>\n \n <div class=\"rating-group\">\n <span *ngFor=\"let star of getStarArray()\" \n class=\"star\"\n [class.filled]=\"isStarFilled(star)\"\n [class.half]=\"isStarHalf(star)\"\n (click)=\"onRatingChange(star, $event)\">\n \u2605\n </span>\n </div>\n \n <span class=\"field-hint\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- Generated Field (Read-only) -->\n <div *ngIf=\"isGenerated\" class=\"field-wrapper\">\n <label *ngIf=\"config.label\" class=\"field-label\">{{ config.label }}</label>\n <div class=\"generated-value\">{{ value || '-' }}</div>\n <span class=\"field-hint\" *ngIf=\"config.hint\">{{ config.hint }}</span>\n </div>\n</div>\n", styles: [".form-field{margin-bottom:16px}.form-field.has-error .field-input{border-color:#f44336}.form-row{display:flex;gap:16px}.form-row.horizontal{flex-direction:row}.form-row.horizontal>*{flex:1}.form-row:not(.horizontal){flex-direction:column}.field-wrapper{display:flex;flex-direction:column;gap:6px}.field-label{font-size:14px;font-weight:500;color:#333}.field-label .required{color:#f44336;margin-left:4px}.field-input{padding:10px 12px;border:1px solid #ddd;border-radius:4px;font-size:14px;transition:border-color .3s}.field-input:focus{outline:none;border-color:#2196f3}.field-input:disabled{background:#f5f5f5;cursor:not-allowed}.field-input.textarea{resize:vertical;font-family:inherit}.field-hint{font-size:12px;color:#666}.field-error{font-size:12px;color:#f44336}.radio-group,.checkbox-group{display:flex;flex-direction:column;gap:8px}.radio-label,.checkbox-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-size:14px}.radio-label input,.checkbox-label input{cursor:pointer}.checkbox-single .checkbox-label{font-weight:500}.chip-group{display:flex;flex-wrap:wrap;gap:8px}.chip-label{padding:8px 16px;border:1px solid #ddd;border-radius:20px;cursor:pointer;font-size:14px;transition:all .3s}.chip-label:hover{background:#f5f5f5}.chip-label.selected{background:#2196f3;color:#fff;border-color:#2196f3}.switch-container{display:flex;justify-content:space-between;align-items:center;cursor:pointer}.switch{position:relative;width:50px;height:24px}.switch input{opacity:0;width:0;height:0}.switch input:checked+.slider{background-color:#2196f3}.switch input:checked+.slider:before{transform:translate(26px)}.switch .slider{position:absolute;cursor:pointer;inset:0;background-color:#ccc;transition:.4s;border-radius:24px}.switch .slider:before{position:absolute;content:\"\";height:18px;width:18px;left:3px;bottom:3px;background-color:#fff;transition:.4s;border-radius:50%}.rating-group{display:flex;gap:4px}.rating-group .star{font-size:28px;display:inline-block;background:#ddd;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.rating-group .star.filled{background:#ffc107;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.rating-group .star.half{background:linear-gradient(90deg,#ffc107 50%,#ddd 50%);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.rating-group .star:hover{background:#ffc107;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.generated-value{padding:10px 12px;background:#f5f5f5;border:1px solid #ddd;border-radius:4px;font-size:14px;color:#666}select[multiple]{min-height:120px}\n"] }]
2073
+ }], ctorParameters: () => [{ type: ExpressionService }, { type: i3.HttpClient }], propDecorators: { config: [{
2074
+ type: Input
2075
+ }], controller: [{
2076
+ type: Input
2077
+ }], sectionIndex: [{
2078
+ type: Input
2079
+ }] } });
2080
+
2081
+ class FormSectionComponent {
2082
+ config;
2083
+ controller;
2084
+ sections = [{}];
2085
+ addSection() {
2086
+ if (this.config.allowMulti) {
2087
+ this.sections.push({});
2088
+ }
2089
+ }
2090
+ removeSection(index) {
2091
+ if (this.sections.length > 1) {
2092
+ this.sections.splice(index, 1);
2093
+ }
2094
+ }
2095
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormSectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2096
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FormSectionComponent, isStandalone: false, selector: "lib-form-section", inputs: { config: "config", controller: "controller" }, ngImport: i0, template: "<div class=\"form-section-container\">\n <h3 class=\"section-label\" *ngIf=\"config.label\">{{ config.label }}</h3>\n \n <div *ngFor=\"let section of sections; let sectionIndex = index\" class=\"section-instance\">\n <div class=\"section-header\" *ngIf=\"config.allowMulti && sections.length > 1\">\n <span class=\"section-number\">{{ config.label }} #{{ sectionIndex + 1 }}</span>\n <button \n type=\"button\" \n class=\"btn-remove\"\n (click)=\"removeSection(sectionIndex)\"\n *ngIf=\"sectionIndex > 0\">\n Remove\n </button>\n </div>\n \n <div class=\"section-fields\">\n <ng-container *ngFor=\"let field of config.children\">\n <lib-form-field\n [config]=\"field\"\n [controller]=\"controller\"\n [sectionIndex]=\"config.allowMulti ? sectionIndex : undefined\">\n </lib-form-field>\n </ng-container>\n </div>\n </div>\n \n <button \n type=\"button\" \n class=\"btn-add-section\"\n *ngIf=\"config.allowMulti\"\n (click)=\"addSection()\">\n + Add {{ config.label }}\n </button>\n</div>\n", styles: [".form-section-container{margin-bottom:24px}.form-section-container .section-label{font-size:18px;font-weight:600;color:#333;margin:0 0 16px}.form-section-container .section-instance{margin-bottom:16px;padding:16px;border:1px solid #e0e0e0;border-radius:4px}.form-section-container .section-instance:last-of-type{margin-bottom:0}.form-section-container .section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;padding-bottom:12px;border-bottom:1px solid #e0e0e0}.form-section-container .section-header .section-number{font-weight:600;color:#333}.form-section-container .section-header .btn-remove{padding:6px 12px;background:#f44336;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px}.form-section-container .section-header .btn-remove:hover{background:#d32f2f}.form-section-container .section-fields{display:flex;flex-direction:column;gap:16px}.form-section-container .btn-add-section{padding:10px 20px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:14px;margin-top:16px}.form-section-container .btn-add-section:hover{background:#1976d2}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: FormFieldComponent, selector: "lib-form-field", inputs: ["config", "controller", "sectionIndex"] }] });
2097
+ }
2098
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormSectionComponent, decorators: [{
2099
+ type: Component,
2100
+ args: [{ selector: 'lib-form-section', standalone: false, template: "<div class=\"form-section-container\">\n <h3 class=\"section-label\" *ngIf=\"config.label\">{{ config.label }}</h3>\n \n <div *ngFor=\"let section of sections; let sectionIndex = index\" class=\"section-instance\">\n <div class=\"section-header\" *ngIf=\"config.allowMulti && sections.length > 1\">\n <span class=\"section-number\">{{ config.label }} #{{ sectionIndex + 1 }}</span>\n <button \n type=\"button\" \n class=\"btn-remove\"\n (click)=\"removeSection(sectionIndex)\"\n *ngIf=\"sectionIndex > 0\">\n Remove\n </button>\n </div>\n \n <div class=\"section-fields\">\n <ng-container *ngFor=\"let field of config.children\">\n <lib-form-field\n [config]=\"field\"\n [controller]=\"controller\"\n [sectionIndex]=\"config.allowMulti ? sectionIndex : undefined\">\n </lib-form-field>\n </ng-container>\n </div>\n </div>\n \n <button \n type=\"button\" \n class=\"btn-add-section\"\n *ngIf=\"config.allowMulti\"\n (click)=\"addSection()\">\n + Add {{ config.label }}\n </button>\n</div>\n", styles: [".form-section-container{margin-bottom:24px}.form-section-container .section-label{font-size:18px;font-weight:600;color:#333;margin:0 0 16px}.form-section-container .section-instance{margin-bottom:16px;padding:16px;border:1px solid #e0e0e0;border-radius:4px}.form-section-container .section-instance:last-of-type{margin-bottom:0}.form-section-container .section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;padding-bottom:12px;border-bottom:1px solid #e0e0e0}.form-section-container .section-header .section-number{font-weight:600;color:#333}.form-section-container .section-header .btn-remove{padding:6px 12px;background:#f44336;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px}.form-section-container .section-header .btn-remove:hover{background:#d32f2f}.form-section-container .section-fields{display:flex;flex-direction:column;gap:16px}.form-section-container .btn-add-section{padding:10px 20px;background:#2196f3;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:14px;margin-top:16px}.form-section-container .btn-add-section:hover{background:#1976d2}\n"] }]
2101
+ }], propDecorators: { config: [{
2102
+ type: Input
2103
+ }], controller: [{
2104
+ type: Input
2105
+ }] } });
2106
+
2107
+ class SmartFormComponent {
2108
+ fb;
2109
+ controller;
2110
+ expressionService;
2111
+ http;
2112
+ formJson;
2113
+ initialValues;
2114
+ enableDraftAutoSave = false;
2115
+ submit = new EventEmitter();
2116
+ draftSave = new EventEmitter();
2117
+ formSchema;
2118
+ formGroup;
2119
+ fieldList = [];
2120
+ isStepper = false;
2121
+ currentStep = 0;
2122
+ isLoading = false;
2123
+ constructor(fb, controller, expressionService, http) {
2124
+ this.fb = fb;
2125
+ this.controller = controller;
2126
+ this.expressionService = expressionService;
2127
+ this.http = http;
2128
+ }
2129
+ ngOnInit() {
2130
+ if (this.initialValues) {
2131
+ this.controller.initialize(this.initialValues);
2132
+ }
2133
+ this.parseFormJson();
2134
+ }
2135
+ ngOnChanges(changes) {
2136
+ if (changes['formJson'] && !changes['formJson'].isFirstChange()) {
2137
+ this.parseFormJson();
2138
+ }
2139
+ }
2140
+ ngOnDestroy() {
2141
+ this.controller.destroy();
2142
+ }
2143
+ parseFormJson() {
2144
+ try {
2145
+ const jsonData = JSON.parse(this.formJson);
2146
+ this.formSchema = jsonData;
2147
+ this.isStepper = this.formSchema.formType === 'STEPPER';
2148
+ this.fieldList = this.isStepper
2149
+ ? this.formSchema.stepperConfig?.children || []
2150
+ : this.formSchema.sectionConfig?.children || [];
2151
+ this.initializeForm();
2152
+ }
2153
+ catch (e) {
2154
+ console.error('Error parsing form JSON:', e);
2155
+ }
2156
+ }
2157
+ initializeForm() {
2158
+ this.formGroup = this.fb.group({});
2159
+ this.collectFields(this.fieldList);
2160
+ }
2161
+ collectFields(fields) {
2162
+ fields.forEach(field => {
2163
+ if (field.name) {
2164
+ const value = field.defaultValue !== undefined ? field.defaultValue : null;
2165
+ this.controller.updateField(field.name, value);
2166
+ }
2167
+ if (field.children && field.children.length > 0) {
2168
+ this.collectFields(field.children);
2169
+ }
2170
+ });
2171
+ }
2172
+ handleSubmit() {
2173
+ if (this.isStepper && this.currentStep < this.fieldList.length - 1) {
2174
+ this.nextStep();
2175
+ return;
2176
+ }
2177
+ const formData = this.controller.getAllData();
2178
+ this.isLoading = true;
2179
+ if (this.formSchema.submitConfig?.apiUrl) {
2180
+ this.submitToApi(formData);
2181
+ }
2182
+ else {
2183
+ this.submit.emit(formData);
2184
+ this.isLoading = false;
2185
+ }
2186
+ }
2187
+ submitToApi(formData) {
2188
+ const config = this.formSchema.submitConfig;
2189
+ const method = config.method || 'POST';
2190
+ this.http.request(method, config.apiUrl, { body: formData })
2191
+ .subscribe({
2192
+ next: (response) => {
2193
+ alert(config.successMessage || 'Form submitted successfully');
2194
+ this.submit.emit(response);
2195
+ this.isLoading = false;
2196
+ },
2197
+ error: (err) => {
2198
+ alert(config.errorMessage || 'Failed to submit form');
2199
+ console.error('Submit error:', err);
2200
+ this.isLoading = false;
2201
+ }
2202
+ });
2203
+ }
2204
+ nextStep() {
2205
+ if (this.currentStep < this.fieldList.length - 1) {
2206
+ this.currentStep++;
2207
+ }
2208
+ }
2209
+ previousStep() {
2210
+ if (this.currentStep > 0) {
2211
+ this.currentStep--;
2212
+ }
2213
+ }
2214
+ get canGoNext() {
2215
+ return this.currentStep < this.fieldList.length - 1;
2216
+ }
2217
+ get canGoPrevious() {
2218
+ return this.currentStep > 0;
2219
+ }
2220
+ get currentStepConfig() {
2221
+ return this.isStepper ? this.fieldList[this.currentStep] : undefined;
2222
+ }
2223
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartFormComponent, deps: [{ token: i1$2.FormBuilder }, { token: SmartFormController }, { token: ExpressionService }, { token: i3.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
2224
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SmartFormComponent, isStandalone: false, selector: "lib-smart-form", inputs: { formJson: "formJson", initialValues: "initialValues", enableDraftAutoSave: "enableDraftAutoSave" }, outputs: { submit: "submit", draftSave: "draftSave" }, providers: [SmartFormController], usesOnChanges: true, ngImport: i0, template: "<div class=\"smart-form-container\">\n <div class=\"smart-form-wrapper\" *ngIf=\"formSchema\">\n <!-- Form Header -->\n <div class=\"form-header\" *ngIf=\"formSchema.showTitle !== false\">\n <h2 class=\"form-title\">{{ formSchema.label }}</h2>\n <p class=\"form-description\" *ngIf=\"formSchema.description\">{{ formSchema.description }}</p>\n </div>\n\n <!-- Stepper Navigation -->\n <div class=\"stepper-nav\" *ngIf=\"isStepper && formSchema.stepperConfig?.showStep !== false\">\n <div class=\"stepper-steps\" [class.horizontal]=\"formSchema.stepperConfig?.isHorizontal !== false\">\n <div \n *ngFor=\"let step of fieldList; let i = index\"\n class=\"stepper-step\"\n [class.active]=\"i === currentStep\"\n [class.completed]=\"i < currentStep\">\n <div class=\"step-number\">{{ i + 1 }}</div>\n <div class=\"step-label\">{{ step.sectionConfig?.label || 'Step ' + (i + 1) }}</div>\n </div>\n </div>\n </div>\n\n <!-- Form Content -->\n <form [formGroup]=\"formGroup\" class=\"smart-form\">\n <!-- Section Form -->\n <div *ngIf=\"!isStepper && formSchema.sectionConfig\" class=\"form-section\">\n <lib-form-section \n [config]=\"formSchema.sectionConfig\"\n [controller]=\"controller\">\n </lib-form-section>\n </div>\n\n <!-- Stepper Form -->\n <div *ngIf=\"isStepper && currentStepConfig\" class=\"form-stepper\">\n <lib-form-section \n [config]=\"currentStepConfig.sectionConfig!\"\n [controller]=\"controller\">\n </lib-form-section>\n </div>\n </form>\n\n <!-- Form Actions -->\n <div class=\"form-actions\">\n <button \n *ngIf=\"isStepper && canGoPrevious\"\n type=\"button\" \n class=\"btn btn-secondary\"\n (click)=\"previousStep()\">\n Previous\n </button>\n \n <button \n type=\"button\" \n class=\"btn btn-primary\"\n [disabled]=\"isLoading\"\n (click)=\"handleSubmit()\">\n {{ isStepper && canGoNext ? 'Next' : 'Submit' }}\n </button>\n </div>\n </div>\n</div>\n", styles: [".smart-form-container{width:100%;max-width:1200px;margin:0 auto;padding:20px}.smart-form-wrapper{background:#fff;border-radius:8px;box-shadow:0 2px 8px #0000001a;padding:24px}.form-header{margin-bottom:24px}.form-header .form-title{font-size:24px;font-weight:600;color:#333;margin:0 0 8px}.form-header .form-description{font-size:14px;color:#666;margin:0}.stepper-nav{margin-bottom:32px}.stepper-nav .stepper-steps{display:flex;gap:16px}.stepper-nav .stepper-steps.horizontal{flex-direction:row;justify-content:space-between}.stepper-nav .stepper-steps:not(.horizontal){flex-direction:column}.stepper-nav .stepper-step{display:flex;align-items:center;gap:12px;flex:1;position:relative}.stepper-nav .stepper-step:not(:last-child):after{content:\"\";position:absolute;top:20px;left:calc(100% + 8px);width:calc(100% - 40px);height:2px;background:#e0e0e0}.stepper-nav .stepper-step.completed:after{background:#4caf50}.stepper-nav .stepper-step .step-number{width:40px;height:40px;border-radius:50%;background:#e0e0e0;color:#666;display:flex;align-items:center;justify-content:center;font-weight:600;transition:all .3s}.stepper-nav .stepper-step .step-label{font-size:14px;color:#666;font-weight:500}.stepper-nav .stepper-step.active .step-number{background:#2196f3;color:#fff}.stepper-nav .stepper-step.active .step-label{color:#2196f3;font-weight:600}.stepper-nav .stepper-step.completed .step-number{background:#4caf50;color:#fff}.smart-form{margin-bottom:24px}.form-actions{display:flex;justify-content:flex-end;gap:12px;padding-top:24px;border-top:1px solid #e0e0e0}.form-actions .btn{padding:10px 24px;border-radius:4px;font-size:14px;font-weight:500;border:none;cursor:pointer;transition:all .3s}.form-actions .btn.btn-primary{background:#2196f3;color:#fff}.form-actions .btn.btn-primary:hover:not(:disabled){background:#1976d2}.form-actions .btn.btn-primary:disabled{opacity:.6;cursor:not-allowed}.form-actions .btn.btn-secondary{background:#f5f5f5;color:#333}.form-actions .btn.btn-secondary:hover{background:#e0e0e0}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: FormSectionComponent, selector: "lib-form-section", inputs: ["config", "controller"] }] });
2225
+ }
2226
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartFormComponent, decorators: [{
2227
+ type: Component,
2228
+ args: [{ selector: 'lib-smart-form', providers: [SmartFormController], standalone: false, template: "<div class=\"smart-form-container\">\n <div class=\"smart-form-wrapper\" *ngIf=\"formSchema\">\n <!-- Form Header -->\n <div class=\"form-header\" *ngIf=\"formSchema.showTitle !== false\">\n <h2 class=\"form-title\">{{ formSchema.label }}</h2>\n <p class=\"form-description\" *ngIf=\"formSchema.description\">{{ formSchema.description }}</p>\n </div>\n\n <!-- Stepper Navigation -->\n <div class=\"stepper-nav\" *ngIf=\"isStepper && formSchema.stepperConfig?.showStep !== false\">\n <div class=\"stepper-steps\" [class.horizontal]=\"formSchema.stepperConfig?.isHorizontal !== false\">\n <div \n *ngFor=\"let step of fieldList; let i = index\"\n class=\"stepper-step\"\n [class.active]=\"i === currentStep\"\n [class.completed]=\"i < currentStep\">\n <div class=\"step-number\">{{ i + 1 }}</div>\n <div class=\"step-label\">{{ step.sectionConfig?.label || 'Step ' + (i + 1) }}</div>\n </div>\n </div>\n </div>\n\n <!-- Form Content -->\n <form [formGroup]=\"formGroup\" class=\"smart-form\">\n <!-- Section Form -->\n <div *ngIf=\"!isStepper && formSchema.sectionConfig\" class=\"form-section\">\n <lib-form-section \n [config]=\"formSchema.sectionConfig\"\n [controller]=\"controller\">\n </lib-form-section>\n </div>\n\n <!-- Stepper Form -->\n <div *ngIf=\"isStepper && currentStepConfig\" class=\"form-stepper\">\n <lib-form-section \n [config]=\"currentStepConfig.sectionConfig!\"\n [controller]=\"controller\">\n </lib-form-section>\n </div>\n </form>\n\n <!-- Form Actions -->\n <div class=\"form-actions\">\n <button \n *ngIf=\"isStepper && canGoPrevious\"\n type=\"button\" \n class=\"btn btn-secondary\"\n (click)=\"previousStep()\">\n Previous\n </button>\n \n <button \n type=\"button\" \n class=\"btn btn-primary\"\n [disabled]=\"isLoading\"\n (click)=\"handleSubmit()\">\n {{ isStepper && canGoNext ? 'Next' : 'Submit' }}\n </button>\n </div>\n </div>\n</div>\n", styles: [".smart-form-container{width:100%;max-width:1200px;margin:0 auto;padding:20px}.smart-form-wrapper{background:#fff;border-radius:8px;box-shadow:0 2px 8px #0000001a;padding:24px}.form-header{margin-bottom:24px}.form-header .form-title{font-size:24px;font-weight:600;color:#333;margin:0 0 8px}.form-header .form-description{font-size:14px;color:#666;margin:0}.stepper-nav{margin-bottom:32px}.stepper-nav .stepper-steps{display:flex;gap:16px}.stepper-nav .stepper-steps.horizontal{flex-direction:row;justify-content:space-between}.stepper-nav .stepper-steps:not(.horizontal){flex-direction:column}.stepper-nav .stepper-step{display:flex;align-items:center;gap:12px;flex:1;position:relative}.stepper-nav .stepper-step:not(:last-child):after{content:\"\";position:absolute;top:20px;left:calc(100% + 8px);width:calc(100% - 40px);height:2px;background:#e0e0e0}.stepper-nav .stepper-step.completed:after{background:#4caf50}.stepper-nav .stepper-step .step-number{width:40px;height:40px;border-radius:50%;background:#e0e0e0;color:#666;display:flex;align-items:center;justify-content:center;font-weight:600;transition:all .3s}.stepper-nav .stepper-step .step-label{font-size:14px;color:#666;font-weight:500}.stepper-nav .stepper-step.active .step-number{background:#2196f3;color:#fff}.stepper-nav .stepper-step.active .step-label{color:#2196f3;font-weight:600}.stepper-nav .stepper-step.completed .step-number{background:#4caf50;color:#fff}.smart-form{margin-bottom:24px}.form-actions{display:flex;justify-content:flex-end;gap:12px;padding-top:24px;border-top:1px solid #e0e0e0}.form-actions .btn{padding:10px 24px;border-radius:4px;font-size:14px;font-weight:500;border:none;cursor:pointer;transition:all .3s}.form-actions .btn.btn-primary{background:#2196f3;color:#fff}.form-actions .btn.btn-primary:hover:not(:disabled){background:#1976d2}.form-actions .btn.btn-primary:disabled{opacity:.6;cursor:not-allowed}.form-actions .btn.btn-secondary{background:#f5f5f5;color:#333}.form-actions .btn.btn-secondary:hover{background:#e0e0e0}\n"] }]
2229
+ }], ctorParameters: () => [{ type: i1$2.FormBuilder }, { type: SmartFormController }, { type: ExpressionService }, { type: i3.HttpClient }], propDecorators: { formJson: [{
2230
+ type: Input
2231
+ }], initialValues: [{
2232
+ type: Input
2233
+ }], enableDraftAutoSave: [{
2234
+ type: Input
2235
+ }], submit: [{
2236
+ type: Output
2237
+ }], draftSave: [{
2238
+ type: Output
2239
+ }] } });
2240
+
2241
+ class SmartFormModule {
2242
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartFormModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2243
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: SmartFormModule, declarations: [SmartFormComponent,
2244
+ FormSectionComponent,
2245
+ FormFieldComponent], imports: [CommonModule,
2246
+ ReactiveFormsModule,
2247
+ FormsModule], exports: [SmartFormComponent] });
2248
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartFormModule, providers: [
2249
+ ExpressionService
2250
+ ], imports: [CommonModule,
2251
+ ReactiveFormsModule,
2252
+ FormsModule] });
2253
+ }
2254
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartFormModule, decorators: [{
2255
+ type: NgModule,
2256
+ args: [{
2257
+ declarations: [
2258
+ SmartFormComponent,
2259
+ FormSectionComponent,
2260
+ FormFieldComponent
2261
+ ],
2262
+ imports: [
2263
+ CommonModule,
2264
+ ReactiveFormsModule,
2265
+ FormsModule
2266
+ ],
2267
+ exports: [
2268
+ SmartFormComponent
2269
+ ],
2270
+ providers: [
2271
+ ExpressionService
2272
+ ]
2273
+ }]
2274
+ }] });
2275
+
1581
2276
  class SharedUiModule {
1582
2277
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SharedUiModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
1583
2278
  static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: SharedUiModule, imports: [CommonModule,
1584
2279
  MaterialModule,
1585
- CardsModule, AlertModule, ButtonModule, ConfirmationModalModule, FilterSidebarModule,
2280
+ AlertModule, ButtonModule, ConfirmationModalModule, FilterSidebarModule,
1586
2281
  SummaryCardModule,
1587
- ConfigurableFormModule], exports: [MaterialModule,
1588
- CardsModule, AlertModule, ButtonModule, ConfirmationModalModule, FilterSidebarModule,
2282
+ ConfigurableFormModule,
2283
+ SmartFormModule], exports: [MaterialModule,
2284
+ AlertModule, ButtonModule, ConfirmationModalModule, FilterSidebarModule,
1589
2285
  SummaryCardModule,
1590
- ConfigurableFormModule] });
2286
+ ConfigurableFormModule,
2287
+ SmartFormModule] });
1591
2288
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SharedUiModule, imports: [CommonModule,
1592
2289
  MaterialModule,
1593
- CardsModule, AlertModule, ButtonModule, ConfirmationModalModule, FilterSidebarModule,
2290
+ AlertModule, ButtonModule, ConfirmationModalModule, FilterSidebarModule,
1594
2291
  SummaryCardModule,
1595
- ConfigurableFormModule, MaterialModule,
1596
- CardsModule, AlertModule, ButtonModule, ConfirmationModalModule, FilterSidebarModule,
2292
+ ConfigurableFormModule,
2293
+ SmartFormModule, MaterialModule,
2294
+ AlertModule, ButtonModule, ConfirmationModalModule, FilterSidebarModule,
1597
2295
  SummaryCardModule,
1598
- ConfigurableFormModule] });
2296
+ ConfigurableFormModule,
2297
+ SmartFormModule] });
1599
2298
  }
1600
2299
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SharedUiModule, decorators: [{
1601
2300
  type: NgModule,
@@ -1604,15 +2303,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
1604
2303
  imports: [
1605
2304
  CommonModule,
1606
2305
  MaterialModule,
1607
- CardsModule, AlertModule, ButtonModule, ConfirmationModalModule, FilterSidebarModule,
2306
+ AlertModule, ButtonModule, ConfirmationModalModule, FilterSidebarModule,
1608
2307
  SummaryCardModule,
1609
2308
  ConfigurableFormModule,
2309
+ SmartFormModule,
1610
2310
  ],
1611
2311
  exports: [
1612
2312
  MaterialModule,
1613
- CardsModule, AlertModule, ButtonModule, ConfirmationModalModule, FilterSidebarModule,
2313
+ AlertModule, ButtonModule, ConfirmationModalModule, FilterSidebarModule,
1614
2314
  SummaryCardModule,
1615
2315
  ConfigurableFormModule,
2316
+ SmartFormModule,
1616
2317
  ],
1617
2318
  }]
1618
2319
  }] });
@@ -2042,6 +2743,7 @@ class SmartTableComponent {
2042
2743
  topAction = new EventEmitter(); // For top bar buttons
2043
2744
  filterChange = new EventEmitter();
2044
2745
  rowSelect = new EventEmitter();
2746
+ rowClick = new EventEmitter();
2045
2747
  data = [];
2046
2748
  totalItems = 0;
2047
2749
  currentPage = 1;
@@ -2484,12 +3186,18 @@ class SmartTableComponent {
2484
3186
  get columnCount() {
2485
3187
  return this.config.columns.length;
2486
3188
  }
3189
+ onRowClick(row) {
3190
+ const hasSelection = window.getSelection()?.toString();
3191
+ if (this.config.clickableRows && !hasSelection) {
3192
+ this.rowClick.emit(row);
3193
+ }
3194
+ }
2487
3195
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartTableComponent, deps: [{ token: i3.HttpClient }, { token: i1$1.Router }, { token: i0.ChangeDetectorRef }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
2488
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SmartTableComponent, isStandalone: false, selector: "lib-smart-table", inputs: { config: "config" }, outputs: { action: "action", topAction: "topAction", filterChange: "filterChange", rowSelect: "rowSelect" }, viewQueries: [{ propertyName: "stickyHeaders", predicate: ["stickyHeader"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"smart-table-wrapper\">\n <!-- Top Toolbar -->\n <div class=\"st-toolbar\" *ngIf=\"config.searchConfig?.enabled || (config.filters && config.filters.length > 0) || (config.topBarButtons && config.topBarButtons.length > 0)\">\n \n <!-- Search -->\n <div class=\"st-search\" *ngIf=\"config.searchConfig?.enabled\">\n <i class=\"fa fa-search\"></i>\n <input type=\"text\" [placeholder]=\"config.labels?.searchPlaceholder || 'Search'\" (input)=\"onSearch($event)\">\n </div>\n\n <!-- Filters -->\n <div class=\"st-filters\" *ngIf=\"config.filters\">\n <div class=\"st-filter-item\" *ngFor=\"let filter of config.filters\">\n <select (change)=\"onFilterChange(filter.key, $event)\">\n <option value=\"\">{{ filter.label }}</option>\n <option *ngFor=\"let opt of filter.options\" [value]=\"opt.value\">{{ opt.label }}</option>\n </select>\n </div>\n </div>\n\n <!-- Top Bar Buttons -->\n <div class=\"st-actions\" *ngIf=\"config.topBarButtons\">\n <lib-button *ngFor=\"let btn of config.topBarButtons\" \n [variant]=\"btn.btnVariant || 'primary'\"\n [icon]=\"btn.icon || ''\"\n (click)=\"onTopAction(btn)\">\n {{ btn.label }}\n </lib-button>\n </div>\n </div>\n\n <!-- Table Container -->\n <div class=\"st-table-container\">\n <div class=\"st-check-loader\" *ngIf=\"loading\">\n <div class=\"spinner\"></div>\n </div>\n <table class=\"st-table\" [class.loading-data]=\"loading\">\n <thead>\n <tr>\n <th *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\n <input type=\"checkbox\" (change)=\"onSelectAll($event)\">\n </th>\n <th *ngFor=\"let col of config.columns\" \n #stickyHeader\n [class.sortable]=\"col.sortable\"\n [class.sticky-col]=\"col.sticky\"\n [ngStyle]=\"stickyColumnStyles[col.key]\"\n (click)=\"onSort(col)\">\n {{ col.label }}\n <span *ngIf=\"col.sortable\" class=\"sort-icon\">\n <i class=\"fa\" [ngClass]=\"getSortIcon(col.key)\"></i>\n </span>\n </th>\n <th *ngIf=\"config.actions && config.actions.length > 0\">{{ config.labels?.actionColumnHeader || 'Actions' }}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let row of data\">\n <td *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\n <input type=\"checkbox\" [(ngModel)]=\"row.selected\" (change)=\"onRowSelect(row)\">\n </td>\n <td *ngFor=\"let col of config.columns\" [class.sticky-col]=\"col.sticky\" [ngStyle]=\"stickyColumnStyles[col.key]\">\n <!-- Text/Number/Date -->\n <span *ngIf=\"col.type !== 'custom' && col.type !== 'html' && col.type !== 'badge'\">\n {{ getCellValue(row, col) }}\n </span>\n <!-- HTML -->\n <div *ngIf=\"col.type === 'html'\" [innerHTML]=\"getCellValue(row, col)\"></div>\n <!-- Badge -->\n <span *ngIf=\"col.type === 'badge'\" class=\"st-badge\" [ngClass]=\"getBadgeClass(row, col)\">\n {{ getCellValue(row, col) }}\n </span>\n </td>\n \n <!-- Row Actions -->\n <td *ngIf=\"config.actions && config.actions.length > 0\" class=\"st-row-actions\">\n <div class=\"action-buttons\">\n <ng-container *ngFor=\"let action of config.actions\">\n <lib-button \n [variant]=\"action.btnVariant || 'secondary'\"\n [icon]=\"action.icon || ''\"\n (click)=\"onAction(action, row)\">\n {{ action.label }}\n </lib-button>\n </ng-container>\n </div>\n <!-- Alternatively use specific icons if needed, but button component is requested -->\n </td>\n </tr>\n <tr *ngIf=\"data.length === 0 && !loading\">\n <td [attr.colspan]=\"columnCount + (config.selectable ? 1 : 0) + (config.actions ? 1 : 0)\" class=\"no-data\">\n {{ config.labels?.noDataMessage || 'No data available' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- Pagination -->\n <div class=\"st-pagination\" *ngIf=\"config.pagination && config.pagination.enabled\">\n <lib-pagination\n [totalItems]=\"totalItems\"\n [itemsPerPage]=\"config.pagination.pageSize\"\n [currentPage]=\"currentPage\"\n [pageSizeOptions]=\"config.pagination.pageSizeOptions\"\n (pageChange)=\"onPageChange($event)\"\n (itemsPerPageChange)=\"onPageSizeChange($event)\">\n </lib-pagination>\n </div>\n</div>\n", styles: [".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 4px rgba(0, 0, 0, .05));display:flex;flex-direction:column;gap:0;padding:0;border:var(--st-table-border, 1px solid #e0e0e0);overflow:hidden}.st-toolbar{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;padding:var(--st-toolbar-padding, 1rem);background:var(--st-toolbar-bg, #fff);border-bottom:var(--st-toolbar-border-bottom, 1px solid #eee);gap:var(--st-toolbar-gap, 1rem)}.st-toolbar .st-search{position:relative;width:var(--st-search-width, auto)}.st-toolbar .st-search input{padding:var(--st-search-padding, .5rem .5rem .5rem 2rem);border:var(--st-search-border, 1px solid #ccc);border-radius:var(--st-search-radius, 4px);background:var(--st-search-bg, #fff);font-size:var(--st-font-size, 14px);width:100%;color:var(--st-text-color, #333)}.st-toolbar .st-search i{position:absolute;left:.75rem;top:50%;transform:translateY(-50%);color:var(--st-search-icon-color, #999)}.st-toolbar .st-filters{display:flex;gap:1rem}.st-toolbar .st-filters select{padding:var(--st-filter-padding, .5rem);border:var(--st-filter-border, 1px solid #ccc);border-radius:var(--st-filter-radius, 4px);font-size:var(--st-filter-font-size, 14px);background:var(--st-filter-bg, #fff);color:var(--st-filter-color, #333)}.st-toolbar .st-actions{display:flex;gap:.5rem}.st-table-container{overflow-x:auto;overflow-y:auto;padding:var(--st-table-padding, 1rem)}.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, #f9f9f9);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, #f9f9f9)}.st-table-container table thead th{padding:.75rem 1rem;text-align:left;color:var(--st-header-color, #333);font-weight:var(--st-header-weight, 500);font-size:var(--st-header-size, 14px);text-transform:var(--st-header-transform, none);border-bottom:var(--st-header-border, 1px solid #eee);white-space:nowrap}.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 .sort-icon{margin-left:.5rem}.st-table-container table thead th .sort-icon .sort-icon{margin-left:.5rem;font-size:var(--st-sort-icon-size, .8em)}.st-table-container table thead th.st-checkbox-col{width:40px}.st-table-container table thead th.sticky-col{position:sticky;z-index:3;background:var(--st-header-bg, #f9f9f9);box-shadow:var(--st-sticky-shadow, 2px 0 5px -2px rgba(0, 0, 0, .1));border-right:var(--st-sticky-border-right, 1px solid rgba(0, 0, 0, .05))}.st-table-container table thead th.sticky-col:first-child{left:0}.st-table-container table tbody tr{background:var(--st-row-bg, #fff)}.st-table-container table tbody tr td{padding:var(--st-cell-padding, 1rem);color:var(--st-text-color, #333);vertical-align:middle;border-bottom:var(--st-row-border, 1px solid #eee)}.st-table-container table tbody tr td.sticky-col{position:sticky;z-index:2;background:var(--st-row-bg, #fff);box-shadow:var(--st-sticky-shadow, 2px 0 5px -2px rgba(0, 0, 0, .1));border-right:var(--st-sticky-border-right, 1px solid rgba(0, 0, 0, .05))}.st-table-container table tbody tr td.sticky-col:first-child{left:0}.st-table-container table tbody tr:hover td,.st-table-container table tbody tr:hover td.sticky-col{background:var(--st-row-hover-bg, #f9f9f9)}.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)}input[type=checkbox]{accent-color:var(--st-checkbox-color, #6200EE);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}.no-data{text-align:center;padding:2rem;color:var(--st-no-data-color, #888)}.st-pagination{padding:var(--st-pagination-padding, 1rem);border-top:var(--st-pagination-border-top, none)}@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{position:relative;min-height:200px}.st-table-container .st-table.loading-data{opacity:.5;pointer-events:none}.st-table-container .st-check-loader{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;z-index:10}.st-table-container .st-check-loader .spinner{width:40px;height:40px;border:4px solid var(--st-spinner-border-color, rgba(0, 0, 0, .1));border-left-color:var(--st-loader-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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"] }] });
3196
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SmartTableComponent, isStandalone: false, selector: "lib-smart-table", inputs: { config: "config" }, outputs: { action: "action", topAction: "topAction", filterChange: "filterChange", rowSelect: "rowSelect", rowClick: "rowClick" }, viewQueries: [{ propertyName: "stickyHeaders", predicate: ["stickyHeader"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"smart-table-wrapper\">\n <!-- Top Toolbar -->\n <div class=\"st-toolbar\" *ngIf=\"config.searchConfig?.enabled || (config.filters && config.filters.length > 0) || (config.topBarButtons && config.topBarButtons.length > 0)\">\n \n <!-- Search -->\n <div class=\"st-search\" *ngIf=\"config.searchConfig?.enabled\">\n <i class=\"fa fa-search\"></i>\n <input type=\"text\" [placeholder]=\"config.labels?.searchPlaceholder || 'Search'\" (input)=\"onSearch($event)\">\n </div>\n\n <!-- Filters -->\n <div class=\"st-filters\" *ngIf=\"config.filters\">\n <div class=\"st-filter-item\" *ngFor=\"let filter of config.filters\">\n <select (change)=\"onFilterChange(filter.key, $event)\">\n <option value=\"\">{{ filter.label }}</option>\n <option *ngFor=\"let opt of filter.options\" [value]=\"opt.value\">{{ opt.label }}</option>\n </select>\n </div>\n </div>\n\n <!-- Top Bar Buttons -->\n <div class=\"st-actions\" *ngIf=\"config.topBarButtons\">\n <lib-button *ngFor=\"let btn of config.topBarButtons\" \n [variant]=\"btn.btnVariant || 'primary'\"\n [icon]=\"btn.icon || ''\"\n (click)=\"onTopAction(btn)\">\n {{ btn.label }}\n </lib-button>\n </div>\n </div>\n\n <!-- Table Container -->\n <div class=\"st-table-container\">\n <div class=\"st-check-loader\" *ngIf=\"loading\">\n <div class=\"spinner\"></div>\n </div>\n <table class=\"st-table\" [class.loading-data]=\"loading\">\n <thead>\n <tr>\n <th *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\n <input type=\"checkbox\" (change)=\"onSelectAll($event)\">\n </th>\n <th *ngFor=\"let col of config.columns\" \n #stickyHeader\n [class.sortable]=\"col.sortable\"\n [class.sticky-col]=\"col.sticky\"\n [ngStyle]=\"stickyColumnStyles[col.key]\"\n (click)=\"onSort(col)\">\n {{ col.label }}\n <span *ngIf=\"col.sortable\" class=\"sort-icon\">\n <i class=\"fa\" [ngClass]=\"getSortIcon(col.key)\"></i>\n </span>\n </th>\n <th *ngIf=\"config.actions && config.actions.length > 0\">{{ config.labels?.actionColumnHeader || 'Actions' }}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let row of data\" \n [class.clickable-row]=\"config.clickableRows\" \n (click)=\"onRowClick(row)\">\n <td *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\n <input type=\"checkbox\" [(ngModel)]=\"row.selected\" (change)=\"onRowSelect(row)\">\n </td>\n <td *ngFor=\"let col of config.columns\" [class.sticky-col]=\"col.sticky\" [ngStyle]=\"stickyColumnStyles[col.key]\">\n <!-- Text/Number/Date -->\n <span *ngIf=\"col.type !== 'custom' && col.type !== 'html' && col.type !== 'badge'\">\n {{ getCellValue(row, col) }}\n </span>\n <!-- HTML -->\n <div *ngIf=\"col.type === 'html'\" [innerHTML]=\"getCellValue(row, col)\"></div>\n <!-- Badge -->\n <span *ngIf=\"col.type === 'badge'\" class=\"st-badge\" [ngClass]=\"getBadgeClass(row, col)\">\n {{ getCellValue(row, col) }}\n </span>\n </td>\n \n <!-- Row Actions -->\n <td *ngIf=\"config.actions && config.actions.length > 0\" class=\"st-row-actions\">\n <div class=\"action-buttons\">\n <ng-container *ngFor=\"let action of config.actions\">\n <lib-button \n [variant]=\"action.btnVariant || 'secondary'\"\n [icon]=\"action.icon || ''\"\n (click)=\"onAction(action, row)\">\n {{ action.label }}\n </lib-button>\n </ng-container>\n </div>\n <!-- Alternatively use specific icons if needed, but button component is requested -->\n </td>\n </tr>\n <tr *ngIf=\"data.length === 0 && !loading\">\n <td [attr.colspan]=\"columnCount + (config.selectable ? 1 : 0) + (config.actions ? 1 : 0)\" class=\"no-data\">\n {{ config.labels?.noDataMessage || 'No data available' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- Pagination -->\n <div class=\"st-pagination\" *ngIf=\"config.pagination && config.pagination.enabled\">\n <lib-pagination\n [totalItems]=\"totalItems\"\n [itemsPerPage]=\"config.pagination.pageSize\"\n [currentPage]=\"currentPage\"\n [pageSizeOptions]=\"config.pagination.pageSizeOptions\"\n (pageChange)=\"onPageChange($event)\"\n (itemsPerPageChange)=\"onPageSizeChange($event)\">\n </lib-pagination>\n </div>\n</div>\n", styles: [".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 4px rgba(0, 0, 0, .05));display:flex;flex-direction:column;gap:0;padding:0;border:var(--st-table-border, 1px solid #e0e0e0);overflow:hidden}.st-toolbar{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;padding:var(--st-toolbar-padding, 1rem);background:var(--st-toolbar-bg, #fff);border-bottom:var(--st-toolbar-border-bottom, 1px solid #eee);gap:var(--st-toolbar-gap, 1rem)}.st-toolbar .st-search{position:relative;width:var(--st-search-width, auto)}.st-toolbar .st-search input{padding:var(--st-search-padding, .5rem .5rem .5rem 2rem);border:var(--st-search-border, 1px solid #ccc);border-radius:var(--st-search-radius, 4px);background:var(--st-search-bg, #fff);font-size:var(--st-font-size, 14px);width:100%;color:var(--st-text-color, #333)}.st-toolbar .st-search i{position:absolute;left:.75rem;top:50%;transform:translateY(-50%);color:var(--st-search-icon-color, #999)}.st-toolbar .st-filters{display:flex;gap:1rem}.st-toolbar .st-filters select{padding:var(--st-filter-padding, .5rem);border:var(--st-filter-border, 1px solid #ccc);border-radius:var(--st-filter-radius, 4px);font-size:var(--st-filter-font-size, 14px);background:var(--st-filter-bg, #fff);color:var(--st-filter-color, #333)}.st-toolbar .st-actions{display:flex;gap:.5rem}.st-table-container{overflow-x:auto;overflow-y:auto;padding:var(--st-table-padding, 1rem)}.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, #f9f9f9);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, #f9f9f9)}.st-table-container table thead th{padding:.75rem 1rem;text-align:left;color:var(--st-header-color, #333);font-weight:var(--st-header-weight, 500);font-size:var(--st-header-size, 14px);text-transform:var(--st-header-transform, none);border-bottom:var(--st-header-border, 1px solid #eee);white-space:nowrap}.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 .sort-icon{margin-left:.5rem}.st-table-container table thead th .sort-icon .sort-icon{margin-left:.5rem;font-size:var(--st-sort-icon-size, .8em)}.st-table-container table thead th.st-checkbox-col{width:40px}.st-table-container table thead th.sticky-col{position:sticky;z-index:3;background:var(--st-header-bg, #f9f9f9);box-shadow:var(--st-sticky-shadow, 2px 0 5px -2px rgba(0, 0, 0, .1));border-right:var(--st-sticky-border-right, 1px solid rgba(0, 0, 0, .05))}.st-table-container table thead th.sticky-col:first-child{left:0}.st-table-container table tbody tr{background:var(--st-row-bg, #fff)}.st-table-container table tbody tr td{padding:var(--st-cell-padding, 1rem);color:var(--st-text-color, #333);vertical-align:middle;border-bottom:var(--st-row-border, 1px solid #eee)}.st-table-container table tbody tr td.sticky-col{position:sticky;z-index:2;background:var(--st-row-bg, #fff);box-shadow:var(--st-sticky-shadow, 2px 0 5px -2px rgba(0, 0, 0, .1));border-right:var(--st-sticky-border-right, 1px solid rgba(0, 0, 0, .05))}.st-table-container table tbody tr td.sticky-col:first-child{left:0}.st-table-container table tbody tr:hover td,.st-table-container table tbody tr:hover td.sticky-col{background:var(--st-row-hover-bg, #f9f9f9)}.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-row,.st-table-container table tbody tr.clickable-row td{cursor:pointer}.st-table-container table tbody tr.clickable-row:hover td,.st-table-container table tbody tr.clickable-row:hover td.sticky-col{background:var(--st-row-hover-bg, #f5f5f5)}input[type=checkbox]{accent-color:var(--st-checkbox-color, #6200EE);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}.no-data{text-align:center;padding:2rem;color:var(--st-no-data-color, #888)}.st-pagination{padding:var(--st-pagination-padding, 1rem);border-top:var(--st-pagination-border-top, none)}@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{position:relative;min-height:200px}.st-table-container .st-table.loading-data{opacity:.5;pointer-events:none}.st-table-container .st-check-loader{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;z-index:10}.st-table-container .st-check-loader .spinner{width:40px;height:40px;border:4px solid var(--st-spinner-border-color, rgba(0, 0, 0, .1));border-left-color:var(--st-loader-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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"] }] });
2489
3197
  }
2490
3198
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartTableComponent, decorators: [{
2491
3199
  type: Component,
2492
- args: [{ selector: 'lib-smart-table', standalone: false, template: "<div class=\"smart-table-wrapper\">\n <!-- Top Toolbar -->\n <div class=\"st-toolbar\" *ngIf=\"config.searchConfig?.enabled || (config.filters && config.filters.length > 0) || (config.topBarButtons && config.topBarButtons.length > 0)\">\n \n <!-- Search -->\n <div class=\"st-search\" *ngIf=\"config.searchConfig?.enabled\">\n <i class=\"fa fa-search\"></i>\n <input type=\"text\" [placeholder]=\"config.labels?.searchPlaceholder || 'Search'\" (input)=\"onSearch($event)\">\n </div>\n\n <!-- Filters -->\n <div class=\"st-filters\" *ngIf=\"config.filters\">\n <div class=\"st-filter-item\" *ngFor=\"let filter of config.filters\">\n <select (change)=\"onFilterChange(filter.key, $event)\">\n <option value=\"\">{{ filter.label }}</option>\n <option *ngFor=\"let opt of filter.options\" [value]=\"opt.value\">{{ opt.label }}</option>\n </select>\n </div>\n </div>\n\n <!-- Top Bar Buttons -->\n <div class=\"st-actions\" *ngIf=\"config.topBarButtons\">\n <lib-button *ngFor=\"let btn of config.topBarButtons\" \n [variant]=\"btn.btnVariant || 'primary'\"\n [icon]=\"btn.icon || ''\"\n (click)=\"onTopAction(btn)\">\n {{ btn.label }}\n </lib-button>\n </div>\n </div>\n\n <!-- Table Container -->\n <div class=\"st-table-container\">\n <div class=\"st-check-loader\" *ngIf=\"loading\">\n <div class=\"spinner\"></div>\n </div>\n <table class=\"st-table\" [class.loading-data]=\"loading\">\n <thead>\n <tr>\n <th *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\n <input type=\"checkbox\" (change)=\"onSelectAll($event)\">\n </th>\n <th *ngFor=\"let col of config.columns\" \n #stickyHeader\n [class.sortable]=\"col.sortable\"\n [class.sticky-col]=\"col.sticky\"\n [ngStyle]=\"stickyColumnStyles[col.key]\"\n (click)=\"onSort(col)\">\n {{ col.label }}\n <span *ngIf=\"col.sortable\" class=\"sort-icon\">\n <i class=\"fa\" [ngClass]=\"getSortIcon(col.key)\"></i>\n </span>\n </th>\n <th *ngIf=\"config.actions && config.actions.length > 0\">{{ config.labels?.actionColumnHeader || 'Actions' }}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let row of data\">\n <td *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\n <input type=\"checkbox\" [(ngModel)]=\"row.selected\" (change)=\"onRowSelect(row)\">\n </td>\n <td *ngFor=\"let col of config.columns\" [class.sticky-col]=\"col.sticky\" [ngStyle]=\"stickyColumnStyles[col.key]\">\n <!-- Text/Number/Date -->\n <span *ngIf=\"col.type !== 'custom' && col.type !== 'html' && col.type !== 'badge'\">\n {{ getCellValue(row, col) }}\n </span>\n <!-- HTML -->\n <div *ngIf=\"col.type === 'html'\" [innerHTML]=\"getCellValue(row, col)\"></div>\n <!-- Badge -->\n <span *ngIf=\"col.type === 'badge'\" class=\"st-badge\" [ngClass]=\"getBadgeClass(row, col)\">\n {{ getCellValue(row, col) }}\n </span>\n </td>\n \n <!-- Row Actions -->\n <td *ngIf=\"config.actions && config.actions.length > 0\" class=\"st-row-actions\">\n <div class=\"action-buttons\">\n <ng-container *ngFor=\"let action of config.actions\">\n <lib-button \n [variant]=\"action.btnVariant || 'secondary'\"\n [icon]=\"action.icon || ''\"\n (click)=\"onAction(action, row)\">\n {{ action.label }}\n </lib-button>\n </ng-container>\n </div>\n <!-- Alternatively use specific icons if needed, but button component is requested -->\n </td>\n </tr>\n <tr *ngIf=\"data.length === 0 && !loading\">\n <td [attr.colspan]=\"columnCount + (config.selectable ? 1 : 0) + (config.actions ? 1 : 0)\" class=\"no-data\">\n {{ config.labels?.noDataMessage || 'No data available' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- Pagination -->\n <div class=\"st-pagination\" *ngIf=\"config.pagination && config.pagination.enabled\">\n <lib-pagination\n [totalItems]=\"totalItems\"\n [itemsPerPage]=\"config.pagination.pageSize\"\n [currentPage]=\"currentPage\"\n [pageSizeOptions]=\"config.pagination.pageSizeOptions\"\n (pageChange)=\"onPageChange($event)\"\n (itemsPerPageChange)=\"onPageSizeChange($event)\">\n </lib-pagination>\n </div>\n</div>\n", styles: [".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 4px rgba(0, 0, 0, .05));display:flex;flex-direction:column;gap:0;padding:0;border:var(--st-table-border, 1px solid #e0e0e0);overflow:hidden}.st-toolbar{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;padding:var(--st-toolbar-padding, 1rem);background:var(--st-toolbar-bg, #fff);border-bottom:var(--st-toolbar-border-bottom, 1px solid #eee);gap:var(--st-toolbar-gap, 1rem)}.st-toolbar .st-search{position:relative;width:var(--st-search-width, auto)}.st-toolbar .st-search input{padding:var(--st-search-padding, .5rem .5rem .5rem 2rem);border:var(--st-search-border, 1px solid #ccc);border-radius:var(--st-search-radius, 4px);background:var(--st-search-bg, #fff);font-size:var(--st-font-size, 14px);width:100%;color:var(--st-text-color, #333)}.st-toolbar .st-search i{position:absolute;left:.75rem;top:50%;transform:translateY(-50%);color:var(--st-search-icon-color, #999)}.st-toolbar .st-filters{display:flex;gap:1rem}.st-toolbar .st-filters select{padding:var(--st-filter-padding, .5rem);border:var(--st-filter-border, 1px solid #ccc);border-radius:var(--st-filter-radius, 4px);font-size:var(--st-filter-font-size, 14px);background:var(--st-filter-bg, #fff);color:var(--st-filter-color, #333)}.st-toolbar .st-actions{display:flex;gap:.5rem}.st-table-container{overflow-x:auto;overflow-y:auto;padding:var(--st-table-padding, 1rem)}.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, #f9f9f9);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, #f9f9f9)}.st-table-container table thead th{padding:.75rem 1rem;text-align:left;color:var(--st-header-color, #333);font-weight:var(--st-header-weight, 500);font-size:var(--st-header-size, 14px);text-transform:var(--st-header-transform, none);border-bottom:var(--st-header-border, 1px solid #eee);white-space:nowrap}.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 .sort-icon{margin-left:.5rem}.st-table-container table thead th .sort-icon .sort-icon{margin-left:.5rem;font-size:var(--st-sort-icon-size, .8em)}.st-table-container table thead th.st-checkbox-col{width:40px}.st-table-container table thead th.sticky-col{position:sticky;z-index:3;background:var(--st-header-bg, #f9f9f9);box-shadow:var(--st-sticky-shadow, 2px 0 5px -2px rgba(0, 0, 0, .1));border-right:var(--st-sticky-border-right, 1px solid rgba(0, 0, 0, .05))}.st-table-container table thead th.sticky-col:first-child{left:0}.st-table-container table tbody tr{background:var(--st-row-bg, #fff)}.st-table-container table tbody tr td{padding:var(--st-cell-padding, 1rem);color:var(--st-text-color, #333);vertical-align:middle;border-bottom:var(--st-row-border, 1px solid #eee)}.st-table-container table tbody tr td.sticky-col{position:sticky;z-index:2;background:var(--st-row-bg, #fff);box-shadow:var(--st-sticky-shadow, 2px 0 5px -2px rgba(0, 0, 0, .1));border-right:var(--st-sticky-border-right, 1px solid rgba(0, 0, 0, .05))}.st-table-container table tbody tr td.sticky-col:first-child{left:0}.st-table-container table tbody tr:hover td,.st-table-container table tbody tr:hover td.sticky-col{background:var(--st-row-hover-bg, #f9f9f9)}.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)}input[type=checkbox]{accent-color:var(--st-checkbox-color, #6200EE);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}.no-data{text-align:center;padding:2rem;color:var(--st-no-data-color, #888)}.st-pagination{padding:var(--st-pagination-padding, 1rem);border-top:var(--st-pagination-border-top, none)}@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{position:relative;min-height:200px}.st-table-container .st-table.loading-data{opacity:.5;pointer-events:none}.st-table-container .st-check-loader{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;z-index:10}.st-table-container .st-check-loader .spinner{width:40px;height:40px;border:4px solid var(--st-spinner-border-color, rgba(0, 0, 0, .1));border-left-color:var(--st-loader-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
3200
+ args: [{ selector: 'lib-smart-table', standalone: false, template: "<div class=\"smart-table-wrapper\">\n <!-- Top Toolbar -->\n <div class=\"st-toolbar\" *ngIf=\"config.searchConfig?.enabled || (config.filters && config.filters.length > 0) || (config.topBarButtons && config.topBarButtons.length > 0)\">\n \n <!-- Search -->\n <div class=\"st-search\" *ngIf=\"config.searchConfig?.enabled\">\n <i class=\"fa fa-search\"></i>\n <input type=\"text\" [placeholder]=\"config.labels?.searchPlaceholder || 'Search'\" (input)=\"onSearch($event)\">\n </div>\n\n <!-- Filters -->\n <div class=\"st-filters\" *ngIf=\"config.filters\">\n <div class=\"st-filter-item\" *ngFor=\"let filter of config.filters\">\n <select (change)=\"onFilterChange(filter.key, $event)\">\n <option value=\"\">{{ filter.label }}</option>\n <option *ngFor=\"let opt of filter.options\" [value]=\"opt.value\">{{ opt.label }}</option>\n </select>\n </div>\n </div>\n\n <!-- Top Bar Buttons -->\n <div class=\"st-actions\" *ngIf=\"config.topBarButtons\">\n <lib-button *ngFor=\"let btn of config.topBarButtons\" \n [variant]=\"btn.btnVariant || 'primary'\"\n [icon]=\"btn.icon || ''\"\n (click)=\"onTopAction(btn)\">\n {{ btn.label }}\n </lib-button>\n </div>\n </div>\n\n <!-- Table Container -->\n <div class=\"st-table-container\">\n <div class=\"st-check-loader\" *ngIf=\"loading\">\n <div class=\"spinner\"></div>\n </div>\n <table class=\"st-table\" [class.loading-data]=\"loading\">\n <thead>\n <tr>\n <th *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\n <input type=\"checkbox\" (change)=\"onSelectAll($event)\">\n </th>\n <th *ngFor=\"let col of config.columns\" \n #stickyHeader\n [class.sortable]=\"col.sortable\"\n [class.sticky-col]=\"col.sticky\"\n [ngStyle]=\"stickyColumnStyles[col.key]\"\n (click)=\"onSort(col)\">\n {{ col.label }}\n <span *ngIf=\"col.sortable\" class=\"sort-icon\">\n <i class=\"fa\" [ngClass]=\"getSortIcon(col.key)\"></i>\n </span>\n </th>\n <th *ngIf=\"config.actions && config.actions.length > 0\">{{ config.labels?.actionColumnHeader || 'Actions' }}</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let row of data\" \n [class.clickable-row]=\"config.clickableRows\" \n (click)=\"onRowClick(row)\">\n <td *ngIf=\"config.selectable\" class=\"st-checkbox-col\">\n <input type=\"checkbox\" [(ngModel)]=\"row.selected\" (change)=\"onRowSelect(row)\">\n </td>\n <td *ngFor=\"let col of config.columns\" [class.sticky-col]=\"col.sticky\" [ngStyle]=\"stickyColumnStyles[col.key]\">\n <!-- Text/Number/Date -->\n <span *ngIf=\"col.type !== 'custom' && col.type !== 'html' && col.type !== 'badge'\">\n {{ getCellValue(row, col) }}\n </span>\n <!-- HTML -->\n <div *ngIf=\"col.type === 'html'\" [innerHTML]=\"getCellValue(row, col)\"></div>\n <!-- Badge -->\n <span *ngIf=\"col.type === 'badge'\" class=\"st-badge\" [ngClass]=\"getBadgeClass(row, col)\">\n {{ getCellValue(row, col) }}\n </span>\n </td>\n \n <!-- Row Actions -->\n <td *ngIf=\"config.actions && config.actions.length > 0\" class=\"st-row-actions\">\n <div class=\"action-buttons\">\n <ng-container *ngFor=\"let action of config.actions\">\n <lib-button \n [variant]=\"action.btnVariant || 'secondary'\"\n [icon]=\"action.icon || ''\"\n (click)=\"onAction(action, row)\">\n {{ action.label }}\n </lib-button>\n </ng-container>\n </div>\n <!-- Alternatively use specific icons if needed, but button component is requested -->\n </td>\n </tr>\n <tr *ngIf=\"data.length === 0 && !loading\">\n <td [attr.colspan]=\"columnCount + (config.selectable ? 1 : 0) + (config.actions ? 1 : 0)\" class=\"no-data\">\n {{ config.labels?.noDataMessage || 'No data available' }}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n\n <!-- Pagination -->\n <div class=\"st-pagination\" *ngIf=\"config.pagination && config.pagination.enabled\">\n <lib-pagination\n [totalItems]=\"totalItems\"\n [itemsPerPage]=\"config.pagination.pageSize\"\n [currentPage]=\"currentPage\"\n [pageSizeOptions]=\"config.pagination.pageSizeOptions\"\n (pageChange)=\"onPageChange($event)\"\n (itemsPerPageChange)=\"onPageSizeChange($event)\">\n </lib-pagination>\n </div>\n</div>\n", styles: [".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 4px rgba(0, 0, 0, .05));display:flex;flex-direction:column;gap:0;padding:0;border:var(--st-table-border, 1px solid #e0e0e0);overflow:hidden}.st-toolbar{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;padding:var(--st-toolbar-padding, 1rem);background:var(--st-toolbar-bg, #fff);border-bottom:var(--st-toolbar-border-bottom, 1px solid #eee);gap:var(--st-toolbar-gap, 1rem)}.st-toolbar .st-search{position:relative;width:var(--st-search-width, auto)}.st-toolbar .st-search input{padding:var(--st-search-padding, .5rem .5rem .5rem 2rem);border:var(--st-search-border, 1px solid #ccc);border-radius:var(--st-search-radius, 4px);background:var(--st-search-bg, #fff);font-size:var(--st-font-size, 14px);width:100%;color:var(--st-text-color, #333)}.st-toolbar .st-search i{position:absolute;left:.75rem;top:50%;transform:translateY(-50%);color:var(--st-search-icon-color, #999)}.st-toolbar .st-filters{display:flex;gap:1rem}.st-toolbar .st-filters select{padding:var(--st-filter-padding, .5rem);border:var(--st-filter-border, 1px solid #ccc);border-radius:var(--st-filter-radius, 4px);font-size:var(--st-filter-font-size, 14px);background:var(--st-filter-bg, #fff);color:var(--st-filter-color, #333)}.st-toolbar .st-actions{display:flex;gap:.5rem}.st-table-container{overflow-x:auto;overflow-y:auto;padding:var(--st-table-padding, 1rem)}.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, #f9f9f9);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, #f9f9f9)}.st-table-container table thead th{padding:.75rem 1rem;text-align:left;color:var(--st-header-color, #333);font-weight:var(--st-header-weight, 500);font-size:var(--st-header-size, 14px);text-transform:var(--st-header-transform, none);border-bottom:var(--st-header-border, 1px solid #eee);white-space:nowrap}.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 .sort-icon{margin-left:.5rem}.st-table-container table thead th .sort-icon .sort-icon{margin-left:.5rem;font-size:var(--st-sort-icon-size, .8em)}.st-table-container table thead th.st-checkbox-col{width:40px}.st-table-container table thead th.sticky-col{position:sticky;z-index:3;background:var(--st-header-bg, #f9f9f9);box-shadow:var(--st-sticky-shadow, 2px 0 5px -2px rgba(0, 0, 0, .1));border-right:var(--st-sticky-border-right, 1px solid rgba(0, 0, 0, .05))}.st-table-container table thead th.sticky-col:first-child{left:0}.st-table-container table tbody tr{background:var(--st-row-bg, #fff)}.st-table-container table tbody tr td{padding:var(--st-cell-padding, 1rem);color:var(--st-text-color, #333);vertical-align:middle;border-bottom:var(--st-row-border, 1px solid #eee)}.st-table-container table tbody tr td.sticky-col{position:sticky;z-index:2;background:var(--st-row-bg, #fff);box-shadow:var(--st-sticky-shadow, 2px 0 5px -2px rgba(0, 0, 0, .1));border-right:var(--st-sticky-border-right, 1px solid rgba(0, 0, 0, .05))}.st-table-container table tbody tr td.sticky-col:first-child{left:0}.st-table-container table tbody tr:hover td,.st-table-container table tbody tr:hover td.sticky-col{background:var(--st-row-hover-bg, #f9f9f9)}.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-row,.st-table-container table tbody tr.clickable-row td{cursor:pointer}.st-table-container table tbody tr.clickable-row:hover td,.st-table-container table tbody tr.clickable-row:hover td.sticky-col{background:var(--st-row-hover-bg, #f5f5f5)}input[type=checkbox]{accent-color:var(--st-checkbox-color, #6200EE);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}.no-data{text-align:center;padding:2rem;color:var(--st-no-data-color, #888)}.st-pagination{padding:var(--st-pagination-padding, 1rem);border-top:var(--st-pagination-border-top, none)}@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{position:relative;min-height:200px}.st-table-container .st-table.loading-data{opacity:.5;pointer-events:none}.st-table-container .st-check-loader{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;justify-content:center;align-items:center;z-index:10}.st-table-container .st-check-loader .spinner{width:40px;height:40px;border:4px solid var(--st-spinner-border-color, rgba(0, 0, 0, .1));border-left-color:var(--st-loader-color);border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
2493
3201
  }], ctorParameters: () => [{ type: i3.HttpClient }, { type: i1$1.Router }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }], propDecorators: { config: [{
2494
3202
  type: Input
2495
3203
  }], action: [{
@@ -2500,6 +3208,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
2500
3208
  type: Output
2501
3209
  }], rowSelect: [{
2502
3210
  type: Output
3211
+ }], rowClick: [{
3212
+ type: Output
2503
3213
  }], stickyHeaders: [{
2504
3214
  type: ViewChildren,
2505
3215
  args: ['stickyHeader']
@@ -2534,6 +3244,389 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
2534
3244
  }]
2535
3245
  }] });
2536
3246
 
3247
+ const SAMPLE_FORMS = {
3248
+ // Simple Contact Form
3249
+ contactForm: `{
3250
+ "entityType": "CONTACT",
3251
+ "label": "Contact Us",
3252
+ "description": "Send us a message",
3253
+ "formType": "SECTION",
3254
+ "sectionConfig": {
3255
+ "children": [
3256
+ {
3257
+ "name": "name",
3258
+ "label": "Full Name",
3259
+ "type": "TEXT_INPUT",
3260
+ "subType": "SHORT",
3261
+ "required": true,
3262
+ "hint": "Enter your full name"
3263
+ },
3264
+ {
3265
+ "name": "email",
3266
+ "label": "Email Address",
3267
+ "type": "TEXT_INPUT",
3268
+ "subType": "EMAIL",
3269
+ "required": true,
3270
+ "hint": "your.email@example.com"
3271
+ },
3272
+ {
3273
+ "name": "message",
3274
+ "label": "Message",
3275
+ "type": "TEXT_INPUT",
3276
+ "subType": "LONG",
3277
+ "required": true,
3278
+ "textConfig": {
3279
+ "length": {
3280
+ "min": 10,
3281
+ "max": 500
3282
+ }
3283
+ }
3284
+ }
3285
+ ]
3286
+ }
3287
+ }`,
3288
+ // User Registration with Stepper
3289
+ registrationForm: `{
3290
+ "entityType": "USER",
3291
+ "label": "User Registration",
3292
+ "description": "Create your account",
3293
+ "formType": "STEPPER",
3294
+ "stepperConfig": {
3295
+ "children": [
3296
+ {
3297
+ "type": "GROUP",
3298
+ "subType": "SECTION",
3299
+ "sectionConfig": {
3300
+ "label": "Personal Information",
3301
+ "children": [
3302
+ {
3303
+ "type": "ROW",
3304
+ "subType": "HORIZONTAL",
3305
+ "children": [
3306
+ {
3307
+ "name": "firstName",
3308
+ "label": "First Name",
3309
+ "type": "TEXT_INPUT",
3310
+ "subType": "SHORT",
3311
+ "required": true
3312
+ },
3313
+ {
3314
+ "name": "lastName",
3315
+ "label": "Last Name",
3316
+ "type": "TEXT_INPUT",
3317
+ "subType": "SHORT",
3318
+ "required": true
3319
+ }
3320
+ ]
3321
+ },
3322
+ {
3323
+ "name": "fullName",
3324
+ "label": "Full Name",
3325
+ "type": "GENERATED",
3326
+ "subType": "FORMULA",
3327
+ "generatedConfig": {
3328
+ "formula": "function fullName(first, last) { return (first || '') + ' ' + (last || ''); }",
3329
+ "variables": ["firstName", "lastName"]
3330
+ }
3331
+ },
3332
+ {
3333
+ "name": "dateOfBirth",
3334
+ "label": "Date of Birth",
3335
+ "type": "DATE",
3336
+ "subType": "SINGLE",
3337
+ "required": true,
3338
+ "dateConfig": {
3339
+ "allowFuture": false
3340
+ }
3341
+ }
3342
+ ]
3343
+ }
3344
+ },
3345
+ {
3346
+ "type": "GROUP",
3347
+ "subType": "SECTION",
3348
+ "sectionConfig": {
3349
+ "label": "Contact Information",
3350
+ "children": [
3351
+ {
3352
+ "name": "email",
3353
+ "label": "Email",
3354
+ "type": "TEXT_INPUT",
3355
+ "subType": "EMAIL",
3356
+ "required": true
3357
+ },
3358
+ {
3359
+ "name": "phone",
3360
+ "label": "Phone Number",
3361
+ "type": "TEXT_INPUT",
3362
+ "subType": "PHONE",
3363
+ "required": true
3364
+ }
3365
+ ]
3366
+ }
3367
+ },
3368
+ {
3369
+ "type": "GROUP",
3370
+ "subType": "SECTION",
3371
+ "sectionConfig": {
3372
+ "label": "Preferences",
3373
+ "children": [
3374
+ {
3375
+ "name": "notifications",
3376
+ "label": "Enable Email Notifications",
3377
+ "type": "SWITCH",
3378
+ "subType": "BOOL",
3379
+ "defaultValue": true
3380
+ },
3381
+ {
3382
+ "name": "interests",
3383
+ "label": "Interests",
3384
+ "type": "CHIP",
3385
+ "subType": "MULTIPLE",
3386
+ "optionConfig": {
3387
+ "optionList": [
3388
+ { "label": "Technology", "code": "TECH" },
3389
+ { "label": "Sports", "code": "SPORTS" },
3390
+ { "label": "Music", "code": "MUSIC" },
3391
+ { "label": "Travel", "code": "TRAVEL" }
3392
+ ]
3393
+ }
3394
+ }
3395
+ ]
3396
+ }
3397
+ }
3398
+ ],
3399
+ "showStep": true,
3400
+ "isHorizontal": true
3401
+ }
3402
+ }`,
3403
+ // Survey Form with Conditional Fields
3404
+ surveyForm: `{
3405
+ "entityType": "SURVEY",
3406
+ "label": "Customer Satisfaction Survey",
3407
+ "formType": "SECTION",
3408
+ "sectionConfig": {
3409
+ "children": [
3410
+ {
3411
+ "name": "overallRating",
3412
+ "label": "Overall Experience",
3413
+ "type": "RATING",
3414
+ "subType": "STAR",
3415
+ "required": true,
3416
+ "ratingConfig": {
3417
+ "maxRating": 5,
3418
+ "allowHalf": false
3419
+ }
3420
+ },
3421
+ {
3422
+ "name": "wouldRecommend",
3423
+ "label": "Would you recommend us?",
3424
+ "type": "RADIO",
3425
+ "subType": "SINGLE",
3426
+ "required": true,
3427
+ "optionConfig": {
3428
+ "optionList": [
3429
+ { "label": "Yes", "code": "YES" },
3430
+ { "label": "No", "code": "NO" },
3431
+ { "label": "Maybe", "code": "MAYBE" }
3432
+ ]
3433
+ }
3434
+ },
3435
+ {
3436
+ "name": "reasonForNo",
3437
+ "label": "Why not?",
3438
+ "type": "TEXT_INPUT",
3439
+ "subType": "LONG",
3440
+ "visibilityExpression": "wouldRecommend === 'NO'",
3441
+ "required": true
3442
+ },
3443
+ {
3444
+ "name": "improvements",
3445
+ "label": "What can we improve?",
3446
+ "type": "CHECKBOX",
3447
+ "subType": "LIST",
3448
+ "optionConfig": {
3449
+ "optionList": [
3450
+ { "label": "Customer Service", "code": "SERVICE" },
3451
+ { "label": "Product Quality", "code": "QUALITY" },
3452
+ { "label": "Pricing", "code": "PRICE" },
3453
+ { "label": "Delivery Speed", "code": "DELIVERY" }
3454
+ ]
3455
+ }
3456
+ },
3457
+ {
3458
+ "name": "additionalComments",
3459
+ "label": "Additional Comments",
3460
+ "type": "TEXT_INPUT",
3461
+ "subType": "LONG",
3462
+ "textConfig": {
3463
+ "length": {
3464
+ "max": 1000
3465
+ }
3466
+ }
3467
+ }
3468
+ ]
3469
+ }
3470
+ }`,
3471
+ // Job Application Form
3472
+ jobApplicationForm: `{
3473
+ "entityType": "JOB_APPLICATION",
3474
+ "label": "Job Application",
3475
+ "description": "Apply for a position at our company",
3476
+ "formType": "STEPPER",
3477
+ "stepperConfig": {
3478
+ "children": [
3479
+ {
3480
+ "type": "GROUP",
3481
+ "subType": "SECTION",
3482
+ "sectionConfig": {
3483
+ "label": "Personal Details",
3484
+ "children": [
3485
+ {
3486
+ "type": "ROW",
3487
+ "subType": "HORIZONTAL",
3488
+ "children": [
3489
+ {
3490
+ "name": "firstName",
3491
+ "label": "First Name",
3492
+ "type": "TEXT_INPUT",
3493
+ "subType": "SHORT",
3494
+ "required": true
3495
+ },
3496
+ {
3497
+ "name": "lastName",
3498
+ "label": "Last Name",
3499
+ "type": "TEXT_INPUT",
3500
+ "subType": "SHORT",
3501
+ "required": true
3502
+ }
3503
+ ]
3504
+ },
3505
+ {
3506
+ "name": "email",
3507
+ "label": "Email",
3508
+ "type": "TEXT_INPUT",
3509
+ "subType": "EMAIL",
3510
+ "required": true
3511
+ },
3512
+ {
3513
+ "name": "phone",
3514
+ "label": "Phone",
3515
+ "type": "TEXT_INPUT",
3516
+ "subType": "PHONE",
3517
+ "required": true
3518
+ }
3519
+ ]
3520
+ }
3521
+ },
3522
+ {
3523
+ "type": "GROUP",
3524
+ "subType": "SECTION",
3525
+ "sectionConfig": {
3526
+ "label": "Experience",
3527
+ "allowMulti": true,
3528
+ "name": "experienceList",
3529
+ "children": [
3530
+ {
3531
+ "name": "company",
3532
+ "label": "Company Name",
3533
+ "type": "TEXT_INPUT",
3534
+ "subType": "SHORT",
3535
+ "required": true
3536
+ },
3537
+ {
3538
+ "name": "position",
3539
+ "label": "Position",
3540
+ "type": "TEXT_INPUT",
3541
+ "subType": "SHORT",
3542
+ "required": true
3543
+ },
3544
+ {
3545
+ "type": "ROW",
3546
+ "subType": "HORIZONTAL",
3547
+ "children": [
3548
+ {
3549
+ "name": "startDate",
3550
+ "label": "Start Date",
3551
+ "type": "DATE",
3552
+ "subType": "SINGLE",
3553
+ "required": true
3554
+ },
3555
+ {
3556
+ "name": "endDate",
3557
+ "label": "End Date",
3558
+ "type": "DATE",
3559
+ "subType": "SINGLE"
3560
+ }
3561
+ ]
3562
+ },
3563
+ {
3564
+ "name": "responsibilities",
3565
+ "label": "Key Responsibilities",
3566
+ "type": "TEXT_INPUT",
3567
+ "subType": "LONG"
3568
+ }
3569
+ ]
3570
+ }
3571
+ },
3572
+ {
3573
+ "type": "GROUP",
3574
+ "subType": "SECTION",
3575
+ "sectionConfig": {
3576
+ "label": "Skills & Qualifications",
3577
+ "children": [
3578
+ {
3579
+ "name": "skills",
3580
+ "label": "Technical Skills",
3581
+ "type": "CHIP",
3582
+ "subType": "MULTIPLE",
3583
+ "optionConfig": {
3584
+ "optionList": [
3585
+ { "label": "JavaScript", "code": "JS" },
3586
+ { "label": "TypeScript", "code": "TS" },
3587
+ { "label": "Angular", "code": "ANGULAR" },
3588
+ { "label": "React", "code": "REACT" },
3589
+ { "label": "Node.js", "code": "NODE" },
3590
+ { "label": "Python", "code": "PYTHON" }
3591
+ ]
3592
+ }
3593
+ },
3594
+ {
3595
+ "name": "yearsOfExperience",
3596
+ "label": "Years of Experience",
3597
+ "type": "NUMBER_INPUT",
3598
+ "subType": "INTEGER",
3599
+ "required": true,
3600
+ "numberConfig": {
3601
+ "min": 0,
3602
+ "max": 50
3603
+ }
3604
+ },
3605
+ {
3606
+ "name": "availableToStart",
3607
+ "label": "Available to Start",
3608
+ "type": "DATE",
3609
+ "subType": "SINGLE",
3610
+ "required": true,
3611
+ "dateConfig": {
3612
+ "allowFuture": true
3613
+ }
3614
+ }
3615
+ ]
3616
+ }
3617
+ }
3618
+ ],
3619
+ "showStep": true,
3620
+ "isHorizontal": true
3621
+ }
3622
+ }`
3623
+ };
3624
+
3625
+ var smartForm_examples = /*#__PURE__*/Object.freeze({
3626
+ __proto__: null,
3627
+ SAMPLE_FORMS: SAMPLE_FORMS
3628
+ });
3629
+
2537
3630
  /*
2538
3631
  * Public API Surface of shared-ui
2539
3632
  */
@@ -2542,5 +3635,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
2542
3635
  * Generated bundle index. Do not edit.
2543
3636
  */
2544
3637
 
2545
- export { AlertComponent, AlertModule, ButtonComponent, ButtonModule, CardType1Component, CardType2Component, CardsModule, ConfigurableFormComponent, configurableForm_examples as ConfigurableFormExamples, ConfigurableFormModule, ConfirmationModalComponent, ConfirmationModalModule, DEFAULT_ITEMS_PER_PAGE, DEFAULT_PAGE_SIZE_OPTIONS, FilterSidebarComponent, FilterSidebarModule, MaterialModule, NAV_ORIENTATION_DEFAULT, NAV_VARIANT_DEFAULT, NavComponent, NavModule, PAGINATION_THEME_DARK, PAGINATION_THEME_DEFAULT, PaginationComponent, PaginationModule, SharedUiModule, SmartTableComponent, SmartTableModule, SummaryCardComponent, SummaryCardModule, clearLocalStorage, clearSessionStorage, getLocalStorageItem, getSessionStorageItem, removeLocalStorageItem, removeSessionStorageItem, setLocalStorageItem, setSessionStorageItem };
3638
+ export { AlertComponent, AlertModule, ButtonComponent, ButtonModule, ConfigurableFormComponent, configurableForm_examples as ConfigurableFormExamples, ConfigurableFormModule, ConfirmationModalComponent, ConfirmationModalModule, DEFAULT_ITEMS_PER_PAGE, DEFAULT_PAGE_SIZE_OPTIONS, ExpressionService, FilterSidebarComponent, FilterSidebarModule, MaterialModule, NAV_ORIENTATION_DEFAULT, NAV_VARIANT_DEFAULT, NavComponent, NavModule, PAGINATION_THEME_DARK, PAGINATION_THEME_DEFAULT, PaginationComponent, PaginationModule, SharedUiModule, SmartFormComponent, SmartFormController, smartForm_examples as SmartFormExamples, SmartFormModule, SmartTableComponent, SmartTableModule, SummaryCardComponent, SummaryCardModule, ValidationUtils, clearLocalStorage, clearSessionStorage, getLocalStorageItem, getSessionStorageItem, removeLocalStorageItem, removeSessionStorageItem, setLocalStorageItem, setSessionStorageItem };
2546
3639
  //# sourceMappingURL=commons-shared-web-ui.mjs.map