osl-base-extended 7.2.0 → 7.3.0
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.
|
@@ -24,6 +24,7 @@ import { MAT_DATE_FORMATS } from '@angular/material/core';
|
|
|
24
24
|
import * as i4 from '@ngxmc/datetime-picker';
|
|
25
25
|
import { NgxMatDatetimepicker, NgxMatDatepickerInput } from '@ngxmc/datetime-picker';
|
|
26
26
|
import { MatInputModule } from '@angular/material/input';
|
|
27
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
27
28
|
import { debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1, switchMap, tap } from 'rxjs/operators';
|
|
28
29
|
import { MatMenuModule } from '@angular/material/menu';
|
|
29
30
|
import * as i3$1 from '@angular/cdk/drag-drop';
|
|
@@ -3529,6 +3530,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3529
3530
|
class OslSetup {
|
|
3530
3531
|
_injector = inject(Injector);
|
|
3531
3532
|
_stateService = inject(OslSetupStateService);
|
|
3533
|
+
santizer = inject(DomSanitizer);
|
|
3532
3534
|
formLoading = false;
|
|
3533
3535
|
saveLoading = false;
|
|
3534
3536
|
restoredRow = null;
|
|
@@ -3544,6 +3546,7 @@ class OslSetup {
|
|
|
3544
3546
|
cardGridRef;
|
|
3545
3547
|
// ── Inputs ────────────────────────────────────────────────────
|
|
3546
3548
|
title = '';
|
|
3549
|
+
titleIcon = '';
|
|
3547
3550
|
columns = [];
|
|
3548
3551
|
datasource = [];
|
|
3549
3552
|
isPaginated = true;
|
|
@@ -3664,6 +3667,9 @@ class OslSetup {
|
|
|
3664
3667
|
this.cardPageSize = size;
|
|
3665
3668
|
this.pageSizeChange.emit({ page: 1, pageSize: Number(size), searchValue: this.searchbar?.searchControl?.value ?? '' });
|
|
3666
3669
|
}
|
|
3670
|
+
renderIcon(icon) {
|
|
3671
|
+
return this.santizer.bypassSecurityTrustHtml(icon);
|
|
3672
|
+
}
|
|
3667
3673
|
// ── Lifecycle ─────────────────────────────────────────────────
|
|
3668
3674
|
ngOnInit() {
|
|
3669
3675
|
this._loadViewMode();
|
|
@@ -3979,11 +3985,11 @@ class OslSetup {
|
|
|
3979
3985
|
});
|
|
3980
3986
|
}
|
|
3981
3987
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslSetup, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3982
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslSetup, isStandalone: false, selector: "osl-setup", inputs: { title: "title", columns: "columns", datasource: "datasource", isPaginated: "isPaginated", pageSize: "pageSize", autoMode: "autoMode", tableHeight: "tableHeight", totalRecords: "totalRecords", loading: "loading", dialogWidth: "dialogWidth", formElements: "formElements", beforeDisplay: "beforeDisplay", onAddEditFn: "onAddEditFn", isLister: "isLister", canAdd: "canAdd", canEdit: "canEdit", canDelete: "canDelete", moreMenuActions: "moreMenuActions", customFormFooter: "customFormFooter", customHeaderTemp: "customHeaderTemp", partialCustomHeaderTemp: "partialCustomHeaderTemp", stateKey: "stateKey", primaryKey: "primaryKey", onSave: "onSave", cardPageSize: "cardPageSize", cardTemplate: "cardTemplate", cardCol: "cardCol" }, outputs: { onSearch: "onSearch", onAdd: "onAdd", onEdit: "onEdit", onDelete: "onDelete", pageChange: "pageChange", pageSizeChange: "pageSizeChange", sortChange: "sortChange", onRowClick: "onRowClick", onStateRestored: "onStateRestored" }, host: { listeners: { "document:click": "onDocumentClick()" } }, viewQueries: [{ propertyName: "formBodyTpl", first: true, predicate: ["formBodyTpl"], descendants: true }, { propertyName: "formFooterTpl", first: true, predicate: ["formFooterTpl"], descendants: true }, { propertyName: "customFooterWrapperTpl", first: true, predicate: ["customFooterWrapperTpl"], descendants: true }, { propertyName: "searchbar", first: true, predicate: ["searchbar"], descendants: true }, { propertyName: "gridRef", first: true, predicate: ["gridRef"], descendants: true }, { propertyName: "cardGridRef", first: true, predicate: ["cardGridRef"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"p-2\">\n\n <!-- \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"osl-setup-header\">\n <h5 class=\"mb-0\">{{ title }}</h5>\n <div class=\"d-flex align-items-center gap-2\">\n <osl-searchbar #searchbar class=\"mx-2\" (onSearch)=\"onSearchSetup($event)\"></osl-searchbar>\n\n <!-- View Toggle -->\n @if(!isLister){\n <div class=\"osl-view-toggle\">\n <button class=\"osl-view-toggle-btn\" [class.osl-view-toggle-btn--active]=\"viewMode === 'table'\"\n (click)=\"toggleView('table')\" title=\"Table view\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/>\n <line x1=\"3\" y1=\"15\" x2=\"21\" y2=\"15\"/>\n <line x1=\"9\" y1=\"9\" x2=\"9\" y2=\"21\"/>\n </svg>\n </button>\n <button class=\"osl-view-toggle-btn\" [class.osl-view-toggle-btn--active]=\"viewMode === 'card'\"\n (click)=\"toggleView('card')\" title=\"Card view\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n </svg>\n </button>\n </div>\n }\n\n @if (!isLister && canAdd) {\n <osl-button variant=\"secondary\" size=\"sm\" [label]=\"'Add ' + title\" (clickEv)=\"openAddDialog()\"></osl-button>\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Table View \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (viewMode === 'table') {\n <div class=\"osl-setup-body my-2\">\n <osl-grid\n #gridRef\n [columns]=\"columnsWithActions\"\n [(datasource)]=\"datasource\"\n [isPaginated]=\"isPaginated\"\n [pageSize]=\"pageSize\"\n [autoMode]=\"autoMode\"\n [tableHeight]=\"tableHeight\"\n [totalRecords]=\"totalRecords\"\n [loading]=\"loading\"\n [moreMenuActions]=\"moreMenuActions\"\n [canEdit]=\"canEdit\"\n [canDelete]=\"canDelete\"\n [highlightedRow]=\"restoredRow\"\n [primaryKey]=\"primaryKey\"\n (editClick)=\"openEditDialog($event)\"\n (deleteClick)=\"onDeleteClick($event)\"\n (pageChange)=\"onPageChange(pageChange, $event)\"\n (pageSizeChange)=\"onPageChange(pageSizeChange, $event)\"\n (sortChange)=\"sortChange.emit($event)\"\n [isSelectable]=\"isLister\"\n (onRowClick)=\"onRowClick.emit($event)\"\n />\n </div>\n }\n\n <!-- \u2500\u2500 Card View \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (viewMode === 'card') {\n\n <!-- Skeleton on initial load -->\n @if (loading && cardPagedData.length === 0) {\n <div class=\"osl-card-grid my-2\">\n @for (sk of skeletonCardRows; track $index) {\n <div class=\"osl-card osl-card--skeleton\">\n <div class=\"osl-card-header osl-card-header--skeleton\">\n <div class=\"osl-card-title-wrap\">\n <div class=\"osl-sk-line osl-sk-line--title\"></div>\n </div>\n </div>\n <div class=\"osl-card-body\">\n <div class=\"row g-0\">\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line\"></div></div>\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line osl-sk-line--short\"></div></div>\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line\"></div></div>\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line osl-sk-line--short\"></div></div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Empty state -->\n @else if (cardPagedData.length === 0 && !loading) {\n <div class=\"osl-card-empty my-2\">\n <svg class=\"osl-card-empty-svg\" viewBox=\"0 0 180 160\" fill=\"none\">\n <ellipse cx=\"90\" cy=\"140\" rx=\"60\" ry=\"8\" fill=\"#f3f4f6\"/>\n <rect x=\"30\" y=\"30\" width=\"120\" height=\"96\" rx=\"10\" fill=\"#f9fafb\" stroke=\"#e5e7eb\" stroke-width=\"2\"/>\n <rect x=\"44\" y=\"48\" width=\"92\" height=\"10\" rx=\"5\" fill=\"#e5e7eb\"/>\n <rect x=\"44\" y=\"66\" width=\"72\" height=\"8\" rx=\"4\" fill=\"#f3f4f6\"/>\n <rect x=\"44\" y=\"82\" width=\"84\" height=\"8\" rx=\"4\" fill=\"#f3f4f6\"/>\n <rect x=\"44\" y=\"98\" width=\"56\" height=\"8\" rx=\"4\" fill=\"#f3f4f6\"/>\n <circle cx=\"140\" cy=\"108\" r=\"26\" fill=\"#fff\" stroke=\"#e5e7eb\" stroke-width=\"2\"/>\n <line x1=\"133\" y1=\"101\" x2=\"147\" y2=\"115\" stroke=\"#d1d5db\" stroke-width=\"2.5\" stroke-linecap=\"round\"/>\n <line x1=\"147\" y1=\"101\" x2=\"133\" y2=\"115\" stroke=\"#d1d5db\" stroke-width=\"2.5\" stroke-linecap=\"round\"/>\n </svg>\n <p class=\"osl-card-empty-title\">No records found</p>\n <p class=\"osl-card-empty-desc\">Try adjusting your search or filter criteria</p>\n </div>\n }\n\n <!-- Card grid -->\n @else {\n <div #cardGridRef class=\"osl-card-grid my-2\" [style.height]=\"tableHeight\">\n @for (row of cardPagedData; track row[primaryKey] ?? $index; let i = $index) {\n\n @if (cardTemplate) {\n <ng-container *ngTemplateOutlet=\"cardTemplate; context: { $implicit: row, index: i }\"></ng-container>\n } @else {\n <div class=\"osl-card my-2\"\n [class.osl-card--selectable]=\"isLister\"\n [class.osl-card--highlighted]=\"isHighlightedCard(row)\"\n (click)=\"isLister ? onRowClick.emit(row) : null\">\n\n <!-- Card Header -->\n <div class=\"osl-card-header\">\n <!-- <svg class=\"osl-card-watermark\" viewBox=\"0 0 120 64\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M10 42 L18 52 L102 52 L110 42 Z\"/>\n <rect x=\"20\" y=\"34\" width=\"80\" height=\"9\" rx=\"1\"/>\n <rect x=\"26\" y=\"20\" width=\"16\" height=\"15\" rx=\"1\"/>\n <rect x=\"46\" y=\"20\" width=\"16\" height=\"15\" rx=\"1\"/>\n <rect x=\"66\" y=\"20\" width=\"16\" height=\"15\" rx=\"1\"/>\n <rect x=\"85\" y=\"14\" width=\"15\" height=\"21\" rx=\"2\"/>\n <rect x=\"90.5\" y=\"7\" width=\"2\" height=\"9\" rx=\"1\"/>\n <path d=\"M4 57 Q18 53 32 57 Q46 61 60 57 Q74 53 88 57 Q102 61 116 57\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\"/>\n </svg> -->\n <div class=\"osl-card-title-wrap\">\n <span class=\"osl-card-title\"\n >\n @if (cardTitleColumn) {\n <span\n >\n @switch (cardTitleColumn.displayType) {\n @case ('date') { {{ row[cardTitleColumn.key] | date }} }\n @case ('datetime') { {{ row[cardTitleColumn.key] | date:'medium' }} }\n @case ('time') { {{ row[cardTitleColumn.key] | date:'shortTime' }} }\n @case ('customDateFormat') { {{ row[cardTitleColumn.key] | date:cardTitleColumn.customDateFormat }} }\n @default { {{ getCellValue(row, cardTitleColumn) }} }\n }\n </span>\n }\n </span>\n </div>\n @if (!isLister && ((hasForm && (canEdit || canDelete)) || (moreMenuActions.length > 0 && hasVisibleActions(row)))) {\n <div class=\"osl-card-actions\">\n @if (hasForm && canEdit) {\n <button class=\"osl-card-action-btn osl-card-action-btn--edit\"\n (click)=\"$event.stopPropagation(); openEditDialog(row)\"\n [oslTooltip]=\"'Edit'\" oslTooltipPosition=\"bottom\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/>\n <path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/>\n </svg>\n </button>\n }\n @if (hasForm && canDelete) {\n <button class=\"osl-card-action-btn osl-card-action-btn--delete\"\n (click)=\"$event.stopPropagation(); onDeleteClick(row)\"\n [oslTooltip]=\"'Delete'\" oslTooltipPosition=\"bottom\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" xmlns=\"http://www.w3.org/2000/svg\">\n <polyline points=\"3 6 5 6 21 6\"/>\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/>\n <line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"/>\n </svg>\n </button>\n }\n @if (moreMenuActions.length > 0 && hasVisibleActions(row)) {\n <button class=\"osl-card-action-btn osl-card-action-btn--more\"\n (click)=\"toggleCardMenu(i, $event)\"\n [oslTooltip]=\"'More actions'\" oslTooltipPosition=\"bottom\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"12\" cy=\"5\" r=\"2.2\"/>\n <circle cx=\"12\" cy=\"12\" r=\"2.2\"/>\n <circle cx=\"12\" cy=\"19\" r=\"2.2\"/>\n </svg>\n </button>\n }\n </div>\n }\n </div>\n\n <!-- Card Body -->\n @if (cardBodyColumns.length > 0) {\n <div class=\"osl-card-body\">\n <div class=\"row g-0\">\n @for (col of cardBodyColumns; track col.key) {\n <div [class]=\"'col-' + cardCol + ' osl-card-field'\">\n <span class=\"osl-card-label\">{{ col.label }}</span>\n <span class=\"osl-card-value\" [class.link]=\"col.displayType === 'link'\" (click)=\"col.click ? col.click(row, col) : null\">\n\n <span>\n @switch (col.displayType) {\n @case ('date') { {{ row[col.key] | date }} }\n @case ('datetime') { {{ row[col.key] | date:'medium' }} }\n @case ('time') { {{ row[col.key] | date:'shortTime' }} }\n @case ('customDateFormat') { {{ row[col.key] | date:col.customDateFormat }} }\n @default { {{ getCellValue(row, col) }} }\n }\n </span>\n </span>\n </div>\n }\n </div>\n </div>\n }\n\n </div>\n }\n }\n </div>\n }\n\n <!-- Card Paginator -->\n @if (isPaginated && _cardTotal > 0) {\n <div class=\"osl-grid-pagination\">\n <span class=\"osl-grid-pagination__info\">\n @if (loading) {\n Loading...\n } @else {\n {{ cardStartRecord }}\u2013{{ cardEndRecord }} of {{ _cardTotal }} records\n }\n </span>\n\n <div class=\"osl-grid-pagination__controls\">\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(1)\"\n [disabled]=\"cardCurrentPage === 1 || loading\" title=\"First page\">\u00AB</button>\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(cardCurrentPage - 1)\"\n [disabled]=\"cardCurrentPage === 1 || loading\" title=\"Previous page\">\u2039 Prev</button>\n\n @for (page of cardPageNumbers; track $index) {\n @if (page === -1) {\n <span class=\"osl-grid-page-ellipsis\">\u2026</span>\n } @else {\n <button class=\"osl-grid-page-btn osl-grid-page-btn--num\"\n [class.osl-grid-page-btn--active]=\"page === cardCurrentPage\"\n [disabled]=\"loading\"\n (click)=\"cardGoToPage(page)\">{{ page }}</button>\n }\n }\n\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(cardCurrentPage + 1)\"\n [disabled]=\"cardCurrentPage === cardTotalPages || loading\" title=\"Next page\">Next \u203A</button>\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(cardTotalPages)\"\n [disabled]=\"cardCurrentPage === cardTotalPages || loading\" title=\"Last page\">\u00BB</button>\n </div>\n\n <div class=\"osl-grid-pagination__size\">\n <select class=\"osl-grid-page-size\"\n [ngModel]=\"_effectiveCardPageSize\"\n (ngModelChange)=\"cardOnPageSizeChange($event)\"\n [disabled]=\"loading\">\n @for (opt of cardPageSizeOptions; track opt) {\n <option [value]=\"opt\">{{ opt }} per page</option>\n }\n </select>\n </div>\n </div>\n }\n }\n\n</div>\n\n<!-- Dialog: Form Body -->\n<ng-template #formBodyTpl>\n <osl-dynamic-form\n [skeletonLoading]=\"formLoading\"\n [elements]=\"formElements\"\n [(model)]=\"dialogModel\"\n ></osl-dynamic-form>\n</ng-template>\n\n<!-- Dialog: Form Footer -->\n<ng-template #formFooterTpl>\n <div class=\"osl-setup-dialog-footer\">\n <osl-button [loading]=\"saveLoading\" variant=\"secondary\" label=\"Save\" (click)=\"saveDialog()\"></osl-button>\n </div>\n</ng-template>\n\n<ng-template #customFooterWrapperTpl let-data>\n <ng-container *ngTemplateOutlet=\"customFormFooter!; context: { $implicit: { dialogModel: dialogModel, dialogMode: dialogMode, dialogRef: data.dialogRef } }\"></ng-container>\n</ng-template>\n\n<!-- Floating card more-actions menu -->\n@if (viewMode === 'card' && cardOpenMenuIndex !== null && moreMenuActions.length > 0) {\n <div class=\"osl-card-menu\"\n [style.top.px]=\"cardMenuPosition.top\"\n [style.left.px]=\"cardMenuPosition.left\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"osl-card-menu-header\">Actions</div>\n @for (action of moreMenuActions; track $index) {\n @if (!action.hideIf || !action.hideIf(cardPagedData[cardOpenMenuIndex])) {\n <button class=\"osl-card-menu-item\"\n (click)=\"action.click(cardPagedData[cardOpenMenuIndex]); cardOpenMenuIndex = null\">\n <span class=\"osl-card-menu-dot\"></span>\n {{ action.labelIf ? action.labelIf(cardPagedData[cardOpenMenuIndex]) : action.label }}\n </button>\n }\n }\n </div>\n}\n", styles: [".osl-setup-header{display:flex;align-items:center;justify-content:space-between}.osl-setup-dialog-footer{display:flex;align-items:center;justify-content:flex-end;gap:10px;width:100%}.dialog-cancel-btn{display:flex;align-items:center;gap:6px;height:38px;font-size:13px;font-weight:500;border-color:var(--osl-border-color, #d1d5db);color:var(--osl-secondary, #6b7280);border-radius:var(--osl-border-radius, 4px);padding:0 16px;transition:all .2s ease}.dialog-cancel-btn:hover{border-color:#9ca3af;background:#f9fafb;color:#374151}.dialog-save-btn{display:flex;align-items:center;gap:6px;height:38px;font-size:13px;font-weight:500;background:linear-gradient(135deg,var(--osl-primary, #2563eb),#3b82f6);color:#fff;border-radius:var(--osl-border-radius, 4px);padding:0 18px;transition:all .2s ease;box-shadow:0 2px 8px #2563eb40}.dialog-save-btn:hover{background:linear-gradient(135deg,var(--osl-primary-hover, #1d4ed8),#2563eb);box-shadow:0 4px 14px #2563eb66;transform:translateY(-1px)}.dialog-save-btn:active{transform:translateY(0);box-shadow:0 2px 8px #2563eb40}.osl-view-toggle{display:flex;border:1.5px solid var(--osl-border-color, #e5e7eb);border-radius:8px;overflow:hidden;flex-shrink:0;background:#fff}.osl-view-toggle-btn{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border:none;background:transparent;cursor:pointer;color:#9ca3af;transition:background .15s,color .15s;padding:0}.osl-view-toggle-btn svg{width:16px;height:16px;transition:stroke .15s}.osl-view-toggle-btn:first-child{border-right:1.5px solid var(--osl-border-color, #e5e7eb)}.osl-view-toggle-btn--active{background:var(--osl-primary, #6366f1);color:#fff}.osl-view-toggle-btn--active svg{stroke:#fff}.osl-view-toggle-btn:not(.osl-view-toggle-btn--active):hover{background:#f3f4f6;color:#374151}.osl-card-grid{display:flexbox;width:100%;overflow:auto}@keyframes osl-card-enter{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.osl-card-grid .osl-card:nth-child(1){animation-delay:0s}.osl-card-grid .osl-card:nth-child(2){animation-delay:.03s}.osl-card-grid .osl-card:nth-child(3){animation-delay:.06s}.osl-card-grid .osl-card:nth-child(4){animation-delay:.09s}.osl-card-grid .osl-card:nth-child(5){animation-delay:.12s}.osl-card-grid .osl-card:nth-child(6){animation-delay:.15s}.osl-card-grid .osl-card:nth-child(7){animation-delay:.18s}.osl-card-grid .osl-card:nth-child(8){animation-delay:.21s}.osl-card{position:relative;background:#fff;border-radius:12px;border:1px solid #eaecf0;box-shadow:0 1px 3px #1018280f,0 1px 2px #1018280a;overflow:hidden;transition:transform .2s cubic-bezier(.34,1.56,.64,1),box-shadow .2s ease,border-color .2s ease;animation:osl-card-enter .26s ease both}.osl-card:hover{transform:translateY(-2px);box-shadow:0 8px 24px #1018281a,0 2px 6px #1018280f}.osl-card--selectable{cursor:pointer}.osl-card--highlighted{border-color:var(--card-accent, #6366f1);box-shadow:0 0 0 3px color-mix(in srgb,var(--card-accent, #6366f1) 20%,transparent),0 4px 16px #1018281a}.osl-card--skeleton{pointer-events:none;animation:osl-card-enter .26s ease both}.osl-card--skeleton:hover{transform:none;box-shadow:0 1px 3px #1018280f}@keyframes osl-card-highlight-pulse{0%{box-shadow:0 0 0 4px color-mix(in srgb,var(--card-accent, #6366f1) 30%,transparent)}to{box-shadow:none}}.osl-card-header{position:relative;overflow:hidden;display:flex;align-items:center;gap:10px;padding:9px 12px 9px 14px;border-bottom:1px solid rgba(0,0,0,.05);min-height:42px}.osl-card-header--skeleton{background:#f9fafb}.osl-card-watermark{position:absolute;right:8px;top:50%;transform:translateY(-50%);width:72px;height:40px;color:var(--card-accent, var(--osl-primary, #6366f1));opacity:.1;pointer-events:none;flex-shrink:0}.osl-card-title-wrap{flex:1;min-width:0;position:relative;z-index:1}.osl-card-title{display:block;font-weight:700;font-size:13px;color:#1e293b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.osl-card-actions{display:flex;align-items:center;gap:4px;flex-shrink:0;position:relative;z-index:1}.osl-card-action-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:7px;border:1.5px solid rgba(255,255,255,.9);background:#fffc;cursor:pointer;transition:background .15s,border-color .15s,transform .15s,box-shadow .15s;padding:0}.osl-card-action-btn svg{width:13px;height:13px;transition:stroke .15s,fill .15s}.osl-card-action-btn--edit svg{stroke:#6b7280}.osl-card-action-btn--edit:hover{background:#fff;border-color:var(--card-accent, #6366f1);box-shadow:0 2px 8px #0000001f;transform:scale(1.1)}.osl-card-action-btn--edit:hover svg{stroke:var(--card-accent, #6366f1)}.osl-card-action-btn--delete svg{stroke:#9ca3af}.osl-card-action-btn--delete:hover{background:#fff1f2;border-color:#f43f5e;box-shadow:0 2px 8px #f43f5e33;transform:scale(1.1)}.osl-card-action-btn--delete:hover svg{stroke:#f43f5e}.osl-card-action-btn--more svg{fill:#9ca3af;stroke:none}.osl-card-action-btn--more:hover{background:#fff;border-color:var(--card-accent, #6366f1);box-shadow:0 2px 8px #0000001f;transform:scale(1.1)}.osl-card-action-btn--more:hover svg{fill:var(--card-accent, #6366f1)}.osl-card-body{padding:8px 12px 10px 14px}.osl-card-field{padding:4px 8px 2px 0;min-width:0;overflow:hidden}.osl-card-label{display:block;font-size:9.5px;font-weight:600;color:#9ca3af;text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-bottom:1px}.osl-card-value{display:block;font-size:12.5px;font-weight:500;color:#374151;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.osl-card-value.link{text-decoration:underline;color:#2563eb;cursor:pointer}.osl-card-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:320px;gap:12px;padding:40px 20px}.osl-card-empty-svg{width:180px;height:auto;opacity:.85}.osl-card-empty-title{font-size:16px;font-weight:600;color:#374151;margin:4px 0 0;text-align:center}.osl-card-empty-desc{font-size:13px;color:#9ca3af;margin:0;text-align:center}@keyframes osl-sk-pulse{0%,to{opacity:1}50%{opacity:.4}}.osl-sk-line{height:12px;background:#e9eaec;border-radius:6px;animation:osl-sk-pulse 1.4s ease-in-out infinite;width:80%}.osl-sk-line--title{height:13px;width:60%}.osl-sk-line--label{width:40%;height:9px;margin-bottom:4px}.osl-sk-line--short{width:50%}.osl-card-menu{position:fixed;z-index:9999;min-width:196px;background:#fff;border:1px solid #eaecf0;border-radius:12px;box-shadow:0 12px 32px #10182824,0 4px 12px #1018280f;overflow:hidden;animation:osl-menu-pop .16s cubic-bezier(.16,1,.3,1)}@keyframes osl-menu-pop{0%{opacity:0;transform:translateY(-8px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.osl-card-menu-header{padding:10px 14px 7px;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#9ca3af;border-bottom:1px solid #f2f4f7;-webkit-user-select:none;user-select:none}.osl-card-menu-item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 14px;background:transparent;border:none;border-left:3px solid transparent;border-bottom:1px solid #f9fafb;text-align:left;font-size:13px;font-weight:500;font-family:inherit;color:#374151;cursor:pointer;white-space:nowrap;transition:background .12s,color .12s,border-left-color .12s}.osl-card-menu-item:last-child{border-bottom:none}.osl-card-menu-item:hover{background:var(--card-accent-bg, #f5f3ff);color:var(--card-accent, var(--osl-primary, #6366f1));border-left-color:var(--card-accent, var(--osl-primary, #6366f1))}.osl-card-menu-item:hover .osl-card-menu-dot{background:var(--card-accent, var(--osl-primary, #6366f1));box-shadow:0 0 0 3px #6366f129}.osl-card-menu-dot{flex-shrink:0;width:6px;height:6px;border-radius:50%;background:#d1d5db;transition:background .12s,box-shadow .12s}.osl-grid-pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:10px;background:#f9fafb;flex-wrap:wrap;margin-top:4px}.osl-grid-pagination__info{font-size:12px;color:#6b7280;white-space:nowrap;min-width:120px}.osl-grid-pagination__controls{display:flex;align-items:center;gap:3px;flex-wrap:nowrap}.osl-grid-pagination__size{display:flex;align-items:center;min-width:120px;justify-content:flex-end}.osl-grid-page-btn{display:inline-flex;align-items:center;justify-content:center;height:30px;padding:0 10px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:13px;font-family:inherit;cursor:pointer;transition:background .12s,border-color .12s,color .12s;white-space:nowrap}.osl-grid-page-btn:hover:not(:disabled){background:#f3f4f6;border-color:#9ca3af}.osl-grid-page-btn:disabled{opacity:.35;cursor:not-allowed}.osl-grid-page-btn--nav{font-size:12px;color:#6b7280}.osl-grid-page-btn--num{min-width:30px;padding:0;font-size:13px}.osl-grid-page-btn--active{background:var(--osl-primary, #6366f1);border-color:var(--osl-primary, #6366f1);color:#fff;font-weight:600}.osl-grid-page-btn--active:hover:not(:disabled){background:var(--osl-primary-hover, #4f46e5);border-color:var(--osl-primary-hover, #4f46e5)}.osl-grid-page-ellipsis{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;color:#9ca3af;font-size:13px;pointer-events:none}.osl-grid-page-size{height:30px;padding:0 8px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:12px;font-family:inherit;cursor:pointer;outline:none}.osl-grid-page-size:focus{border-color:var(--osl-primary, #6366f1)}\n"], dependencies: [{ kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: OslTooltipDirective, selector: "[oslTooltip]", inputs: ["oslTooltip", "oslTooltipPosition", "oslTooltipAnimation", "oslTooltipDisabled", "oslTooltipMaxWidth"] }, { kind: "component", type: DynamicForm, selector: "osl-dynamic-form", inputs: ["elements", "model", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange"] }, { kind: "component", type: OslButton, selector: "osl-button", inputs: ["label", "icon", "variant", "size", "disabled", "loading", "type", "fullWidth"], outputs: ["clickEv"] }, { kind: "component", type: OslSearchbar, selector: "osl-searchbar", inputs: ["label"], outputs: ["onSearch"] }, { kind: "component", type: OslGrid, selector: "osl-grid", inputs: ["columns", "datasource", "isPaginated", "pageSize", "autoMode", "totalRecords", "tableHeight", "loading", "isSelectable", "moreMenuActions", "canEdit", "canDelete", "highlightedRow", "primaryKey"], outputs: ["datasourceChange", "pageChange", "pageSizeChange", "sortChange", "editClick", "deleteClick", "onRowClick"] }, { kind: "pipe", type: i1$2.DatePipe, name: "date" }] });
|
|
3988
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslSetup, isStandalone: false, selector: "osl-setup", inputs: { title: "title", titleIcon: "titleIcon", columns: "columns", datasource: "datasource", isPaginated: "isPaginated", pageSize: "pageSize", autoMode: "autoMode", tableHeight: "tableHeight", totalRecords: "totalRecords", loading: "loading", dialogWidth: "dialogWidth", formElements: "formElements", beforeDisplay: "beforeDisplay", onAddEditFn: "onAddEditFn", isLister: "isLister", canAdd: "canAdd", canEdit: "canEdit", canDelete: "canDelete", moreMenuActions: "moreMenuActions", customFormFooter: "customFormFooter", customHeaderTemp: "customHeaderTemp", partialCustomHeaderTemp: "partialCustomHeaderTemp", stateKey: "stateKey", primaryKey: "primaryKey", onSave: "onSave", cardPageSize: "cardPageSize", cardTemplate: "cardTemplate", cardCol: "cardCol" }, outputs: { onSearch: "onSearch", onAdd: "onAdd", onEdit: "onEdit", onDelete: "onDelete", pageChange: "pageChange", pageSizeChange: "pageSizeChange", sortChange: "sortChange", onRowClick: "onRowClick", onStateRestored: "onStateRestored" }, host: { listeners: { "document:click": "onDocumentClick()" } }, viewQueries: [{ propertyName: "formBodyTpl", first: true, predicate: ["formBodyTpl"], descendants: true }, { propertyName: "formFooterTpl", first: true, predicate: ["formFooterTpl"], descendants: true }, { propertyName: "customFooterWrapperTpl", first: true, predicate: ["customFooterWrapperTpl"], descendants: true }, { propertyName: "searchbar", first: true, predicate: ["searchbar"], descendants: true }, { propertyName: "gridRef", first: true, predicate: ["gridRef"], descendants: true }, { propertyName: "cardGridRef", first: true, predicate: ["cardGridRef"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"p-2\">\n\n <!-- \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"osl-setup-header\">\n <div class=\"d-flex align-items-center\">\n @if(titleIcon){\n\n <span class=\"title-icon mx-2 mb-1\" [innerHTML]=\"renderIcon(titleIcon)\"></span>\n }\n <h5 class=\"mb-0\">{{ title }}</h5>\n\n </div>\n <div class=\"d-flex align-items-center gap-2\">\n <osl-searchbar #searchbar class=\"mx-2\" (onSearch)=\"onSearchSetup($event)\"></osl-searchbar>\n\n <!-- View Toggle -->\n @if(!isLister){\n <div class=\"osl-view-toggle\">\n <button class=\"osl-view-toggle-btn\" [class.osl-view-toggle-btn--active]=\"viewMode === 'table'\"\n (click)=\"toggleView('table')\" title=\"Table view\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/>\n <line x1=\"3\" y1=\"15\" x2=\"21\" y2=\"15\"/>\n <line x1=\"9\" y1=\"9\" x2=\"9\" y2=\"21\"/>\n </svg>\n </button>\n <button class=\"osl-view-toggle-btn\" [class.osl-view-toggle-btn--active]=\"viewMode === 'card'\"\n (click)=\"toggleView('card')\" title=\"Card view\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n </svg>\n </button>\n </div>\n }\n\n @if (!isLister && canAdd) {\n <osl-button variant=\"secondary\" size=\"sm\" [label]=\"'Add ' + title\" (clickEv)=\"openAddDialog()\"></osl-button>\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Table View \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (viewMode === 'table') {\n <div class=\"osl-setup-body my-2\">\n <osl-grid\n #gridRef\n [columns]=\"columnsWithActions\"\n [(datasource)]=\"datasource\"\n [isPaginated]=\"isPaginated\"\n [pageSize]=\"pageSize\"\n [autoMode]=\"autoMode\"\n [tableHeight]=\"tableHeight\"\n [totalRecords]=\"totalRecords\"\n [loading]=\"loading\"\n [moreMenuActions]=\"moreMenuActions\"\n [canEdit]=\"canEdit\"\n [canDelete]=\"canDelete\"\n [highlightedRow]=\"restoredRow\"\n [primaryKey]=\"primaryKey\"\n (editClick)=\"openEditDialog($event)\"\n (deleteClick)=\"onDeleteClick($event)\"\n (pageChange)=\"onPageChange(pageChange, $event)\"\n (pageSizeChange)=\"onPageChange(pageSizeChange, $event)\"\n (sortChange)=\"sortChange.emit($event)\"\n [isSelectable]=\"isLister\"\n (onRowClick)=\"onRowClick.emit($event)\"\n />\n </div>\n }\n\n <!-- \u2500\u2500 Card View \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (viewMode === 'card') {\n\n <!-- Skeleton on initial load -->\n @if (loading && cardPagedData.length === 0) {\n <div class=\"osl-card-grid my-2\">\n @for (sk of skeletonCardRows; track $index) {\n <div class=\"osl-card osl-card--skeleton\">\n <div class=\"osl-card-header osl-card-header--skeleton\">\n <div class=\"osl-card-title-wrap\">\n <div class=\"osl-sk-line osl-sk-line--title\"></div>\n </div>\n </div>\n <div class=\"osl-card-body\">\n <div class=\"row g-0\">\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line\"></div></div>\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line osl-sk-line--short\"></div></div>\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line\"></div></div>\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line osl-sk-line--short\"></div></div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Empty state -->\n @else if (cardPagedData.length === 0 && !loading) {\n <div class=\"osl-card-empty my-2\">\n <svg class=\"osl-card-empty-svg\" viewBox=\"0 0 180 160\" fill=\"none\">\n <ellipse cx=\"90\" cy=\"140\" rx=\"60\" ry=\"8\" fill=\"#f3f4f6\"/>\n <rect x=\"30\" y=\"30\" width=\"120\" height=\"96\" rx=\"10\" fill=\"#f9fafb\" stroke=\"#e5e7eb\" stroke-width=\"2\"/>\n <rect x=\"44\" y=\"48\" width=\"92\" height=\"10\" rx=\"5\" fill=\"#e5e7eb\"/>\n <rect x=\"44\" y=\"66\" width=\"72\" height=\"8\" rx=\"4\" fill=\"#f3f4f6\"/>\n <rect x=\"44\" y=\"82\" width=\"84\" height=\"8\" rx=\"4\" fill=\"#f3f4f6\"/>\n <rect x=\"44\" y=\"98\" width=\"56\" height=\"8\" rx=\"4\" fill=\"#f3f4f6\"/>\n <circle cx=\"140\" cy=\"108\" r=\"26\" fill=\"#fff\" stroke=\"#e5e7eb\" stroke-width=\"2\"/>\n <line x1=\"133\" y1=\"101\" x2=\"147\" y2=\"115\" stroke=\"#d1d5db\" stroke-width=\"2.5\" stroke-linecap=\"round\"/>\n <line x1=\"147\" y1=\"101\" x2=\"133\" y2=\"115\" stroke=\"#d1d5db\" stroke-width=\"2.5\" stroke-linecap=\"round\"/>\n </svg>\n <p class=\"osl-card-empty-title\">No records found</p>\n <p class=\"osl-card-empty-desc\">Try adjusting your search or filter criteria</p>\n </div>\n }\n\n <!-- Card grid -->\n @else {\n <div #cardGridRef class=\"osl-card-grid my-2\" [style.height]=\"tableHeight\">\n @for (row of cardPagedData; track row[primaryKey] ?? $index; let i = $index) {\n\n @if (cardTemplate) {\n <ng-container *ngTemplateOutlet=\"cardTemplate; context: { $implicit: row, index: i }\"></ng-container>\n } @else {\n <div class=\"osl-card my-2\"\n [class.osl-card--selectable]=\"isLister\"\n [class.osl-card--highlighted]=\"isHighlightedCard(row)\"\n (click)=\"isLister ? onRowClick.emit(row) : null\">\n\n <!-- Card Header -->\n <div class=\"osl-card-header\">\n <!-- <svg class=\"osl-card-watermark\" viewBox=\"0 0 120 64\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M10 42 L18 52 L102 52 L110 42 Z\"/>\n <rect x=\"20\" y=\"34\" width=\"80\" height=\"9\" rx=\"1\"/>\n <rect x=\"26\" y=\"20\" width=\"16\" height=\"15\" rx=\"1\"/>\n <rect x=\"46\" y=\"20\" width=\"16\" height=\"15\" rx=\"1\"/>\n <rect x=\"66\" y=\"20\" width=\"16\" height=\"15\" rx=\"1\"/>\n <rect x=\"85\" y=\"14\" width=\"15\" height=\"21\" rx=\"2\"/>\n <rect x=\"90.5\" y=\"7\" width=\"2\" height=\"9\" rx=\"1\"/>\n <path d=\"M4 57 Q18 53 32 57 Q46 61 60 57 Q74 53 88 57 Q102 61 116 57\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\"/>\n </svg> -->\n <div class=\"osl-card-title-wrap\">\n <span class=\"osl-card-title\"\n >\n @if (cardTitleColumn) {\n <span\n >\n @switch (cardTitleColumn.displayType) {\n @case ('date') { {{ row[cardTitleColumn.key] | date }} }\n @case ('datetime') { {{ row[cardTitleColumn.key] | date:'medium' }} }\n @case ('time') { {{ row[cardTitleColumn.key] | date:'shortTime' }} }\n @case ('customDateFormat') { {{ row[cardTitleColumn.key] | date:cardTitleColumn.customDateFormat }} }\n @default { {{ getCellValue(row, cardTitleColumn) }} }\n }\n </span>\n }\n </span>\n </div>\n @if (!isLister && ((hasForm && (canEdit || canDelete)) || (moreMenuActions.length > 0 && hasVisibleActions(row)))) {\n <div class=\"osl-card-actions\">\n @if (hasForm && canEdit) {\n <button class=\"osl-card-action-btn osl-card-action-btn--edit\"\n (click)=\"$event.stopPropagation(); openEditDialog(row)\"\n [oslTooltip]=\"'Edit'\" oslTooltipPosition=\"bottom\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/>\n <path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/>\n </svg>\n </button>\n }\n @if (hasForm && canDelete) {\n <button class=\"osl-card-action-btn osl-card-action-btn--delete\"\n (click)=\"$event.stopPropagation(); onDeleteClick(row)\"\n [oslTooltip]=\"'Delete'\" oslTooltipPosition=\"bottom\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" xmlns=\"http://www.w3.org/2000/svg\">\n <polyline points=\"3 6 5 6 21 6\"/>\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/>\n <line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"/>\n </svg>\n </button>\n }\n @if (moreMenuActions.length > 0 && hasVisibleActions(row)) {\n <button class=\"osl-card-action-btn osl-card-action-btn--more\"\n (click)=\"toggleCardMenu(i, $event)\"\n [oslTooltip]=\"'More actions'\" oslTooltipPosition=\"bottom\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"12\" cy=\"5\" r=\"2.2\"/>\n <circle cx=\"12\" cy=\"12\" r=\"2.2\"/>\n <circle cx=\"12\" cy=\"19\" r=\"2.2\"/>\n </svg>\n </button>\n }\n </div>\n }\n </div>\n\n <!-- Card Body -->\n @if (cardBodyColumns.length > 0) {\n <div class=\"osl-card-body\">\n <div class=\"row g-0\">\n @for (col of cardBodyColumns; track col.key) {\n <div [class]=\"'col-' + cardCol + ' osl-card-field'\">\n <span class=\"osl-card-label\">{{ col.label }}</span>\n <span class=\"osl-card-value\" [class.link]=\"col.displayType === 'link'\" (click)=\"col.click ? col.click(row, col) : null\">\n\n <span>\n @switch (col.displayType) {\n @case ('date') { {{ row[col.key] | date }} }\n @case ('datetime') { {{ row[col.key] | date:'medium' }} }\n @case ('time') { {{ row[col.key] | date:'shortTime' }} }\n @case ('customDateFormat') { {{ row[col.key] | date:col.customDateFormat }} }\n @default { {{ getCellValue(row, col) }} }\n }\n </span>\n </span>\n </div>\n }\n </div>\n </div>\n }\n\n </div>\n }\n }\n </div>\n }\n\n <!-- Card Paginator -->\n @if (isPaginated && _cardTotal > 0) {\n <div class=\"osl-grid-pagination\">\n <span class=\"osl-grid-pagination__info\">\n @if (loading) {\n Loading...\n } @else {\n {{ cardStartRecord }}\u2013{{ cardEndRecord }} of {{ _cardTotal }} records\n }\n </span>\n\n <div class=\"osl-grid-pagination__controls\">\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(1)\"\n [disabled]=\"cardCurrentPage === 1 || loading\" title=\"First page\">\u00AB</button>\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(cardCurrentPage - 1)\"\n [disabled]=\"cardCurrentPage === 1 || loading\" title=\"Previous page\">\u2039 Prev</button>\n\n @for (page of cardPageNumbers; track $index) {\n @if (page === -1) {\n <span class=\"osl-grid-page-ellipsis\">\u2026</span>\n } @else {\n <button class=\"osl-grid-page-btn osl-grid-page-btn--num\"\n [class.osl-grid-page-btn--active]=\"page === cardCurrentPage\"\n [disabled]=\"loading\"\n (click)=\"cardGoToPage(page)\">{{ page }}</button>\n }\n }\n\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(cardCurrentPage + 1)\"\n [disabled]=\"cardCurrentPage === cardTotalPages || loading\" title=\"Next page\">Next \u203A</button>\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(cardTotalPages)\"\n [disabled]=\"cardCurrentPage === cardTotalPages || loading\" title=\"Last page\">\u00BB</button>\n </div>\n\n <div class=\"osl-grid-pagination__size\">\n <select class=\"osl-grid-page-size\"\n [ngModel]=\"_effectiveCardPageSize\"\n (ngModelChange)=\"cardOnPageSizeChange($event)\"\n [disabled]=\"loading\">\n @for (opt of cardPageSizeOptions; track opt) {\n <option [value]=\"opt\">{{ opt }} per page</option>\n }\n </select>\n </div>\n </div>\n }\n }\n\n</div>\n\n<!-- Dialog: Form Body -->\n<ng-template #formBodyTpl>\n <osl-dynamic-form\n [skeletonLoading]=\"formLoading\"\n [elements]=\"formElements\"\n [(model)]=\"dialogModel\"\n ></osl-dynamic-form>\n</ng-template>\n\n<!-- Dialog: Form Footer -->\n<ng-template #formFooterTpl>\n <div class=\"osl-setup-dialog-footer\">\n <osl-button [loading]=\"saveLoading\" variant=\"secondary\" label=\"Save\" (click)=\"saveDialog()\"></osl-button>\n </div>\n</ng-template>\n\n<ng-template #customFooterWrapperTpl let-data>\n <ng-container *ngTemplateOutlet=\"customFormFooter!; context: { $implicit: { dialogModel: dialogModel, dialogMode: dialogMode, dialogRef: data.dialogRef } }\"></ng-container>\n</ng-template>\n\n<!-- Floating card more-actions menu -->\n@if (viewMode === 'card' && cardOpenMenuIndex !== null && moreMenuActions.length > 0) {\n <div class=\"osl-card-menu\"\n [style.top.px]=\"cardMenuPosition.top\"\n [style.left.px]=\"cardMenuPosition.left\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"osl-card-menu-header\">Actions</div>\n @for (action of moreMenuActions; track $index) {\n @if (!action.hideIf || !action.hideIf(cardPagedData[cardOpenMenuIndex])) {\n <button class=\"osl-card-menu-item\"\n (click)=\"action.click(cardPagedData[cardOpenMenuIndex]); cardOpenMenuIndex = null\">\n <span class=\"osl-card-menu-dot\"></span>\n {{ action.labelIf ? action.labelIf(cardPagedData[cardOpenMenuIndex]) : action.label }}\n </button>\n }\n }\n </div>\n}\n", styles: [".osl-setup-header{display:flex;align-items:center;justify-content:space-between}.osl-setup-dialog-footer{display:flex;align-items:center;justify-content:flex-end;gap:10px;width:100%}.dialog-cancel-btn{display:flex;align-items:center;gap:6px;height:38px;font-size:13px;font-weight:500;border-color:var(--osl-border-color, #d1d5db);color:var(--osl-secondary, #6b7280);border-radius:var(--osl-border-radius, 4px);padding:0 16px;transition:all .2s ease}.dialog-cancel-btn:hover{border-color:#9ca3af;background:#f9fafb;color:#374151}.dialog-save-btn{display:flex;align-items:center;gap:6px;height:38px;font-size:13px;font-weight:500;background:linear-gradient(135deg,var(--osl-primary, #2563eb),#3b82f6);color:#fff;border-radius:var(--osl-border-radius, 4px);padding:0 18px;transition:all .2s ease;box-shadow:0 2px 8px #2563eb40}.dialog-save-btn:hover{background:linear-gradient(135deg,var(--osl-primary-hover, #1d4ed8),#2563eb);box-shadow:0 4px 14px #2563eb66;transform:translateY(-1px)}.dialog-save-btn:active{transform:translateY(0);box-shadow:0 2px 8px #2563eb40}.osl-view-toggle{display:flex;border:1.5px solid var(--osl-border-color, #e5e7eb);border-radius:8px;overflow:hidden;flex-shrink:0;background:#fff}.osl-view-toggle-btn{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border:none;background:transparent;cursor:pointer;color:#9ca3af;transition:background .15s,color .15s;padding:0}.osl-view-toggle-btn svg{width:16px;height:16px;transition:stroke .15s}.osl-view-toggle-btn:first-child{border-right:1.5px solid var(--osl-border-color, #e5e7eb)}.osl-view-toggle-btn--active{background:var(--osl-primary, #6366f1);color:#fff}.osl-view-toggle-btn--active svg{stroke:#fff}.osl-view-toggle-btn:not(.osl-view-toggle-btn--active):hover{background:#f3f4f6;color:#374151}.osl-card-grid{display:flexbox;width:100%;overflow:auto}@keyframes osl-card-enter{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.osl-card-grid .osl-card:nth-child(1){animation-delay:0s}.osl-card-grid .osl-card:nth-child(2){animation-delay:.03s}.osl-card-grid .osl-card:nth-child(3){animation-delay:.06s}.osl-card-grid .osl-card:nth-child(4){animation-delay:.09s}.osl-card-grid .osl-card:nth-child(5){animation-delay:.12s}.osl-card-grid .osl-card:nth-child(6){animation-delay:.15s}.osl-card-grid .osl-card:nth-child(7){animation-delay:.18s}.osl-card-grid .osl-card:nth-child(8){animation-delay:.21s}.osl-card{position:relative;background:#fff;border-radius:12px;border:1px solid #eaecf0;box-shadow:0 1px 3px #1018280f,0 1px 2px #1018280a;overflow:hidden;transition:transform .2s cubic-bezier(.34,1.56,.64,1),box-shadow .2s ease,border-color .2s ease;animation:osl-card-enter .26s ease both}.osl-card:hover{transform:translateY(-2px);box-shadow:0 8px 24px #1018281a,0 2px 6px #1018280f}.osl-card--selectable{cursor:pointer}.osl-card--highlighted{border-color:var(--card-accent, #6366f1);box-shadow:0 0 0 3px color-mix(in srgb,var(--card-accent, #6366f1) 20%,transparent),0 4px 16px #1018281a}.osl-card--skeleton{pointer-events:none;animation:osl-card-enter .26s ease both}.osl-card--skeleton:hover{transform:none;box-shadow:0 1px 3px #1018280f}@keyframes osl-card-highlight-pulse{0%{box-shadow:0 0 0 4px color-mix(in srgb,var(--card-accent, #6366f1) 30%,transparent)}to{box-shadow:none}}.osl-card-header{position:relative;overflow:hidden;display:flex;align-items:center;gap:10px;padding:9px 12px 9px 14px;border-bottom:1px solid rgba(0,0,0,.05);min-height:42px}.osl-card-header--skeleton{background:#f9fafb}.osl-card-watermark{position:absolute;right:8px;top:50%;transform:translateY(-50%);width:72px;height:40px;color:var(--card-accent, var(--osl-primary, #6366f1));opacity:.1;pointer-events:none;flex-shrink:0}.osl-card-title-wrap{flex:1;min-width:0;position:relative;z-index:1}.osl-card-title{display:block;font-weight:700;font-size:13px;color:#1e293b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.osl-card-actions{display:flex;align-items:center;gap:4px;flex-shrink:0;position:relative;z-index:1}.osl-card-action-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:7px;border:1.5px solid rgba(255,255,255,.9);background:#fffc;cursor:pointer;transition:background .15s,border-color .15s,transform .15s,box-shadow .15s;padding:0}.osl-card-action-btn svg{width:13px;height:13px;transition:stroke .15s,fill .15s}.osl-card-action-btn--edit svg{stroke:#6b7280}.osl-card-action-btn--edit:hover{background:#fff;border-color:var(--card-accent, #6366f1);box-shadow:0 2px 8px #0000001f;transform:scale(1.1)}.osl-card-action-btn--edit:hover svg{stroke:var(--card-accent, #6366f1)}.osl-card-action-btn--delete svg{stroke:#9ca3af}.osl-card-action-btn--delete:hover{background:#fff1f2;border-color:#f43f5e;box-shadow:0 2px 8px #f43f5e33;transform:scale(1.1)}.osl-card-action-btn--delete:hover svg{stroke:#f43f5e}.osl-card-action-btn--more svg{fill:#9ca3af;stroke:none}.osl-card-action-btn--more:hover{background:#fff;border-color:var(--card-accent, #6366f1);box-shadow:0 2px 8px #0000001f;transform:scale(1.1)}.osl-card-action-btn--more:hover svg{fill:var(--card-accent, #6366f1)}.osl-card-body{padding:8px 12px 10px 14px}.osl-card-field{padding:4px 8px 2px 0;min-width:0;overflow:hidden}.osl-card-label{display:block;font-size:9.5px;font-weight:600;color:#9ca3af;text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-bottom:1px}.osl-card-value{display:block;font-size:12.5px;font-weight:500;color:#374151;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.osl-card-value.link{text-decoration:underline;color:#2563eb;cursor:pointer}.osl-card-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:320px;gap:12px;padding:40px 20px}.osl-card-empty-svg{width:180px;height:auto;opacity:.85}.osl-card-empty-title{font-size:16px;font-weight:600;color:#374151;margin:4px 0 0;text-align:center}.osl-card-empty-desc{font-size:13px;color:#9ca3af;margin:0;text-align:center}@keyframes osl-sk-pulse{0%,to{opacity:1}50%{opacity:.4}}.osl-sk-line{height:12px;background:#e9eaec;border-radius:6px;animation:osl-sk-pulse 1.4s ease-in-out infinite;width:80%}.osl-sk-line--title{height:13px;width:60%}.osl-sk-line--label{width:40%;height:9px;margin-bottom:4px}.osl-sk-line--short{width:50%}.osl-card-menu{position:fixed;z-index:9999;min-width:196px;background:#fff;border:1px solid #eaecf0;border-radius:12px;box-shadow:0 12px 32px #10182824,0 4px 12px #1018280f;overflow:hidden;animation:osl-menu-pop .16s cubic-bezier(.16,1,.3,1)}@keyframes osl-menu-pop{0%{opacity:0;transform:translateY(-8px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.osl-card-menu-header{padding:10px 14px 7px;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#9ca3af;border-bottom:1px solid #f2f4f7;-webkit-user-select:none;user-select:none}.osl-card-menu-item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 14px;background:transparent;border:none;border-left:3px solid transparent;border-bottom:1px solid #f9fafb;text-align:left;font-size:13px;font-weight:500;font-family:inherit;color:#374151;cursor:pointer;white-space:nowrap;transition:background .12s,color .12s,border-left-color .12s}.osl-card-menu-item:last-child{border-bottom:none}.osl-card-menu-item:hover{background:var(--card-accent-bg, #f5f3ff);color:var(--card-accent, var(--osl-primary, #6366f1));border-left-color:var(--card-accent, var(--osl-primary, #6366f1))}.osl-card-menu-item:hover .osl-card-menu-dot{background:var(--card-accent, var(--osl-primary, #6366f1));box-shadow:0 0 0 3px #6366f129}.osl-card-menu-dot{flex-shrink:0;width:6px;height:6px;border-radius:50%;background:#d1d5db;transition:background .12s,box-shadow .12s}.osl-grid-pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:10px;background:#f9fafb;flex-wrap:wrap;margin-top:4px}.osl-grid-pagination__info{font-size:12px;color:#6b7280;white-space:nowrap;min-width:120px}.osl-grid-pagination__controls{display:flex;align-items:center;gap:3px;flex-wrap:nowrap}.osl-grid-pagination__size{display:flex;align-items:center;min-width:120px;justify-content:flex-end}.osl-grid-page-btn{display:inline-flex;align-items:center;justify-content:center;height:30px;padding:0 10px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:13px;font-family:inherit;cursor:pointer;transition:background .12s,border-color .12s,color .12s;white-space:nowrap}.osl-grid-page-btn:hover:not(:disabled){background:#f3f4f6;border-color:#9ca3af}.osl-grid-page-btn:disabled{opacity:.35;cursor:not-allowed}.osl-grid-page-btn--nav{font-size:12px;color:#6b7280}.osl-grid-page-btn--num{min-width:30px;padding:0;font-size:13px}.osl-grid-page-btn--active{background:var(--osl-primary, #6366f1);border-color:var(--osl-primary, #6366f1);color:#fff;font-weight:600}.osl-grid-page-btn--active:hover:not(:disabled){background:var(--osl-primary-hover, #4f46e5);border-color:var(--osl-primary-hover, #4f46e5)}.osl-grid-page-ellipsis{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;color:#9ca3af;font-size:13px;pointer-events:none}.osl-grid-page-size{height:30px;padding:0 8px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:12px;font-family:inherit;cursor:pointer;outline:none}.osl-grid-page-size:focus{border-color:var(--osl-primary, #6366f1)}.title-icon{width:20px}\n"], dependencies: [{ kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: OslTooltipDirective, selector: "[oslTooltip]", inputs: ["oslTooltip", "oslTooltipPosition", "oslTooltipAnimation", "oslTooltipDisabled", "oslTooltipMaxWidth"] }, { kind: "component", type: DynamicForm, selector: "osl-dynamic-form", inputs: ["elements", "model", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange"] }, { kind: "component", type: OslButton, selector: "osl-button", inputs: ["label", "icon", "variant", "size", "disabled", "loading", "type", "fullWidth"], outputs: ["clickEv"] }, { kind: "component", type: OslSearchbar, selector: "osl-searchbar", inputs: ["label"], outputs: ["onSearch"] }, { kind: "component", type: OslGrid, selector: "osl-grid", inputs: ["columns", "datasource", "isPaginated", "pageSize", "autoMode", "totalRecords", "tableHeight", "loading", "isSelectable", "moreMenuActions", "canEdit", "canDelete", "highlightedRow", "primaryKey"], outputs: ["datasourceChange", "pageChange", "pageSizeChange", "sortChange", "editClick", "deleteClick", "onRowClick"] }, { kind: "pipe", type: i1$2.DatePipe, name: "date" }] });
|
|
3983
3989
|
}
|
|
3984
3990
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslSetup, decorators: [{
|
|
3985
3991
|
type: Component,
|
|
3986
|
-
args: [{ selector: 'osl-setup', standalone: false, template: "<div class=\"p-2\">\n\n <!-- \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"osl-setup-header\">\n <h5 class=\"mb-0\">{{ title }}</h5>\n <div class=\"d-flex align-items-center gap-2\">\n <osl-searchbar #searchbar class=\"mx-2\" (onSearch)=\"onSearchSetup($event)\"></osl-searchbar>\n\n <!-- View Toggle -->\n @if(!isLister){\n <div class=\"osl-view-toggle\">\n <button class=\"osl-view-toggle-btn\" [class.osl-view-toggle-btn--active]=\"viewMode === 'table'\"\n (click)=\"toggleView('table')\" title=\"Table view\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/>\n <line x1=\"3\" y1=\"15\" x2=\"21\" y2=\"15\"/>\n <line x1=\"9\" y1=\"9\" x2=\"9\" y2=\"21\"/>\n </svg>\n </button>\n <button class=\"osl-view-toggle-btn\" [class.osl-view-toggle-btn--active]=\"viewMode === 'card'\"\n (click)=\"toggleView('card')\" title=\"Card view\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n </svg>\n </button>\n </div>\n }\n\n @if (!isLister && canAdd) {\n <osl-button variant=\"secondary\" size=\"sm\" [label]=\"'Add ' + title\" (clickEv)=\"openAddDialog()\"></osl-button>\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Table View \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (viewMode === 'table') {\n <div class=\"osl-setup-body my-2\">\n <osl-grid\n #gridRef\n [columns]=\"columnsWithActions\"\n [(datasource)]=\"datasource\"\n [isPaginated]=\"isPaginated\"\n [pageSize]=\"pageSize\"\n [autoMode]=\"autoMode\"\n [tableHeight]=\"tableHeight\"\n [totalRecords]=\"totalRecords\"\n [loading]=\"loading\"\n [moreMenuActions]=\"moreMenuActions\"\n [canEdit]=\"canEdit\"\n [canDelete]=\"canDelete\"\n [highlightedRow]=\"restoredRow\"\n [primaryKey]=\"primaryKey\"\n (editClick)=\"openEditDialog($event)\"\n (deleteClick)=\"onDeleteClick($event)\"\n (pageChange)=\"onPageChange(pageChange, $event)\"\n (pageSizeChange)=\"onPageChange(pageSizeChange, $event)\"\n (sortChange)=\"sortChange.emit($event)\"\n [isSelectable]=\"isLister\"\n (onRowClick)=\"onRowClick.emit($event)\"\n />\n </div>\n }\n\n <!-- \u2500\u2500 Card View \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (viewMode === 'card') {\n\n <!-- Skeleton on initial load -->\n @if (loading && cardPagedData.length === 0) {\n <div class=\"osl-card-grid my-2\">\n @for (sk of skeletonCardRows; track $index) {\n <div class=\"osl-card osl-card--skeleton\">\n <div class=\"osl-card-header osl-card-header--skeleton\">\n <div class=\"osl-card-title-wrap\">\n <div class=\"osl-sk-line osl-sk-line--title\"></div>\n </div>\n </div>\n <div class=\"osl-card-body\">\n <div class=\"row g-0\">\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line\"></div></div>\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line osl-sk-line--short\"></div></div>\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line\"></div></div>\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line osl-sk-line--short\"></div></div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Empty state -->\n @else if (cardPagedData.length === 0 && !loading) {\n <div class=\"osl-card-empty my-2\">\n <svg class=\"osl-card-empty-svg\" viewBox=\"0 0 180 160\" fill=\"none\">\n <ellipse cx=\"90\" cy=\"140\" rx=\"60\" ry=\"8\" fill=\"#f3f4f6\"/>\n <rect x=\"30\" y=\"30\" width=\"120\" height=\"96\" rx=\"10\" fill=\"#f9fafb\" stroke=\"#e5e7eb\" stroke-width=\"2\"/>\n <rect x=\"44\" y=\"48\" width=\"92\" height=\"10\" rx=\"5\" fill=\"#e5e7eb\"/>\n <rect x=\"44\" y=\"66\" width=\"72\" height=\"8\" rx=\"4\" fill=\"#f3f4f6\"/>\n <rect x=\"44\" y=\"82\" width=\"84\" height=\"8\" rx=\"4\" fill=\"#f3f4f6\"/>\n <rect x=\"44\" y=\"98\" width=\"56\" height=\"8\" rx=\"4\" fill=\"#f3f4f6\"/>\n <circle cx=\"140\" cy=\"108\" r=\"26\" fill=\"#fff\" stroke=\"#e5e7eb\" stroke-width=\"2\"/>\n <line x1=\"133\" y1=\"101\" x2=\"147\" y2=\"115\" stroke=\"#d1d5db\" stroke-width=\"2.5\" stroke-linecap=\"round\"/>\n <line x1=\"147\" y1=\"101\" x2=\"133\" y2=\"115\" stroke=\"#d1d5db\" stroke-width=\"2.5\" stroke-linecap=\"round\"/>\n </svg>\n <p class=\"osl-card-empty-title\">No records found</p>\n <p class=\"osl-card-empty-desc\">Try adjusting your search or filter criteria</p>\n </div>\n }\n\n <!-- Card grid -->\n @else {\n <div #cardGridRef class=\"osl-card-grid my-2\" [style.height]=\"tableHeight\">\n @for (row of cardPagedData; track row[primaryKey] ?? $index; let i = $index) {\n\n @if (cardTemplate) {\n <ng-container *ngTemplateOutlet=\"cardTemplate; context: { $implicit: row, index: i }\"></ng-container>\n } @else {\n <div class=\"osl-card my-2\"\n [class.osl-card--selectable]=\"isLister\"\n [class.osl-card--highlighted]=\"isHighlightedCard(row)\"\n (click)=\"isLister ? onRowClick.emit(row) : null\">\n\n <!-- Card Header -->\n <div class=\"osl-card-header\">\n <!-- <svg class=\"osl-card-watermark\" viewBox=\"0 0 120 64\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M10 42 L18 52 L102 52 L110 42 Z\"/>\n <rect x=\"20\" y=\"34\" width=\"80\" height=\"9\" rx=\"1\"/>\n <rect x=\"26\" y=\"20\" width=\"16\" height=\"15\" rx=\"1\"/>\n <rect x=\"46\" y=\"20\" width=\"16\" height=\"15\" rx=\"1\"/>\n <rect x=\"66\" y=\"20\" width=\"16\" height=\"15\" rx=\"1\"/>\n <rect x=\"85\" y=\"14\" width=\"15\" height=\"21\" rx=\"2\"/>\n <rect x=\"90.5\" y=\"7\" width=\"2\" height=\"9\" rx=\"1\"/>\n <path d=\"M4 57 Q18 53 32 57 Q46 61 60 57 Q74 53 88 57 Q102 61 116 57\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\"/>\n </svg> -->\n <div class=\"osl-card-title-wrap\">\n <span class=\"osl-card-title\"\n >\n @if (cardTitleColumn) {\n <span\n >\n @switch (cardTitleColumn.displayType) {\n @case ('date') { {{ row[cardTitleColumn.key] | date }} }\n @case ('datetime') { {{ row[cardTitleColumn.key] | date:'medium' }} }\n @case ('time') { {{ row[cardTitleColumn.key] | date:'shortTime' }} }\n @case ('customDateFormat') { {{ row[cardTitleColumn.key] | date:cardTitleColumn.customDateFormat }} }\n @default { {{ getCellValue(row, cardTitleColumn) }} }\n }\n </span>\n }\n </span>\n </div>\n @if (!isLister && ((hasForm && (canEdit || canDelete)) || (moreMenuActions.length > 0 && hasVisibleActions(row)))) {\n <div class=\"osl-card-actions\">\n @if (hasForm && canEdit) {\n <button class=\"osl-card-action-btn osl-card-action-btn--edit\"\n (click)=\"$event.stopPropagation(); openEditDialog(row)\"\n [oslTooltip]=\"'Edit'\" oslTooltipPosition=\"bottom\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/>\n <path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/>\n </svg>\n </button>\n }\n @if (hasForm && canDelete) {\n <button class=\"osl-card-action-btn osl-card-action-btn--delete\"\n (click)=\"$event.stopPropagation(); onDeleteClick(row)\"\n [oslTooltip]=\"'Delete'\" oslTooltipPosition=\"bottom\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" xmlns=\"http://www.w3.org/2000/svg\">\n <polyline points=\"3 6 5 6 21 6\"/>\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/>\n <line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"/>\n </svg>\n </button>\n }\n @if (moreMenuActions.length > 0 && hasVisibleActions(row)) {\n <button class=\"osl-card-action-btn osl-card-action-btn--more\"\n (click)=\"toggleCardMenu(i, $event)\"\n [oslTooltip]=\"'More actions'\" oslTooltipPosition=\"bottom\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"12\" cy=\"5\" r=\"2.2\"/>\n <circle cx=\"12\" cy=\"12\" r=\"2.2\"/>\n <circle cx=\"12\" cy=\"19\" r=\"2.2\"/>\n </svg>\n </button>\n }\n </div>\n }\n </div>\n\n <!-- Card Body -->\n @if (cardBodyColumns.length > 0) {\n <div class=\"osl-card-body\">\n <div class=\"row g-0\">\n @for (col of cardBodyColumns; track col.key) {\n <div [class]=\"'col-' + cardCol + ' osl-card-field'\">\n <span class=\"osl-card-label\">{{ col.label }}</span>\n <span class=\"osl-card-value\" [class.link]=\"col.displayType === 'link'\" (click)=\"col.click ? col.click(row, col) : null\">\n\n <span>\n @switch (col.displayType) {\n @case ('date') { {{ row[col.key] | date }} }\n @case ('datetime') { {{ row[col.key] | date:'medium' }} }\n @case ('time') { {{ row[col.key] | date:'shortTime' }} }\n @case ('customDateFormat') { {{ row[col.key] | date:col.customDateFormat }} }\n @default { {{ getCellValue(row, col) }} }\n }\n </span>\n </span>\n </div>\n }\n </div>\n </div>\n }\n\n </div>\n }\n }\n </div>\n }\n\n <!-- Card Paginator -->\n @if (isPaginated && _cardTotal > 0) {\n <div class=\"osl-grid-pagination\">\n <span class=\"osl-grid-pagination__info\">\n @if (loading) {\n Loading...\n } @else {\n {{ cardStartRecord }}\u2013{{ cardEndRecord }} of {{ _cardTotal }} records\n }\n </span>\n\n <div class=\"osl-grid-pagination__controls\">\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(1)\"\n [disabled]=\"cardCurrentPage === 1 || loading\" title=\"First page\">\u00AB</button>\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(cardCurrentPage - 1)\"\n [disabled]=\"cardCurrentPage === 1 || loading\" title=\"Previous page\">\u2039 Prev</button>\n\n @for (page of cardPageNumbers; track $index) {\n @if (page === -1) {\n <span class=\"osl-grid-page-ellipsis\">\u2026</span>\n } @else {\n <button class=\"osl-grid-page-btn osl-grid-page-btn--num\"\n [class.osl-grid-page-btn--active]=\"page === cardCurrentPage\"\n [disabled]=\"loading\"\n (click)=\"cardGoToPage(page)\">{{ page }}</button>\n }\n }\n\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(cardCurrentPage + 1)\"\n [disabled]=\"cardCurrentPage === cardTotalPages || loading\" title=\"Next page\">Next \u203A</button>\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(cardTotalPages)\"\n [disabled]=\"cardCurrentPage === cardTotalPages || loading\" title=\"Last page\">\u00BB</button>\n </div>\n\n <div class=\"osl-grid-pagination__size\">\n <select class=\"osl-grid-page-size\"\n [ngModel]=\"_effectiveCardPageSize\"\n (ngModelChange)=\"cardOnPageSizeChange($event)\"\n [disabled]=\"loading\">\n @for (opt of cardPageSizeOptions; track opt) {\n <option [value]=\"opt\">{{ opt }} per page</option>\n }\n </select>\n </div>\n </div>\n }\n }\n\n</div>\n\n<!-- Dialog: Form Body -->\n<ng-template #formBodyTpl>\n <osl-dynamic-form\n [skeletonLoading]=\"formLoading\"\n [elements]=\"formElements\"\n [(model)]=\"dialogModel\"\n ></osl-dynamic-form>\n</ng-template>\n\n<!-- Dialog: Form Footer -->\n<ng-template #formFooterTpl>\n <div class=\"osl-setup-dialog-footer\">\n <osl-button [loading]=\"saveLoading\" variant=\"secondary\" label=\"Save\" (click)=\"saveDialog()\"></osl-button>\n </div>\n</ng-template>\n\n<ng-template #customFooterWrapperTpl let-data>\n <ng-container *ngTemplateOutlet=\"customFormFooter!; context: { $implicit: { dialogModel: dialogModel, dialogMode: dialogMode, dialogRef: data.dialogRef } }\"></ng-container>\n</ng-template>\n\n<!-- Floating card more-actions menu -->\n@if (viewMode === 'card' && cardOpenMenuIndex !== null && moreMenuActions.length > 0) {\n <div class=\"osl-card-menu\"\n [style.top.px]=\"cardMenuPosition.top\"\n [style.left.px]=\"cardMenuPosition.left\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"osl-card-menu-header\">Actions</div>\n @for (action of moreMenuActions; track $index) {\n @if (!action.hideIf || !action.hideIf(cardPagedData[cardOpenMenuIndex])) {\n <button class=\"osl-card-menu-item\"\n (click)=\"action.click(cardPagedData[cardOpenMenuIndex]); cardOpenMenuIndex = null\">\n <span class=\"osl-card-menu-dot\"></span>\n {{ action.labelIf ? action.labelIf(cardPagedData[cardOpenMenuIndex]) : action.label }}\n </button>\n }\n }\n </div>\n}\n", styles: [".osl-setup-header{display:flex;align-items:center;justify-content:space-between}.osl-setup-dialog-footer{display:flex;align-items:center;justify-content:flex-end;gap:10px;width:100%}.dialog-cancel-btn{display:flex;align-items:center;gap:6px;height:38px;font-size:13px;font-weight:500;border-color:var(--osl-border-color, #d1d5db);color:var(--osl-secondary, #6b7280);border-radius:var(--osl-border-radius, 4px);padding:0 16px;transition:all .2s ease}.dialog-cancel-btn:hover{border-color:#9ca3af;background:#f9fafb;color:#374151}.dialog-save-btn{display:flex;align-items:center;gap:6px;height:38px;font-size:13px;font-weight:500;background:linear-gradient(135deg,var(--osl-primary, #2563eb),#3b82f6);color:#fff;border-radius:var(--osl-border-radius, 4px);padding:0 18px;transition:all .2s ease;box-shadow:0 2px 8px #2563eb40}.dialog-save-btn:hover{background:linear-gradient(135deg,var(--osl-primary-hover, #1d4ed8),#2563eb);box-shadow:0 4px 14px #2563eb66;transform:translateY(-1px)}.dialog-save-btn:active{transform:translateY(0);box-shadow:0 2px 8px #2563eb40}.osl-view-toggle{display:flex;border:1.5px solid var(--osl-border-color, #e5e7eb);border-radius:8px;overflow:hidden;flex-shrink:0;background:#fff}.osl-view-toggle-btn{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border:none;background:transparent;cursor:pointer;color:#9ca3af;transition:background .15s,color .15s;padding:0}.osl-view-toggle-btn svg{width:16px;height:16px;transition:stroke .15s}.osl-view-toggle-btn:first-child{border-right:1.5px solid var(--osl-border-color, #e5e7eb)}.osl-view-toggle-btn--active{background:var(--osl-primary, #6366f1);color:#fff}.osl-view-toggle-btn--active svg{stroke:#fff}.osl-view-toggle-btn:not(.osl-view-toggle-btn--active):hover{background:#f3f4f6;color:#374151}.osl-card-grid{display:flexbox;width:100%;overflow:auto}@keyframes osl-card-enter{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.osl-card-grid .osl-card:nth-child(1){animation-delay:0s}.osl-card-grid .osl-card:nth-child(2){animation-delay:.03s}.osl-card-grid .osl-card:nth-child(3){animation-delay:.06s}.osl-card-grid .osl-card:nth-child(4){animation-delay:.09s}.osl-card-grid .osl-card:nth-child(5){animation-delay:.12s}.osl-card-grid .osl-card:nth-child(6){animation-delay:.15s}.osl-card-grid .osl-card:nth-child(7){animation-delay:.18s}.osl-card-grid .osl-card:nth-child(8){animation-delay:.21s}.osl-card{position:relative;background:#fff;border-radius:12px;border:1px solid #eaecf0;box-shadow:0 1px 3px #1018280f,0 1px 2px #1018280a;overflow:hidden;transition:transform .2s cubic-bezier(.34,1.56,.64,1),box-shadow .2s ease,border-color .2s ease;animation:osl-card-enter .26s ease both}.osl-card:hover{transform:translateY(-2px);box-shadow:0 8px 24px #1018281a,0 2px 6px #1018280f}.osl-card--selectable{cursor:pointer}.osl-card--highlighted{border-color:var(--card-accent, #6366f1);box-shadow:0 0 0 3px color-mix(in srgb,var(--card-accent, #6366f1) 20%,transparent),0 4px 16px #1018281a}.osl-card--skeleton{pointer-events:none;animation:osl-card-enter .26s ease both}.osl-card--skeleton:hover{transform:none;box-shadow:0 1px 3px #1018280f}@keyframes osl-card-highlight-pulse{0%{box-shadow:0 0 0 4px color-mix(in srgb,var(--card-accent, #6366f1) 30%,transparent)}to{box-shadow:none}}.osl-card-header{position:relative;overflow:hidden;display:flex;align-items:center;gap:10px;padding:9px 12px 9px 14px;border-bottom:1px solid rgba(0,0,0,.05);min-height:42px}.osl-card-header--skeleton{background:#f9fafb}.osl-card-watermark{position:absolute;right:8px;top:50%;transform:translateY(-50%);width:72px;height:40px;color:var(--card-accent, var(--osl-primary, #6366f1));opacity:.1;pointer-events:none;flex-shrink:0}.osl-card-title-wrap{flex:1;min-width:0;position:relative;z-index:1}.osl-card-title{display:block;font-weight:700;font-size:13px;color:#1e293b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.osl-card-actions{display:flex;align-items:center;gap:4px;flex-shrink:0;position:relative;z-index:1}.osl-card-action-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:7px;border:1.5px solid rgba(255,255,255,.9);background:#fffc;cursor:pointer;transition:background .15s,border-color .15s,transform .15s,box-shadow .15s;padding:0}.osl-card-action-btn svg{width:13px;height:13px;transition:stroke .15s,fill .15s}.osl-card-action-btn--edit svg{stroke:#6b7280}.osl-card-action-btn--edit:hover{background:#fff;border-color:var(--card-accent, #6366f1);box-shadow:0 2px 8px #0000001f;transform:scale(1.1)}.osl-card-action-btn--edit:hover svg{stroke:var(--card-accent, #6366f1)}.osl-card-action-btn--delete svg{stroke:#9ca3af}.osl-card-action-btn--delete:hover{background:#fff1f2;border-color:#f43f5e;box-shadow:0 2px 8px #f43f5e33;transform:scale(1.1)}.osl-card-action-btn--delete:hover svg{stroke:#f43f5e}.osl-card-action-btn--more svg{fill:#9ca3af;stroke:none}.osl-card-action-btn--more:hover{background:#fff;border-color:var(--card-accent, #6366f1);box-shadow:0 2px 8px #0000001f;transform:scale(1.1)}.osl-card-action-btn--more:hover svg{fill:var(--card-accent, #6366f1)}.osl-card-body{padding:8px 12px 10px 14px}.osl-card-field{padding:4px 8px 2px 0;min-width:0;overflow:hidden}.osl-card-label{display:block;font-size:9.5px;font-weight:600;color:#9ca3af;text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-bottom:1px}.osl-card-value{display:block;font-size:12.5px;font-weight:500;color:#374151;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.osl-card-value.link{text-decoration:underline;color:#2563eb;cursor:pointer}.osl-card-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:320px;gap:12px;padding:40px 20px}.osl-card-empty-svg{width:180px;height:auto;opacity:.85}.osl-card-empty-title{font-size:16px;font-weight:600;color:#374151;margin:4px 0 0;text-align:center}.osl-card-empty-desc{font-size:13px;color:#9ca3af;margin:0;text-align:center}@keyframes osl-sk-pulse{0%,to{opacity:1}50%{opacity:.4}}.osl-sk-line{height:12px;background:#e9eaec;border-radius:6px;animation:osl-sk-pulse 1.4s ease-in-out infinite;width:80%}.osl-sk-line--title{height:13px;width:60%}.osl-sk-line--label{width:40%;height:9px;margin-bottom:4px}.osl-sk-line--short{width:50%}.osl-card-menu{position:fixed;z-index:9999;min-width:196px;background:#fff;border:1px solid #eaecf0;border-radius:12px;box-shadow:0 12px 32px #10182824,0 4px 12px #1018280f;overflow:hidden;animation:osl-menu-pop .16s cubic-bezier(.16,1,.3,1)}@keyframes osl-menu-pop{0%{opacity:0;transform:translateY(-8px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.osl-card-menu-header{padding:10px 14px 7px;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#9ca3af;border-bottom:1px solid #f2f4f7;-webkit-user-select:none;user-select:none}.osl-card-menu-item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 14px;background:transparent;border:none;border-left:3px solid transparent;border-bottom:1px solid #f9fafb;text-align:left;font-size:13px;font-weight:500;font-family:inherit;color:#374151;cursor:pointer;white-space:nowrap;transition:background .12s,color .12s,border-left-color .12s}.osl-card-menu-item:last-child{border-bottom:none}.osl-card-menu-item:hover{background:var(--card-accent-bg, #f5f3ff);color:var(--card-accent, var(--osl-primary, #6366f1));border-left-color:var(--card-accent, var(--osl-primary, #6366f1))}.osl-card-menu-item:hover .osl-card-menu-dot{background:var(--card-accent, var(--osl-primary, #6366f1));box-shadow:0 0 0 3px #6366f129}.osl-card-menu-dot{flex-shrink:0;width:6px;height:6px;border-radius:50%;background:#d1d5db;transition:background .12s,box-shadow .12s}.osl-grid-pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:10px;background:#f9fafb;flex-wrap:wrap;margin-top:4px}.osl-grid-pagination__info{font-size:12px;color:#6b7280;white-space:nowrap;min-width:120px}.osl-grid-pagination__controls{display:flex;align-items:center;gap:3px;flex-wrap:nowrap}.osl-grid-pagination__size{display:flex;align-items:center;min-width:120px;justify-content:flex-end}.osl-grid-page-btn{display:inline-flex;align-items:center;justify-content:center;height:30px;padding:0 10px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:13px;font-family:inherit;cursor:pointer;transition:background .12s,border-color .12s,color .12s;white-space:nowrap}.osl-grid-page-btn:hover:not(:disabled){background:#f3f4f6;border-color:#9ca3af}.osl-grid-page-btn:disabled{opacity:.35;cursor:not-allowed}.osl-grid-page-btn--nav{font-size:12px;color:#6b7280}.osl-grid-page-btn--num{min-width:30px;padding:0;font-size:13px}.osl-grid-page-btn--active{background:var(--osl-primary, #6366f1);border-color:var(--osl-primary, #6366f1);color:#fff;font-weight:600}.osl-grid-page-btn--active:hover:not(:disabled){background:var(--osl-primary-hover, #4f46e5);border-color:var(--osl-primary-hover, #4f46e5)}.osl-grid-page-ellipsis{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;color:#9ca3af;font-size:13px;pointer-events:none}.osl-grid-page-size{height:30px;padding:0 8px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:12px;font-family:inherit;cursor:pointer;outline:none}.osl-grid-page-size:focus{border-color:var(--osl-primary, #6366f1)}\n"] }]
|
|
3992
|
+
args: [{ selector: 'osl-setup', standalone: false, template: "<div class=\"p-2\">\n\n <!-- \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"osl-setup-header\">\n <div class=\"d-flex align-items-center\">\n @if(titleIcon){\n\n <span class=\"title-icon mx-2 mb-1\" [innerHTML]=\"renderIcon(titleIcon)\"></span>\n }\n <h5 class=\"mb-0\">{{ title }}</h5>\n\n </div>\n <div class=\"d-flex align-items-center gap-2\">\n <osl-searchbar #searchbar class=\"mx-2\" (onSearch)=\"onSearchSetup($event)\"></osl-searchbar>\n\n <!-- View Toggle -->\n @if(!isLister){\n <div class=\"osl-view-toggle\">\n <button class=\"osl-view-toggle-btn\" [class.osl-view-toggle-btn--active]=\"viewMode === 'table'\"\n (click)=\"toggleView('table')\" title=\"Table view\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/>\n <line x1=\"3\" y1=\"15\" x2=\"21\" y2=\"15\"/>\n <line x1=\"9\" y1=\"9\" x2=\"9\" y2=\"21\"/>\n </svg>\n </button>\n <button class=\"osl-view-toggle-btn\" [class.osl-view-toggle-btn--active]=\"viewMode === 'card'\"\n (click)=\"toggleView('card')\" title=\"Card view\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\"/>\n </svg>\n </button>\n </div>\n }\n\n @if (!isLister && canAdd) {\n <osl-button variant=\"secondary\" size=\"sm\" [label]=\"'Add ' + title\" (clickEv)=\"openAddDialog()\"></osl-button>\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Table View \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (viewMode === 'table') {\n <div class=\"osl-setup-body my-2\">\n <osl-grid\n #gridRef\n [columns]=\"columnsWithActions\"\n [(datasource)]=\"datasource\"\n [isPaginated]=\"isPaginated\"\n [pageSize]=\"pageSize\"\n [autoMode]=\"autoMode\"\n [tableHeight]=\"tableHeight\"\n [totalRecords]=\"totalRecords\"\n [loading]=\"loading\"\n [moreMenuActions]=\"moreMenuActions\"\n [canEdit]=\"canEdit\"\n [canDelete]=\"canDelete\"\n [highlightedRow]=\"restoredRow\"\n [primaryKey]=\"primaryKey\"\n (editClick)=\"openEditDialog($event)\"\n (deleteClick)=\"onDeleteClick($event)\"\n (pageChange)=\"onPageChange(pageChange, $event)\"\n (pageSizeChange)=\"onPageChange(pageSizeChange, $event)\"\n (sortChange)=\"sortChange.emit($event)\"\n [isSelectable]=\"isLister\"\n (onRowClick)=\"onRowClick.emit($event)\"\n />\n </div>\n }\n\n <!-- \u2500\u2500 Card View \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (viewMode === 'card') {\n\n <!-- Skeleton on initial load -->\n @if (loading && cardPagedData.length === 0) {\n <div class=\"osl-card-grid my-2\">\n @for (sk of skeletonCardRows; track $index) {\n <div class=\"osl-card osl-card--skeleton\">\n <div class=\"osl-card-header osl-card-header--skeleton\">\n <div class=\"osl-card-title-wrap\">\n <div class=\"osl-sk-line osl-sk-line--title\"></div>\n </div>\n </div>\n <div class=\"osl-card-body\">\n <div class=\"row g-0\">\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line\"></div></div>\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line osl-sk-line--short\"></div></div>\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line\"></div></div>\n <div class=\"col-6 osl-card-field\"><div class=\"osl-sk-line osl-sk-line--label\"></div><div class=\"osl-sk-line osl-sk-line--short\"></div></div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Empty state -->\n @else if (cardPagedData.length === 0 && !loading) {\n <div class=\"osl-card-empty my-2\">\n <svg class=\"osl-card-empty-svg\" viewBox=\"0 0 180 160\" fill=\"none\">\n <ellipse cx=\"90\" cy=\"140\" rx=\"60\" ry=\"8\" fill=\"#f3f4f6\"/>\n <rect x=\"30\" y=\"30\" width=\"120\" height=\"96\" rx=\"10\" fill=\"#f9fafb\" stroke=\"#e5e7eb\" stroke-width=\"2\"/>\n <rect x=\"44\" y=\"48\" width=\"92\" height=\"10\" rx=\"5\" fill=\"#e5e7eb\"/>\n <rect x=\"44\" y=\"66\" width=\"72\" height=\"8\" rx=\"4\" fill=\"#f3f4f6\"/>\n <rect x=\"44\" y=\"82\" width=\"84\" height=\"8\" rx=\"4\" fill=\"#f3f4f6\"/>\n <rect x=\"44\" y=\"98\" width=\"56\" height=\"8\" rx=\"4\" fill=\"#f3f4f6\"/>\n <circle cx=\"140\" cy=\"108\" r=\"26\" fill=\"#fff\" stroke=\"#e5e7eb\" stroke-width=\"2\"/>\n <line x1=\"133\" y1=\"101\" x2=\"147\" y2=\"115\" stroke=\"#d1d5db\" stroke-width=\"2.5\" stroke-linecap=\"round\"/>\n <line x1=\"147\" y1=\"101\" x2=\"133\" y2=\"115\" stroke=\"#d1d5db\" stroke-width=\"2.5\" stroke-linecap=\"round\"/>\n </svg>\n <p class=\"osl-card-empty-title\">No records found</p>\n <p class=\"osl-card-empty-desc\">Try adjusting your search or filter criteria</p>\n </div>\n }\n\n <!-- Card grid -->\n @else {\n <div #cardGridRef class=\"osl-card-grid my-2\" [style.height]=\"tableHeight\">\n @for (row of cardPagedData; track row[primaryKey] ?? $index; let i = $index) {\n\n @if (cardTemplate) {\n <ng-container *ngTemplateOutlet=\"cardTemplate; context: { $implicit: row, index: i }\"></ng-container>\n } @else {\n <div class=\"osl-card my-2\"\n [class.osl-card--selectable]=\"isLister\"\n [class.osl-card--highlighted]=\"isHighlightedCard(row)\"\n (click)=\"isLister ? onRowClick.emit(row) : null\">\n\n <!-- Card Header -->\n <div class=\"osl-card-header\">\n <!-- <svg class=\"osl-card-watermark\" viewBox=\"0 0 120 64\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M10 42 L18 52 L102 52 L110 42 Z\"/>\n <rect x=\"20\" y=\"34\" width=\"80\" height=\"9\" rx=\"1\"/>\n <rect x=\"26\" y=\"20\" width=\"16\" height=\"15\" rx=\"1\"/>\n <rect x=\"46\" y=\"20\" width=\"16\" height=\"15\" rx=\"1\"/>\n <rect x=\"66\" y=\"20\" width=\"16\" height=\"15\" rx=\"1\"/>\n <rect x=\"85\" y=\"14\" width=\"15\" height=\"21\" rx=\"2\"/>\n <rect x=\"90.5\" y=\"7\" width=\"2\" height=\"9\" rx=\"1\"/>\n <path d=\"M4 57 Q18 53 32 57 Q46 61 60 57 Q74 53 88 57 Q102 61 116 57\" stroke=\"currentColor\" stroke-width=\"2\" fill=\"none\" stroke-linecap=\"round\"/>\n </svg> -->\n <div class=\"osl-card-title-wrap\">\n <span class=\"osl-card-title\"\n >\n @if (cardTitleColumn) {\n <span\n >\n @switch (cardTitleColumn.displayType) {\n @case ('date') { {{ row[cardTitleColumn.key] | date }} }\n @case ('datetime') { {{ row[cardTitleColumn.key] | date:'medium' }} }\n @case ('time') { {{ row[cardTitleColumn.key] | date:'shortTime' }} }\n @case ('customDateFormat') { {{ row[cardTitleColumn.key] | date:cardTitleColumn.customDateFormat }} }\n @default { {{ getCellValue(row, cardTitleColumn) }} }\n }\n </span>\n }\n </span>\n </div>\n @if (!isLister && ((hasForm && (canEdit || canDelete)) || (moreMenuActions.length > 0 && hasVisibleActions(row)))) {\n <div class=\"osl-card-actions\">\n @if (hasForm && canEdit) {\n <button class=\"osl-card-action-btn osl-card-action-btn--edit\"\n (click)=\"$event.stopPropagation(); openEditDialog(row)\"\n [oslTooltip]=\"'Edit'\" oslTooltipPosition=\"bottom\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"/>\n <path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\"/>\n </svg>\n </button>\n }\n @if (hasForm && canDelete) {\n <button class=\"osl-card-action-btn osl-card-action-btn--delete\"\n (click)=\"$event.stopPropagation(); onDeleteClick(row)\"\n [oslTooltip]=\"'Delete'\" oslTooltipPosition=\"bottom\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" xmlns=\"http://www.w3.org/2000/svg\">\n <polyline points=\"3 6 5 6 21 6\"/>\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"/>\n <line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"/><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"/>\n </svg>\n </button>\n }\n @if (moreMenuActions.length > 0 && hasVisibleActions(row)) {\n <button class=\"osl-card-action-btn osl-card-action-btn--more\"\n (click)=\"toggleCardMenu(i, $event)\"\n [oslTooltip]=\"'More actions'\" oslTooltipPosition=\"bottom\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"12\" cy=\"5\" r=\"2.2\"/>\n <circle cx=\"12\" cy=\"12\" r=\"2.2\"/>\n <circle cx=\"12\" cy=\"19\" r=\"2.2\"/>\n </svg>\n </button>\n }\n </div>\n }\n </div>\n\n <!-- Card Body -->\n @if (cardBodyColumns.length > 0) {\n <div class=\"osl-card-body\">\n <div class=\"row g-0\">\n @for (col of cardBodyColumns; track col.key) {\n <div [class]=\"'col-' + cardCol + ' osl-card-field'\">\n <span class=\"osl-card-label\">{{ col.label }}</span>\n <span class=\"osl-card-value\" [class.link]=\"col.displayType === 'link'\" (click)=\"col.click ? col.click(row, col) : null\">\n\n <span>\n @switch (col.displayType) {\n @case ('date') { {{ row[col.key] | date }} }\n @case ('datetime') { {{ row[col.key] | date:'medium' }} }\n @case ('time') { {{ row[col.key] | date:'shortTime' }} }\n @case ('customDateFormat') { {{ row[col.key] | date:col.customDateFormat }} }\n @default { {{ getCellValue(row, col) }} }\n }\n </span>\n </span>\n </div>\n }\n </div>\n </div>\n }\n\n </div>\n }\n }\n </div>\n }\n\n <!-- Card Paginator -->\n @if (isPaginated && _cardTotal > 0) {\n <div class=\"osl-grid-pagination\">\n <span class=\"osl-grid-pagination__info\">\n @if (loading) {\n Loading...\n } @else {\n {{ cardStartRecord }}\u2013{{ cardEndRecord }} of {{ _cardTotal }} records\n }\n </span>\n\n <div class=\"osl-grid-pagination__controls\">\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(1)\"\n [disabled]=\"cardCurrentPage === 1 || loading\" title=\"First page\">\u00AB</button>\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(cardCurrentPage - 1)\"\n [disabled]=\"cardCurrentPage === 1 || loading\" title=\"Previous page\">\u2039 Prev</button>\n\n @for (page of cardPageNumbers; track $index) {\n @if (page === -1) {\n <span class=\"osl-grid-page-ellipsis\">\u2026</span>\n } @else {\n <button class=\"osl-grid-page-btn osl-grid-page-btn--num\"\n [class.osl-grid-page-btn--active]=\"page === cardCurrentPage\"\n [disabled]=\"loading\"\n (click)=\"cardGoToPage(page)\">{{ page }}</button>\n }\n }\n\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(cardCurrentPage + 1)\"\n [disabled]=\"cardCurrentPage === cardTotalPages || loading\" title=\"Next page\">Next \u203A</button>\n <button class=\"osl-grid-page-btn osl-grid-page-btn--nav\"\n (click)=\"cardGoToPage(cardTotalPages)\"\n [disabled]=\"cardCurrentPage === cardTotalPages || loading\" title=\"Last page\">\u00BB</button>\n </div>\n\n <div class=\"osl-grid-pagination__size\">\n <select class=\"osl-grid-page-size\"\n [ngModel]=\"_effectiveCardPageSize\"\n (ngModelChange)=\"cardOnPageSizeChange($event)\"\n [disabled]=\"loading\">\n @for (opt of cardPageSizeOptions; track opt) {\n <option [value]=\"opt\">{{ opt }} per page</option>\n }\n </select>\n </div>\n </div>\n }\n }\n\n</div>\n\n<!-- Dialog: Form Body -->\n<ng-template #formBodyTpl>\n <osl-dynamic-form\n [skeletonLoading]=\"formLoading\"\n [elements]=\"formElements\"\n [(model)]=\"dialogModel\"\n ></osl-dynamic-form>\n</ng-template>\n\n<!-- Dialog: Form Footer -->\n<ng-template #formFooterTpl>\n <div class=\"osl-setup-dialog-footer\">\n <osl-button [loading]=\"saveLoading\" variant=\"secondary\" label=\"Save\" (click)=\"saveDialog()\"></osl-button>\n </div>\n</ng-template>\n\n<ng-template #customFooterWrapperTpl let-data>\n <ng-container *ngTemplateOutlet=\"customFormFooter!; context: { $implicit: { dialogModel: dialogModel, dialogMode: dialogMode, dialogRef: data.dialogRef } }\"></ng-container>\n</ng-template>\n\n<!-- Floating card more-actions menu -->\n@if (viewMode === 'card' && cardOpenMenuIndex !== null && moreMenuActions.length > 0) {\n <div class=\"osl-card-menu\"\n [style.top.px]=\"cardMenuPosition.top\"\n [style.left.px]=\"cardMenuPosition.left\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"osl-card-menu-header\">Actions</div>\n @for (action of moreMenuActions; track $index) {\n @if (!action.hideIf || !action.hideIf(cardPagedData[cardOpenMenuIndex])) {\n <button class=\"osl-card-menu-item\"\n (click)=\"action.click(cardPagedData[cardOpenMenuIndex]); cardOpenMenuIndex = null\">\n <span class=\"osl-card-menu-dot\"></span>\n {{ action.labelIf ? action.labelIf(cardPagedData[cardOpenMenuIndex]) : action.label }}\n </button>\n }\n }\n </div>\n}\n", styles: [".osl-setup-header{display:flex;align-items:center;justify-content:space-between}.osl-setup-dialog-footer{display:flex;align-items:center;justify-content:flex-end;gap:10px;width:100%}.dialog-cancel-btn{display:flex;align-items:center;gap:6px;height:38px;font-size:13px;font-weight:500;border-color:var(--osl-border-color, #d1d5db);color:var(--osl-secondary, #6b7280);border-radius:var(--osl-border-radius, 4px);padding:0 16px;transition:all .2s ease}.dialog-cancel-btn:hover{border-color:#9ca3af;background:#f9fafb;color:#374151}.dialog-save-btn{display:flex;align-items:center;gap:6px;height:38px;font-size:13px;font-weight:500;background:linear-gradient(135deg,var(--osl-primary, #2563eb),#3b82f6);color:#fff;border-radius:var(--osl-border-radius, 4px);padding:0 18px;transition:all .2s ease;box-shadow:0 2px 8px #2563eb40}.dialog-save-btn:hover{background:linear-gradient(135deg,var(--osl-primary-hover, #1d4ed8),#2563eb);box-shadow:0 4px 14px #2563eb66;transform:translateY(-1px)}.dialog-save-btn:active{transform:translateY(0);box-shadow:0 2px 8px #2563eb40}.osl-view-toggle{display:flex;border:1.5px solid var(--osl-border-color, #e5e7eb);border-radius:8px;overflow:hidden;flex-shrink:0;background:#fff}.osl-view-toggle-btn{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border:none;background:transparent;cursor:pointer;color:#9ca3af;transition:background .15s,color .15s;padding:0}.osl-view-toggle-btn svg{width:16px;height:16px;transition:stroke .15s}.osl-view-toggle-btn:first-child{border-right:1.5px solid var(--osl-border-color, #e5e7eb)}.osl-view-toggle-btn--active{background:var(--osl-primary, #6366f1);color:#fff}.osl-view-toggle-btn--active svg{stroke:#fff}.osl-view-toggle-btn:not(.osl-view-toggle-btn--active):hover{background:#f3f4f6;color:#374151}.osl-card-grid{display:flexbox;width:100%;overflow:auto}@keyframes osl-card-enter{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.osl-card-grid .osl-card:nth-child(1){animation-delay:0s}.osl-card-grid .osl-card:nth-child(2){animation-delay:.03s}.osl-card-grid .osl-card:nth-child(3){animation-delay:.06s}.osl-card-grid .osl-card:nth-child(4){animation-delay:.09s}.osl-card-grid .osl-card:nth-child(5){animation-delay:.12s}.osl-card-grid .osl-card:nth-child(6){animation-delay:.15s}.osl-card-grid .osl-card:nth-child(7){animation-delay:.18s}.osl-card-grid .osl-card:nth-child(8){animation-delay:.21s}.osl-card{position:relative;background:#fff;border-radius:12px;border:1px solid #eaecf0;box-shadow:0 1px 3px #1018280f,0 1px 2px #1018280a;overflow:hidden;transition:transform .2s cubic-bezier(.34,1.56,.64,1),box-shadow .2s ease,border-color .2s ease;animation:osl-card-enter .26s ease both}.osl-card:hover{transform:translateY(-2px);box-shadow:0 8px 24px #1018281a,0 2px 6px #1018280f}.osl-card--selectable{cursor:pointer}.osl-card--highlighted{border-color:var(--card-accent, #6366f1);box-shadow:0 0 0 3px color-mix(in srgb,var(--card-accent, #6366f1) 20%,transparent),0 4px 16px #1018281a}.osl-card--skeleton{pointer-events:none;animation:osl-card-enter .26s ease both}.osl-card--skeleton:hover{transform:none;box-shadow:0 1px 3px #1018280f}@keyframes osl-card-highlight-pulse{0%{box-shadow:0 0 0 4px color-mix(in srgb,var(--card-accent, #6366f1) 30%,transparent)}to{box-shadow:none}}.osl-card-header{position:relative;overflow:hidden;display:flex;align-items:center;gap:10px;padding:9px 12px 9px 14px;border-bottom:1px solid rgba(0,0,0,.05);min-height:42px}.osl-card-header--skeleton{background:#f9fafb}.osl-card-watermark{position:absolute;right:8px;top:50%;transform:translateY(-50%);width:72px;height:40px;color:var(--card-accent, var(--osl-primary, #6366f1));opacity:.1;pointer-events:none;flex-shrink:0}.osl-card-title-wrap{flex:1;min-width:0;position:relative;z-index:1}.osl-card-title{display:block;font-weight:700;font-size:13px;color:#1e293b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.osl-card-actions{display:flex;align-items:center;gap:4px;flex-shrink:0;position:relative;z-index:1}.osl-card-action-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:7px;border:1.5px solid rgba(255,255,255,.9);background:#fffc;cursor:pointer;transition:background .15s,border-color .15s,transform .15s,box-shadow .15s;padding:0}.osl-card-action-btn svg{width:13px;height:13px;transition:stroke .15s,fill .15s}.osl-card-action-btn--edit svg{stroke:#6b7280}.osl-card-action-btn--edit:hover{background:#fff;border-color:var(--card-accent, #6366f1);box-shadow:0 2px 8px #0000001f;transform:scale(1.1)}.osl-card-action-btn--edit:hover svg{stroke:var(--card-accent, #6366f1)}.osl-card-action-btn--delete svg{stroke:#9ca3af}.osl-card-action-btn--delete:hover{background:#fff1f2;border-color:#f43f5e;box-shadow:0 2px 8px #f43f5e33;transform:scale(1.1)}.osl-card-action-btn--delete:hover svg{stroke:#f43f5e}.osl-card-action-btn--more svg{fill:#9ca3af;stroke:none}.osl-card-action-btn--more:hover{background:#fff;border-color:var(--card-accent, #6366f1);box-shadow:0 2px 8px #0000001f;transform:scale(1.1)}.osl-card-action-btn--more:hover svg{fill:var(--card-accent, #6366f1)}.osl-card-body{padding:8px 12px 10px 14px}.osl-card-field{padding:4px 8px 2px 0;min-width:0;overflow:hidden}.osl-card-label{display:block;font-size:9.5px;font-weight:600;color:#9ca3af;text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-bottom:1px}.osl-card-value{display:block;font-size:12.5px;font-weight:500;color:#374151;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.osl-card-value.link{text-decoration:underline;color:#2563eb;cursor:pointer}.osl-card-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:320px;gap:12px;padding:40px 20px}.osl-card-empty-svg{width:180px;height:auto;opacity:.85}.osl-card-empty-title{font-size:16px;font-weight:600;color:#374151;margin:4px 0 0;text-align:center}.osl-card-empty-desc{font-size:13px;color:#9ca3af;margin:0;text-align:center}@keyframes osl-sk-pulse{0%,to{opacity:1}50%{opacity:.4}}.osl-sk-line{height:12px;background:#e9eaec;border-radius:6px;animation:osl-sk-pulse 1.4s ease-in-out infinite;width:80%}.osl-sk-line--title{height:13px;width:60%}.osl-sk-line--label{width:40%;height:9px;margin-bottom:4px}.osl-sk-line--short{width:50%}.osl-card-menu{position:fixed;z-index:9999;min-width:196px;background:#fff;border:1px solid #eaecf0;border-radius:12px;box-shadow:0 12px 32px #10182824,0 4px 12px #1018280f;overflow:hidden;animation:osl-menu-pop .16s cubic-bezier(.16,1,.3,1)}@keyframes osl-menu-pop{0%{opacity:0;transform:translateY(-8px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.osl-card-menu-header{padding:10px 14px 7px;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#9ca3af;border-bottom:1px solid #f2f4f7;-webkit-user-select:none;user-select:none}.osl-card-menu-item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 14px;background:transparent;border:none;border-left:3px solid transparent;border-bottom:1px solid #f9fafb;text-align:left;font-size:13px;font-weight:500;font-family:inherit;color:#374151;cursor:pointer;white-space:nowrap;transition:background .12s,color .12s,border-left-color .12s}.osl-card-menu-item:last-child{border-bottom:none}.osl-card-menu-item:hover{background:var(--card-accent-bg, #f5f3ff);color:var(--card-accent, var(--osl-primary, #6366f1));border-left-color:var(--card-accent, var(--osl-primary, #6366f1))}.osl-card-menu-item:hover .osl-card-menu-dot{background:var(--card-accent, var(--osl-primary, #6366f1));box-shadow:0 0 0 3px #6366f129}.osl-card-menu-dot{flex-shrink:0;width:6px;height:6px;border-radius:50%;background:#d1d5db;transition:background .12s,box-shadow .12s}.osl-grid-pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:10px;background:#f9fafb;flex-wrap:wrap;margin-top:4px}.osl-grid-pagination__info{font-size:12px;color:#6b7280;white-space:nowrap;min-width:120px}.osl-grid-pagination__controls{display:flex;align-items:center;gap:3px;flex-wrap:nowrap}.osl-grid-pagination__size{display:flex;align-items:center;min-width:120px;justify-content:flex-end}.osl-grid-page-btn{display:inline-flex;align-items:center;justify-content:center;height:30px;padding:0 10px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:13px;font-family:inherit;cursor:pointer;transition:background .12s,border-color .12s,color .12s;white-space:nowrap}.osl-grid-page-btn:hover:not(:disabled){background:#f3f4f6;border-color:#9ca3af}.osl-grid-page-btn:disabled{opacity:.35;cursor:not-allowed}.osl-grid-page-btn--nav{font-size:12px;color:#6b7280}.osl-grid-page-btn--num{min-width:30px;padding:0;font-size:13px}.osl-grid-page-btn--active{background:var(--osl-primary, #6366f1);border-color:var(--osl-primary, #6366f1);color:#fff;font-weight:600}.osl-grid-page-btn--active:hover:not(:disabled){background:var(--osl-primary-hover, #4f46e5);border-color:var(--osl-primary-hover, #4f46e5)}.osl-grid-page-ellipsis{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;color:#9ca3af;font-size:13px;pointer-events:none}.osl-grid-page-size{height:30px;padding:0 8px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:12px;font-family:inherit;cursor:pointer;outline:none}.osl-grid-page-size:focus{border-color:var(--osl-primary, #6366f1)}.title-icon{width:20px}\n"] }]
|
|
3987
3993
|
}], propDecorators: { formBodyTpl: [{
|
|
3988
3994
|
type: ViewChild,
|
|
3989
3995
|
args: ['formBodyTpl']
|
|
@@ -4005,6 +4011,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
4005
4011
|
}], title: [{
|
|
4006
4012
|
type: Input,
|
|
4007
4013
|
args: ['title']
|
|
4014
|
+
}], titleIcon: [{
|
|
4015
|
+
type: Input,
|
|
4016
|
+
args: ['titleIcon']
|
|
4008
4017
|
}], columns: [{
|
|
4009
4018
|
type: Input,
|
|
4010
4019
|
args: ['columns']
|
|
@@ -4335,7 +4344,7 @@ class OslAutocompleteLister {
|
|
|
4335
4344
|
this.dialogRef.close(event);
|
|
4336
4345
|
}
|
|
4337
4346
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslAutocompleteLister, deps: [{ token: i1.MatDialogRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
4338
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: OslAutocompleteLister, isStandalone: false, selector: "osl-autocomplete-lister", inputs: { data: "data" }, viewQueries: [{ propertyName: "setup", first: true, predicate: ["setup"], descendants: true }], ngImport: i0, template: "<div class=\"p-4\">\r\n <osl-setup #setup [loading]=\"loader\" [autoMode]=\"false\" [title]=\"autocompleteData?.title\" (sortChange)=\"onSortChange($event)\" [totalRecords]=\"recordCount\" (pageSizeChange)=\"onPageChange($event)\" (pageChange)=\"onPageChange($event)\" (onRowClick)=\"onRowClick($event)\" [columns]=\"column\" [datasource]=\"datasource\" [isLister]=\"true\"></osl-setup>\r\n \r\n</div>", styles: [""], dependencies: [{ kind: "component", type: OslSetup, selector: "osl-setup", inputs: ["title", "columns", "datasource", "isPaginated", "pageSize", "autoMode", "tableHeight", "totalRecords", "loading", "dialogWidth", "formElements", "beforeDisplay", "onAddEditFn", "isLister", "canAdd", "canEdit", "canDelete", "moreMenuActions", "customFormFooter", "customHeaderTemp", "partialCustomHeaderTemp", "stateKey", "primaryKey", "onSave", "cardPageSize", "cardTemplate", "cardCol"], outputs: ["onSearch", "onAdd", "onEdit", "onDelete", "pageChange", "pageSizeChange", "sortChange", "onRowClick", "onStateRestored"] }] });
|
|
4347
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: OslAutocompleteLister, isStandalone: false, selector: "osl-autocomplete-lister", inputs: { data: "data" }, viewQueries: [{ propertyName: "setup", first: true, predicate: ["setup"], descendants: true }], ngImport: i0, template: "<div class=\"p-4\">\r\n <osl-setup #setup [loading]=\"loader\" [autoMode]=\"false\" [title]=\"autocompleteData?.title\" (sortChange)=\"onSortChange($event)\" [totalRecords]=\"recordCount\" (pageSizeChange)=\"onPageChange($event)\" (pageChange)=\"onPageChange($event)\" (onRowClick)=\"onRowClick($event)\" [columns]=\"column\" [datasource]=\"datasource\" [isLister]=\"true\"></osl-setup>\r\n \r\n</div>", styles: [""], dependencies: [{ kind: "component", type: OslSetup, selector: "osl-setup", inputs: ["title", "titleIcon", "columns", "datasource", "isPaginated", "pageSize", "autoMode", "tableHeight", "totalRecords", "loading", "dialogWidth", "formElements", "beforeDisplay", "onAddEditFn", "isLister", "canAdd", "canEdit", "canDelete", "moreMenuActions", "customFormFooter", "customHeaderTemp", "partialCustomHeaderTemp", "stateKey", "primaryKey", "onSave", "cardPageSize", "cardTemplate", "cardCol"], outputs: ["onSearch", "onAdd", "onEdit", "onDelete", "pageChange", "pageSizeChange", "sortChange", "onRowClick", "onStateRestored"] }] });
|
|
4339
4348
|
}
|
|
4340
4349
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslAutocompleteLister, decorators: [{
|
|
4341
4350
|
type: Component,
|