commons-shared-web-ui 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/commons-shared-web-ui.mjs +510 -15
- package/fesm2022/commons-shared-web-ui.mjs.map +1 -1
- package/index.d.ts +194 -7
- package/package.json +2 -2
- package/src/lib/modules/smart-table/smart-table.theme.scss +214 -0
- package/src/lib/modules/summary-card/summary-card.theme.scss +0 -8
- package/src/lib/styles/global.scss +0 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { NgModule, EventEmitter, Output, Input, Component, HostListener, forwardRef,
|
|
2
|
+
import { NgModule, EventEmitter, Output, Input, Component, HostListener, forwardRef, inject, LOCALE_ID, ViewChildren } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/common';
|
|
4
|
-
import { CommonModule } from '@angular/common';
|
|
4
|
+
import { CommonModule, formatDate } from '@angular/common';
|
|
5
5
|
import { MatCardModule } from '@angular/material/card';
|
|
6
6
|
import * as i2$1 from '@angular/material/snack-bar';
|
|
7
7
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
|
@@ -44,7 +44,9 @@ import * as i1$2 from '@angular/forms';
|
|
|
44
44
|
import { FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
45
45
|
import * as i1$1 from '@angular/router';
|
|
46
46
|
import * as i3 from '@angular/common/http';
|
|
47
|
-
import {
|
|
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
50
|
|
|
49
51
|
class MaterialModule {
|
|
50
52
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: MaterialModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
@@ -911,11 +913,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
911
913
|
|
|
912
914
|
class SummaryCardComponent {
|
|
913
915
|
config;
|
|
914
|
-
theme
|
|
916
|
+
theme;
|
|
915
917
|
cardClick = new EventEmitter();
|
|
916
|
-
get hostClasses() {
|
|
917
|
-
return `${this.theme} ${this.config?.isDisabled ? 'disabled' : ''} ${this.config?.isClickable !== false && !this.config?.isDisabled ? 'clickable' : ''}`;
|
|
918
|
-
}
|
|
919
918
|
constructor() { }
|
|
920
919
|
onCardClick() {
|
|
921
920
|
if (this.config?.isDisabled || this.config?.isClickable === false) {
|
|
@@ -974,7 +973,7 @@ class SummaryCardComponent {
|
|
|
974
973
|
return styles;
|
|
975
974
|
}
|
|
976
975
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SummaryCardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
977
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SummaryCardComponent, isStandalone: false, selector: "lib-summary-card", inputs: { config: "config", theme: "theme" }, outputs: { cardClick: "cardClick" },
|
|
976
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SummaryCardComponent, isStandalone: false, selector: "lib-summary-card", inputs: { config: "config", theme: "theme" }, outputs: { cardClick: "cardClick" }, ngImport: i0, template: "<div class=\"cc-summary-card\" (click)=\"onCardClick()\">\r\n <!-- Icon Section (Left Side) -->\r\n <div class=\"icon-section\" *ngIf=\"config.icon || config.iconImage\" [ngClass]=\"config.iconClass\"\r\n [ngStyle]=\"iconStyles\">\r\n <mat-icon *ngIf=\"config.icon\">{{ config.icon }}</mat-icon>\r\n <img *ngIf=\"!config.icon && config.iconImage\" [src]=\"config.iconImage\" alt=\"icon\">\r\n </div>\r\n\r\n <!-- Right Section (Header + Value/Meta) -->\r\n <div class=\"right-section\">\r\n <!-- Header (Full Width on Right) -->\r\n <div class=\"header\" [ngClass]=\"config.headerClass\" [ngStyle]=\"headerStyles\">\r\n {{ config.header }}\r\n </div>\r\n\r\n <!-- Value and Meta Row -->\r\n <div class=\"value-meta-row\">\r\n <!-- Content Section -->\r\n <div class=\"content-section\">\r\n <!-- Value Row (with optional inline description) -->\r\n <div class=\"value-row\" [class.inline-layout]=\"isDescriptionInline\">\r\n <div class=\"value-container\">\r\n <div class=\"value\" [ngClass]=\"config.valueClass\" [ngStyle]=\"valueStyles\">\r\n {{ config.value }}\r\n <span *ngIf=\"config.valueSubtext\" class=\"value-subtext\">{{ config.valueSubtext }}</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Description (inline or bottom based on config) -->\r\n <div class=\"description\" *ngIf=\"config.description\" [ngClass]=\"config.descriptionClass\"\r\n [ngStyle]=\"descriptionStyles\">\r\n {{ config.description }}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Meta/Status Section (Aligned with Value) -->\r\n <div class=\"meta-section\" *ngIf=\"config.metaData && config.metaData.length > 0\">\r\n <div *ngFor=\"let meta of config.metaData\" class=\"meta-item\"\r\n [ngClass]=\"[meta.type === 'pill' ? 'meta-pill' : 'meta-text', meta.cssClass || '']\"\r\n [ngStyle]=\"getMetaStyles(meta)\">\r\n {{ meta.text }}\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>", styles: [":host{display:block;width:100%}.cc-summary-card{display:flex;flex-direction:row;align-items:stretch;box-sizing:border-box;font-family:var(--cc-sc-font-family, \"Inter\", sans-serif);background-color:var(--cc-sc-bg-color, #ffffff);border-radius:var(--cc-sc-border-radius, 8px);border:var(--cc-sc-border, 1px solid #e0e0e0);padding:var(--cc-sc-padding, 16px);box-shadow:var(--cc-sc-shadow, 0 2px 4px rgba(0, 0, 0, .05));transition:all var(--cc-sc-transition-duration, .2s) ease;height:100%;width:100%;overflow:hidden;gap:var(--cc-sc-icon-margin, 1rem)}.cc-summary-card.clickable{cursor:pointer}.cc-summary-card.clickable:hover{transform:var(--cc-sc-hover-transform);box-shadow:var(--cc-sc-hover-shadow)}.cc-summary-card.disabled{opacity:var(--cc-sc-disabled-opacity);cursor:not-allowed;pointer-events:none}.cc-summary-card .icon-section{width:var(--cc-sc-icon-box-size);height:var(--cc-sc-icon-box-size);min-width:var(--cc-sc-icon-box-size);display:flex;align-items:center;justify-content:center;border-radius:var(--cc-sc-icon-radius);background-color:var(--cc-sc-icon-bg);color:var(--cc-sc-icon-color);flex-shrink:0;align-self:center}.cc-summary-card .icon-section mat-icon{width:var(--cc-sc-icon-size);height:var(--cc-sc-icon-size);font-size:var(--cc-sc-icon-size);line-height:var(--cc-sc-icon-size)}.cc-summary-card .icon-section img{width:var(--cc-sc-icon-size);height:var(--cc-sc-icon-size);object-fit:contain}.cc-summary-card .right-section{display:flex;flex-direction:column;flex:1;min-width:0;gap:var(--cc-sc-content-gap, .5rem)}.cc-summary-card .right-section .header{font-size:var(--cc-sc-header-size);font-weight:var(--cc-sc-header-weight);text-transform:var(--cc-sc-header-transform);color:var(--cc-sc-header-color);letter-spacing:var(--cc-sc-header-letter-spacing);line-height:var(--cc-sc-header-line-height);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:100%}.cc-summary-card .right-section .value-meta-row{display:flex;flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1rem;flex:1}.cc-summary-card .right-section .value-meta-row .content-section{display:flex;flex-direction:column;justify-content:flex-start;flex:1;min-width:0}.cc-summary-card .right-section .value-meta-row .content-section .value-row{display:flex;flex-direction:column;gap:var(--cc-sc-value-desc-gap)}.cc-summary-card .right-section .value-meta-row .content-section .value-row.inline-layout{flex-direction:row;align-items:baseline;flex-wrap:wrap}.cc-summary-card .right-section .value-meta-row .content-section .value-row .value{font-size:var(--cc-sc-value-size);font-weight:var(--cc-sc-value-weight);color:var(--cc-sc-value-color);line-height:var(--cc-sc-value-line-height);white-space:nowrap}.cc-summary-card .right-section .value-meta-row .content-section .value-row .value .value-subtext{font-size:var(--cc-sc-desc-size);font-weight:var(--cc-sc-desc-weight);color:var(--cc-sc-desc-color);margin-left:4px}.cc-summary-card .right-section .value-meta-row .content-section .value-row .description{font-size:var(--cc-sc-desc-size);font-weight:var(--cc-sc-desc-weight);color:var(--cc-sc-desc-color);line-height:var(--cc-sc-desc-line-height)}.cc-summary-card .right-section .value-meta-row .meta-section{display:flex;flex-direction:column;align-items:flex-end;justify-content:flex-start;flex-shrink:0;gap:6px}.cc-summary-card .right-section .value-meta-row .meta-section .meta-item{font-size:.8125rem;line-height:1.4;white-space:nowrap;text-align:right;flex-shrink:0}.cc-summary-card .right-section .value-meta-row .meta-section .meta-item.meta-text{color:var(--cc-sc-desc-color, #64748b);font-weight:500}.cc-summary-card .right-section .value-meta-row .meta-section .meta-item.meta-pill{display:inline-flex;align-items:center;justify-content:center;padding:var(--cc-sc-meta-pill-padding, 4px 12px);border-radius:var(--cc-sc-meta-pill-radius, 20px);font-size:var(--cc-sc-meta-pill-font-size, .75rem);font-weight:var(--cc-sc-meta-pill-font-weight, 600);line-height:1.2;background-color:var(--cc-sc-meta-pill-bg, #f1f5f9);color:var(--cc-sc-meta-pill-color, #475569)}@media(max-width:600px){.cc-summary-card .icon-section{margin-right:var(--cc-sc-icon-margin-mobile, .75rem)}.cc-summary-card .content-section .header{font-size:var(--cc-sc-header-size-mobile, .7rem)}.cc-summary-card .content-section .value-row .value{font-size:var(--cc-sc-value-size-mobile, 1.25rem)}.cc-summary-card .content-section .value-row .description{font-size:var(--cc-sc-desc-size-mobile, .7rem)}}\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: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }] });
|
|
978
977
|
}
|
|
979
978
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SummaryCardComponent, decorators: [{
|
|
980
979
|
type: Component,
|
|
@@ -985,9 +984,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
985
984
|
type: Input
|
|
986
985
|
}], cardClick: [{
|
|
987
986
|
type: Output
|
|
988
|
-
}], hostClasses: [{
|
|
989
|
-
type: HostBinding,
|
|
990
|
-
args: ['class']
|
|
991
987
|
}] } });
|
|
992
988
|
|
|
993
989
|
class SummaryCardModule {
|
|
@@ -1559,12 +1555,10 @@ class ConfigurableFormModule {
|
|
|
1559
1555
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigurableFormModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1560
1556
|
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: ConfigurableFormModule, imports: [CommonModule,
|
|
1561
1557
|
ReactiveFormsModule,
|
|
1562
|
-
HttpClientModule,
|
|
1563
1558
|
MaterialModule,
|
|
1564
1559
|
ConfigurableFormComponent], exports: [ConfigurableFormComponent] });
|
|
1565
1560
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigurableFormModule, imports: [CommonModule,
|
|
1566
1561
|
ReactiveFormsModule,
|
|
1567
|
-
HttpClientModule,
|
|
1568
1562
|
MaterialModule,
|
|
1569
1563
|
ConfigurableFormComponent] });
|
|
1570
1564
|
}
|
|
@@ -1575,7 +1569,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
1575
1569
|
imports: [
|
|
1576
1570
|
CommonModule,
|
|
1577
1571
|
ReactiveFormsModule,
|
|
1578
|
-
HttpClientModule,
|
|
1579
1572
|
MaterialModule,
|
|
1580
1573
|
ConfigurableFormComponent
|
|
1581
1574
|
],
|
|
@@ -2039,6 +2032,508 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
2039
2032
|
}]
|
|
2040
2033
|
}] });
|
|
2041
2034
|
|
|
2035
|
+
class SmartTableComponent {
|
|
2036
|
+
http;
|
|
2037
|
+
router;
|
|
2038
|
+
cdr;
|
|
2039
|
+
ngZone;
|
|
2040
|
+
config;
|
|
2041
|
+
action = new EventEmitter();
|
|
2042
|
+
topAction = new EventEmitter(); // For top bar buttons
|
|
2043
|
+
filterChange = new EventEmitter();
|
|
2044
|
+
rowSelect = new EventEmitter();
|
|
2045
|
+
data = [];
|
|
2046
|
+
totalItems = 0;
|
|
2047
|
+
currentPage = 1;
|
|
2048
|
+
loading = false;
|
|
2049
|
+
// State
|
|
2050
|
+
activeSort = { key: '', direction: 'ASC' };
|
|
2051
|
+
activeFilters = {};
|
|
2052
|
+
searchTerm = '';
|
|
2053
|
+
selectedRows = [];
|
|
2054
|
+
stickyColumnStyles = {};
|
|
2055
|
+
hasStickyColumns = false;
|
|
2056
|
+
searchSubject = new Subject();
|
|
2057
|
+
stickyHeaders;
|
|
2058
|
+
resizeObserver = null;
|
|
2059
|
+
locale = inject(LOCALE_ID);
|
|
2060
|
+
constructor(http, router, cdr, ngZone) {
|
|
2061
|
+
this.http = http;
|
|
2062
|
+
this.router = router;
|
|
2063
|
+
this.cdr = cdr;
|
|
2064
|
+
this.ngZone = ngZone;
|
|
2065
|
+
// Debounce search input
|
|
2066
|
+
this.searchSubject.pipe(debounceTime(this.config?.searchConfig?.debounceTime || 300), distinctUntilChanged()).subscribe(term => {
|
|
2067
|
+
this.searchTerm = term;
|
|
2068
|
+
this.currentPage = 1;
|
|
2069
|
+
this.loadData();
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
2072
|
+
ngOnInit() {
|
|
2073
|
+
if (this.config) {
|
|
2074
|
+
if (this.config.sortBy) {
|
|
2075
|
+
this.activeSort.key = this.config.sortBy;
|
|
2076
|
+
}
|
|
2077
|
+
if (this.config.orderBy) {
|
|
2078
|
+
this.activeSort.direction = this.config.orderBy;
|
|
2079
|
+
}
|
|
2080
|
+
this.loadFilterOptions();
|
|
2081
|
+
this.loadData();
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
ngOnChanges(changes) {
|
|
2085
|
+
if (changes['config'] && !changes['config'].firstChange) {
|
|
2086
|
+
this.loadFilterOptions();
|
|
2087
|
+
this.loadData();
|
|
2088
|
+
setTimeout(() => this.calculateStickyPositions());
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
ngAfterViewInit() {
|
|
2092
|
+
this.setupResizeObserver();
|
|
2093
|
+
}
|
|
2094
|
+
ngOnDestroy() {
|
|
2095
|
+
if (this.resizeObserver) {
|
|
2096
|
+
this.resizeObserver.disconnect();
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
setupResizeObserver() {
|
|
2100
|
+
if (this.resizeObserver) {
|
|
2101
|
+
this.resizeObserver.disconnect();
|
|
2102
|
+
}
|
|
2103
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
2104
|
+
this.ngZone.run(() => {
|
|
2105
|
+
this.calculateStickyPositions();
|
|
2106
|
+
});
|
|
2107
|
+
});
|
|
2108
|
+
if (this.stickyHeaders) {
|
|
2109
|
+
this.stickyHeaders.changes.subscribe(() => {
|
|
2110
|
+
this.observeHeaders();
|
|
2111
|
+
// Also recalculate immediately
|
|
2112
|
+
setTimeout(() => this.calculateStickyPositions());
|
|
2113
|
+
});
|
|
2114
|
+
this.observeHeaders();
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
observeHeaders() {
|
|
2118
|
+
if (!this.resizeObserver || !this.stickyHeaders)
|
|
2119
|
+
return;
|
|
2120
|
+
this.stickyHeaders.forEach(header => {
|
|
2121
|
+
this.resizeObserver.observe(header.nativeElement);
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
loadData() {
|
|
2125
|
+
if (!this.config?.apiUrl)
|
|
2126
|
+
return;
|
|
2127
|
+
this.loading = true;
|
|
2128
|
+
let params;
|
|
2129
|
+
// --- Query Params Construction ---
|
|
2130
|
+
if (this.config.queryParamsConfig) {
|
|
2131
|
+
const qpConfig = this.config.queryParamsConfig;
|
|
2132
|
+
const pageKey = qpConfig.pageKey || 'page';
|
|
2133
|
+
const sizeKey = qpConfig.sizeKey || 'pageSize';
|
|
2134
|
+
const pageIndex = this.currentPage + (qpConfig.pageIndexOffset || 0);
|
|
2135
|
+
let paramsObj = {
|
|
2136
|
+
[pageKey]: pageIndex.toString(),
|
|
2137
|
+
[sizeKey]: (this.config.pagination?.pageSize || 10).toString()
|
|
2138
|
+
};
|
|
2139
|
+
if (this.activeSort.key)
|
|
2140
|
+
paramsObj['sortBy'] = this.activeSort.key;
|
|
2141
|
+
// Note: Some APIs might use different keys for sort/order, can be extended later if needed
|
|
2142
|
+
if (this.activeSort.direction)
|
|
2143
|
+
paramsObj['orderBy'] = this.activeSort.direction;
|
|
2144
|
+
// Search Handling
|
|
2145
|
+
if (this.searchTerm) {
|
|
2146
|
+
const searchConfig = this.config.searchConfig;
|
|
2147
|
+
const searchKey = searchConfig?.searchKey || 'search';
|
|
2148
|
+
if (searchConfig?.handling === 'nested_string' && qpConfig.nestedStringConfig) {
|
|
2149
|
+
// Will be handled in nested string construction below
|
|
2150
|
+
}
|
|
2151
|
+
else {
|
|
2152
|
+
paramsObj[searchKey] = this.searchTerm;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
// Filter Handling (and Search if nested)
|
|
2156
|
+
if (qpConfig.filterHandling === 'nested_string' && qpConfig.nestedStringConfig) {
|
|
2157
|
+
const { paramName, baseValue, separator, assignment } = qpConfig.nestedStringConfig;
|
|
2158
|
+
let nestedString = baseValue || '';
|
|
2159
|
+
const assign = assignment || '=';
|
|
2160
|
+
// Add Filters
|
|
2161
|
+
Object.keys(this.activeFilters).forEach(key => {
|
|
2162
|
+
if (this.activeFilters[key]) {
|
|
2163
|
+
const prefix = nestedString ? separator : '';
|
|
2164
|
+
nestedString += `${prefix}${key}${assign}${this.activeFilters[key]}`;
|
|
2165
|
+
}
|
|
2166
|
+
});
|
|
2167
|
+
// Add Search if nested
|
|
2168
|
+
if (this.searchTerm && this.config.searchConfig?.handling === 'nested_string') {
|
|
2169
|
+
const searchKey = this.config.searchConfig.searchKey || 'SEARCH_TERM';
|
|
2170
|
+
const prefix = nestedString ? separator : '';
|
|
2171
|
+
nestedString += `${prefix}${searchKey}${assign}${this.searchTerm}`;
|
|
2172
|
+
}
|
|
2173
|
+
paramsObj[paramName] = nestedString;
|
|
2174
|
+
}
|
|
2175
|
+
else {
|
|
2176
|
+
// Standard handling
|
|
2177
|
+
Object.keys(this.activeFilters).forEach(key => {
|
|
2178
|
+
if (this.activeFilters[key])
|
|
2179
|
+
paramsObj[key] = this.activeFilters[key];
|
|
2180
|
+
});
|
|
2181
|
+
}
|
|
2182
|
+
params = new HttpParams({ fromObject: paramsObj });
|
|
2183
|
+
}
|
|
2184
|
+
else {
|
|
2185
|
+
// --- Default Behavior (Backward Compatibility) ---
|
|
2186
|
+
let paramsObj = {};
|
|
2187
|
+
if (this.config.pagination?.enabled) {
|
|
2188
|
+
paramsObj['page'] = this.currentPage;
|
|
2189
|
+
paramsObj['pageSize'] = this.config.pagination.pageSize;
|
|
2190
|
+
}
|
|
2191
|
+
if (this.activeSort.key) {
|
|
2192
|
+
paramsObj['sortBy'] = this.activeSort.key;
|
|
2193
|
+
paramsObj['orderBy'] = this.activeSort.direction;
|
|
2194
|
+
}
|
|
2195
|
+
if (this.searchTerm) {
|
|
2196
|
+
const searchKey = this.config.searchConfig?.searchKey || 'search';
|
|
2197
|
+
paramsObj[searchKey] = this.searchTerm;
|
|
2198
|
+
}
|
|
2199
|
+
Object.keys(this.activeFilters).forEach(key => {
|
|
2200
|
+
if (this.activeFilters[key]) {
|
|
2201
|
+
paramsObj[key] = this.activeFilters[key];
|
|
2202
|
+
}
|
|
2203
|
+
});
|
|
2204
|
+
// Custom Request Params Mapper (if provided)
|
|
2205
|
+
if (this.config.requestParams) {
|
|
2206
|
+
const customParams = this.config.requestParams(this.currentPage, this.config.pagination?.pageSize || 10, this.activeSort.key, this.activeSort.direction, this.searchTerm, this.activeFilters);
|
|
2207
|
+
if (customParams instanceof HttpParams) {
|
|
2208
|
+
// If function returns HttpParams, use it directly (caveat: might lose previous params if not careful in mapper)
|
|
2209
|
+
params = customParams;
|
|
2210
|
+
}
|
|
2211
|
+
else {
|
|
2212
|
+
paramsObj = { ...paramsObj, ...customParams };
|
|
2213
|
+
params = new HttpParams({ fromObject: paramsObj });
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
else {
|
|
2217
|
+
params = new HttpParams({ fromObject: paramsObj });
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
// --- Data Fetching ---
|
|
2221
|
+
// Check for separate count API
|
|
2222
|
+
const totalCountConfig = this.config.pagination?.totalCountConfig;
|
|
2223
|
+
let request$; // Observable
|
|
2224
|
+
if (totalCountConfig?.source === 'separate' && totalCountConfig.apiUrl) {
|
|
2225
|
+
request$ = forkJoin({
|
|
2226
|
+
data: this.http.get(this.config.apiUrl, { params }),
|
|
2227
|
+
count: this.http.get(totalCountConfig.apiUrl, { params })
|
|
2228
|
+
}).pipe(map(({ data, count }) => {
|
|
2229
|
+
const dataPath = this.config.dataResponsePath !== undefined ? this.config.dataResponsePath : '';
|
|
2230
|
+
return {
|
|
2231
|
+
data: this.getValueByPath(data, dataPath),
|
|
2232
|
+
total: this.getValueByPath(count, totalCountConfig.responsePath || '')
|
|
2233
|
+
};
|
|
2234
|
+
}));
|
|
2235
|
+
}
|
|
2236
|
+
else {
|
|
2237
|
+
request$ = this.http.get(this.config.apiUrl, { params }).pipe(map(response => {
|
|
2238
|
+
const dataPath = this.config.dataResponsePath !== undefined ? this.config.dataResponsePath : '';
|
|
2239
|
+
const totalPath = totalCountConfig?.responsePath || '';
|
|
2240
|
+
return {
|
|
2241
|
+
data: this.getValueByPath(response, dataPath),
|
|
2242
|
+
// If source is 'same', try to get total from response, else default 0
|
|
2243
|
+
total: totalCountConfig ? this.getValueByPath(response, totalPath) : 0
|
|
2244
|
+
};
|
|
2245
|
+
}));
|
|
2246
|
+
}
|
|
2247
|
+
request$.pipe(finalize(() => this.loading = false), catchError(err => {
|
|
2248
|
+
console.error('Table Data Fetch Error', err);
|
|
2249
|
+
return of({ data: [], total: 0 });
|
|
2250
|
+
}))
|
|
2251
|
+
.subscribe((result) => {
|
|
2252
|
+
this.data = result.data || [];
|
|
2253
|
+
if (this.config.pagination) {
|
|
2254
|
+
this.totalItems = result.total || 0;
|
|
2255
|
+
}
|
|
2256
|
+
// Dynamic Column Generation
|
|
2257
|
+
if (!this.config.columns || this.config.columns.length === 0) {
|
|
2258
|
+
if (this.data.length > 0) {
|
|
2259
|
+
this.config.columns = Object.keys(this.data[0]).map(key => ({
|
|
2260
|
+
key: key,
|
|
2261
|
+
label: this.toTitleCase(key),
|
|
2262
|
+
type: 'text',
|
|
2263
|
+
sortable: false,
|
|
2264
|
+
editable: false
|
|
2265
|
+
}));
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
});
|
|
2269
|
+
}
|
|
2270
|
+
// --- Actions ---
|
|
2271
|
+
onPageChange(page) {
|
|
2272
|
+
this.currentPage = page;
|
|
2273
|
+
this.loadData();
|
|
2274
|
+
}
|
|
2275
|
+
onPageSizeChange(size) {
|
|
2276
|
+
if (this.config.pagination) {
|
|
2277
|
+
this.config.pagination.pageSize = size;
|
|
2278
|
+
this.currentPage = 1; // Reset to first page
|
|
2279
|
+
this.loadData();
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
onSort(col) {
|
|
2283
|
+
if (!col.sortable)
|
|
2284
|
+
return;
|
|
2285
|
+
if (this.activeSort.key === col.key) {
|
|
2286
|
+
this.activeSort.direction = this.activeSort.direction === 'ASC' ? 'DESC' : 'ASC';
|
|
2287
|
+
}
|
|
2288
|
+
else {
|
|
2289
|
+
this.activeSort.key = col.key;
|
|
2290
|
+
this.activeSort.direction = 'ASC';
|
|
2291
|
+
}
|
|
2292
|
+
this.loadData();
|
|
2293
|
+
}
|
|
2294
|
+
onSearch(event) {
|
|
2295
|
+
const value = event.target.value;
|
|
2296
|
+
this.searchSubject.next(value);
|
|
2297
|
+
}
|
|
2298
|
+
onFilterChange(key, event) {
|
|
2299
|
+
const value = event.target.value;
|
|
2300
|
+
this.activeFilters[key] = value;
|
|
2301
|
+
this.currentPage = 1;
|
|
2302
|
+
this.filterChange.emit({ key, value });
|
|
2303
|
+
this.loadData();
|
|
2304
|
+
}
|
|
2305
|
+
onAction(action, row) {
|
|
2306
|
+
if (action.type === 'callback' && action.callback) {
|
|
2307
|
+
action.callback(row);
|
|
2308
|
+
}
|
|
2309
|
+
if (action.type === 'route' && action.route) {
|
|
2310
|
+
const url = this.replaceParams(action.route, row);
|
|
2311
|
+
this.router.navigateByUrl(url);
|
|
2312
|
+
return;
|
|
2313
|
+
}
|
|
2314
|
+
if (action.type === 'api') {
|
|
2315
|
+
if (action.confirmationNeeded) {
|
|
2316
|
+
const message = action.confirmationMessage || this.config.labels?.defaultConfirmationMessage || 'Are you sure?';
|
|
2317
|
+
if (!confirm(message))
|
|
2318
|
+
return;
|
|
2319
|
+
}
|
|
2320
|
+
this.action.emit({ action, row });
|
|
2321
|
+
}
|
|
2322
|
+
else {
|
|
2323
|
+
this.action.emit({ action, row });
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
onTopAction(action) {
|
|
2327
|
+
if (action.type === 'callback' && action.callback) {
|
|
2328
|
+
action.callback(null); // No row for top action
|
|
2329
|
+
}
|
|
2330
|
+
this.topAction.emit(action);
|
|
2331
|
+
}
|
|
2332
|
+
// --- Selection ---
|
|
2333
|
+
onSelectAll(event) {
|
|
2334
|
+
const checked = event.target.checked;
|
|
2335
|
+
this.data.forEach(row => row.selected = checked);
|
|
2336
|
+
this.updateSelectedRows();
|
|
2337
|
+
}
|
|
2338
|
+
onRowSelect(row) {
|
|
2339
|
+
this.updateSelectedRows();
|
|
2340
|
+
}
|
|
2341
|
+
updateSelectedRows() {
|
|
2342
|
+
this.selectedRows = this.data.filter(row => row.selected);
|
|
2343
|
+
this.rowSelect.emit(this.selectedRows);
|
|
2344
|
+
}
|
|
2345
|
+
// --- Helpers ---
|
|
2346
|
+
getCellValue(row, col) {
|
|
2347
|
+
// Support nested properties via labelPath or key
|
|
2348
|
+
const path = col.labelPath || col.key;
|
|
2349
|
+
let val = this.getValueByPath(row, path);
|
|
2350
|
+
// Formatting (Date, etc.)
|
|
2351
|
+
if (col.type === 'date' && val) {
|
|
2352
|
+
if (col.dateFormat) {
|
|
2353
|
+
try {
|
|
2354
|
+
return formatDate(val, col.dateFormat, this.locale);
|
|
2355
|
+
}
|
|
2356
|
+
catch (e) {
|
|
2357
|
+
console.warn('Invalid date format or value', val, col.dateFormat);
|
|
2358
|
+
return val;
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
return new Date(val).toLocaleDateString();
|
|
2362
|
+
}
|
|
2363
|
+
return val;
|
|
2364
|
+
}
|
|
2365
|
+
getBadgeClass(row, col) {
|
|
2366
|
+
const val = this.getCellValue(row, col);
|
|
2367
|
+
const strVal = String(val);
|
|
2368
|
+
// Config approach
|
|
2369
|
+
if (col.badgeConfig && col.badgeConfig[strVal]) {
|
|
2370
|
+
return `badge-${col.badgeConfig[strVal]}`;
|
|
2371
|
+
}
|
|
2372
|
+
// Default Logic
|
|
2373
|
+
const status = strVal.toLowerCase();
|
|
2374
|
+
if (['active', 'completed', 'success', 'approved'].includes(status))
|
|
2375
|
+
return 'badge-success';
|
|
2376
|
+
if (['pending', 'in progress', 'waiting'].includes(status))
|
|
2377
|
+
return 'badge-warning';
|
|
2378
|
+
if (['rejected', 'failed', 'error', 'deleted'].includes(status))
|
|
2379
|
+
return 'badge-danger';
|
|
2380
|
+
if (['draft', 'inactive'].includes(status))
|
|
2381
|
+
return 'badge-neutral';
|
|
2382
|
+
return 'badge-info'; // default
|
|
2383
|
+
}
|
|
2384
|
+
getSortIcon(key) {
|
|
2385
|
+
if (this.activeSort.key !== key)
|
|
2386
|
+
return 'fa-sort';
|
|
2387
|
+
return this.activeSort.direction === 'ASC' ? 'fa-sort-up' : 'fa-sort-down';
|
|
2388
|
+
}
|
|
2389
|
+
replaceParams(template, row) {
|
|
2390
|
+
return template.replace(/:([a-zA-Z0-9_]+)/g, (_, key) => row[key] || '');
|
|
2391
|
+
}
|
|
2392
|
+
loadFilterOptions() {
|
|
2393
|
+
if (!this.config.filters)
|
|
2394
|
+
return;
|
|
2395
|
+
this.config.filters.forEach(filter => {
|
|
2396
|
+
if (filter.apiUrl && !filter.options) {
|
|
2397
|
+
this.http.get(filter.apiUrl).subscribe({
|
|
2398
|
+
next: (response) => {
|
|
2399
|
+
const data = filter.dataPath ? this.getValueByPath(response, filter.dataPath) : response;
|
|
2400
|
+
if (!Array.isArray(data)) {
|
|
2401
|
+
console.error(`Filter data for ${filter.key} is not an array`, data);
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
2404
|
+
filter.options = data.map((item) => {
|
|
2405
|
+
const label = filter.labelPath ? this.getValueByPath(item, filter.labelPath) :
|
|
2406
|
+
(filter.labelKey ? item[filter.labelKey] : item.label || item.name);
|
|
2407
|
+
const value = filter.valuePath ? this.getValueByPath(item, filter.valuePath) :
|
|
2408
|
+
(filter.valueKey ? item[filter.valueKey] : item.value || item.code);
|
|
2409
|
+
return { label, value };
|
|
2410
|
+
});
|
|
2411
|
+
// Auto-populate matching column options
|
|
2412
|
+
if (this.config.columns) {
|
|
2413
|
+
const matchingColumn = this.config.columns.find(col => col.key === filter.key);
|
|
2414
|
+
if (matchingColumn && matchingColumn.dataType === 'select' && !matchingColumn.options) {
|
|
2415
|
+
matchingColumn.options = filter.options;
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
},
|
|
2419
|
+
error: (err) => console.error(`Failed to load filter options for ${filter.key}:`, err)
|
|
2420
|
+
});
|
|
2421
|
+
}
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
getValueByPath(obj, path) {
|
|
2425
|
+
if (!path || path === '')
|
|
2426
|
+
return obj;
|
|
2427
|
+
return path.split('.').reduce((acc, part) => {
|
|
2428
|
+
const match = part.match(/(\w+)\[(\d+)\]/);
|
|
2429
|
+
if (match) {
|
|
2430
|
+
return acc?.[match[1]]?.[parseInt(match[2])];
|
|
2431
|
+
}
|
|
2432
|
+
return acc?.[part];
|
|
2433
|
+
}, obj);
|
|
2434
|
+
}
|
|
2435
|
+
calculateStickyPositions() {
|
|
2436
|
+
// We calculate positions based on rendered widths
|
|
2437
|
+
if (!this.stickyHeaders || this.stickyHeaders.length === 0)
|
|
2438
|
+
return;
|
|
2439
|
+
this.stickyColumnStyles = {};
|
|
2440
|
+
let leftOffset = 0;
|
|
2441
|
+
this.hasStickyColumns = false;
|
|
2442
|
+
// Default sticky columns count is 3 if not specified
|
|
2443
|
+
const stickyCount = this.config.stickyColumnCount !== undefined ? this.config.stickyColumnCount : 3;
|
|
2444
|
+
// Checkbox width handling
|
|
2445
|
+
if (this.config.selectable) {
|
|
2446
|
+
const firstSticky = this.config.columns.find((c, i) => i < stickyCount || c.sticky);
|
|
2447
|
+
if (firstSticky) {
|
|
2448
|
+
// We can try to measure checkbox col if needed, but usually fixed 40px
|
|
2449
|
+
// Or better, if we have a checkbox col ref, assume 40px for now as it is fixed in CSS
|
|
2450
|
+
leftOffset = 40;
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
const headerElements = this.stickyHeaders.toArray();
|
|
2454
|
+
this.config.columns.forEach((col, index) => {
|
|
2455
|
+
if (col.sticky || index < stickyCount) {
|
|
2456
|
+
col.sticky = true;
|
|
2457
|
+
this.hasStickyColumns = false; // Reset to true only after we set styles? No, wait.
|
|
2458
|
+
// Actually property is used in template to add class 'sticky-col'
|
|
2459
|
+
// but we need to update Styles based on previous cols widths
|
|
2460
|
+
// Find corresponding header element
|
|
2461
|
+
// Note: headerElements corresponds to columns indices
|
|
2462
|
+
const headerEl = headerElements[index]?.nativeElement;
|
|
2463
|
+
if (headerEl) {
|
|
2464
|
+
// Set style for current column
|
|
2465
|
+
this.stickyColumnStyles[col.key] = {
|
|
2466
|
+
left: `${leftOffset}px`
|
|
2467
|
+
// We DO NOT set width here, allowing it to be dynamic/auto
|
|
2468
|
+
};
|
|
2469
|
+
// Add THIS column's width to offset for the NEXT column
|
|
2470
|
+
// use getBoundingClientRect or offsetWidth
|
|
2471
|
+
leftOffset += headerEl.offsetWidth;
|
|
2472
|
+
}
|
|
2473
|
+
this.hasStickyColumns = true;
|
|
2474
|
+
}
|
|
2475
|
+
});
|
|
2476
|
+
this.cdr.detectChanges();
|
|
2477
|
+
}
|
|
2478
|
+
toTitleCase(str) {
|
|
2479
|
+
return str
|
|
2480
|
+
.replace(/([A-Z])/g, ' $1') // insert space before capital letters
|
|
2481
|
+
.replace(/^./, (str) => str.toUpperCase()) // capitalize the first letter
|
|
2482
|
+
.trim(); // remove any leading/trailing whitespace
|
|
2483
|
+
}
|
|
2484
|
+
get columnCount() {
|
|
2485
|
+
return this.config.columns.length;
|
|
2486
|
+
}
|
|
2487
|
+
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"] }] });
|
|
2489
|
+
}
|
|
2490
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartTableComponent, decorators: [{
|
|
2491
|
+
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"] }]
|
|
2493
|
+
}], ctorParameters: () => [{ type: i3.HttpClient }, { type: i1$1.Router }, { type: i0.ChangeDetectorRef }, { type: i0.NgZone }], propDecorators: { config: [{
|
|
2494
|
+
type: Input
|
|
2495
|
+
}], action: [{
|
|
2496
|
+
type: Output
|
|
2497
|
+
}], topAction: [{
|
|
2498
|
+
type: Output
|
|
2499
|
+
}], filterChange: [{
|
|
2500
|
+
type: Output
|
|
2501
|
+
}], rowSelect: [{
|
|
2502
|
+
type: Output
|
|
2503
|
+
}], stickyHeaders: [{
|
|
2504
|
+
type: ViewChildren,
|
|
2505
|
+
args: ['stickyHeader']
|
|
2506
|
+
}] } });
|
|
2507
|
+
|
|
2508
|
+
class SmartTableModule {
|
|
2509
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartTableModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
2510
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.15", ngImport: i0, type: SmartTableModule, declarations: [SmartTableComponent], imports: [CommonModule,
|
|
2511
|
+
FormsModule,
|
|
2512
|
+
PaginationModule,
|
|
2513
|
+
ButtonModule], exports: [SmartTableComponent] });
|
|
2514
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartTableModule, imports: [CommonModule,
|
|
2515
|
+
FormsModule,
|
|
2516
|
+
PaginationModule,
|
|
2517
|
+
ButtonModule] });
|
|
2518
|
+
}
|
|
2519
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartTableModule, decorators: [{
|
|
2520
|
+
type: NgModule,
|
|
2521
|
+
args: [{
|
|
2522
|
+
declarations: [
|
|
2523
|
+
SmartTableComponent
|
|
2524
|
+
],
|
|
2525
|
+
imports: [
|
|
2526
|
+
CommonModule,
|
|
2527
|
+
FormsModule,
|
|
2528
|
+
PaginationModule,
|
|
2529
|
+
ButtonModule
|
|
2530
|
+
],
|
|
2531
|
+
exports: [
|
|
2532
|
+
SmartTableComponent
|
|
2533
|
+
]
|
|
2534
|
+
}]
|
|
2535
|
+
}] });
|
|
2536
|
+
|
|
2042
2537
|
/*
|
|
2043
2538
|
* Public API Surface of shared-ui
|
|
2044
2539
|
*/
|
|
@@ -2047,5 +2542,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
2047
2542
|
* Generated bundle index. Do not edit.
|
|
2048
2543
|
*/
|
|
2049
2544
|
|
|
2050
|
-
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, SummaryCardComponent, SummaryCardModule, clearLocalStorage, clearSessionStorage, getLocalStorageItem, getSessionStorageItem, removeLocalStorageItem, removeSessionStorageItem, setLocalStorageItem, setSessionStorageItem };
|
|
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 };
|
|
2051
2546
|
//# sourceMappingURL=commons-shared-web-ui.mjs.map
|