osl-base-extended 3.4.0 → 3.5.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.
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Component, Inject, inject, Injector,
|
|
2
|
+
import { Component, Inject, Injectable, inject, Injector, HostListener, InjectionToken, PLATFORM_ID, Input, Optional, Directive, EventEmitter, Output, ViewChild, NgModule, ChangeDetectorRef, ChangeDetectionStrategy, ViewEncapsulation } from '@angular/core';
|
|
3
3
|
import * as i1$2 from '@angular/common';
|
|
4
4
|
import { NgTemplateOutlet, NgComponentOutlet, DOCUMENT, isPlatformBrowser, DatePipe, DecimalPipe, NgStyle, NgClass, UpperCasePipe } from '@angular/common';
|
|
5
5
|
import * as i1 from '@angular/material/dialog';
|
|
6
6
|
import { MAT_DIALOG_DATA, MatDialogModule, MatDialog } from '@angular/material/dialog';
|
|
7
7
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
|
8
8
|
import { Router, ActivatedRoute } from '@angular/router';
|
|
9
|
-
import * as
|
|
9
|
+
import * as i2$1 from '@angular/material/button';
|
|
10
10
|
import { MatIconButton, MatButtonModule } from '@angular/material/button';
|
|
11
11
|
import * as i2 from '@angular/material/icon';
|
|
12
12
|
import { MatIconModule } from '@angular/material/icon';
|
|
13
13
|
import { HttpHeaders, HttpParams, HttpClient } from '@angular/common/http';
|
|
14
|
-
import { firstValueFrom, timeout, BehaviorSubject, debounceTime, distinctUntilChanged } from 'rxjs';
|
|
14
|
+
import { firstValueFrom, timeout, BehaviorSubject, debounceTime, distinctUntilChanged, from, of } from 'rxjs';
|
|
15
15
|
import * as i1$1 from '@angular/forms';
|
|
16
16
|
import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
17
|
-
import * as i2$
|
|
17
|
+
import * as i2$2 from '@angular/material/form-field';
|
|
18
18
|
import { MatFormFieldModule, MatHint } from '@angular/material/form-field';
|
|
19
19
|
import * as i6 from '@angular/material/tooltip';
|
|
20
20
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
21
21
|
import * as i5 from '@angular/material/datepicker';
|
|
22
22
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
23
|
+
import { MatInputModule } from '@angular/material/input';
|
|
23
24
|
import { MAT_DATE_FORMATS } from '@angular/material/core';
|
|
24
25
|
import * as i4 from '@ngxmc/datetime-picker';
|
|
25
26
|
import { NgxMatDatetimepicker, NgxMatDatepickerInput } from '@ngxmc/datetime-picker';
|
|
26
|
-
import {
|
|
27
|
-
import { debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1 } from 'rxjs/operators';
|
|
27
|
+
import { debounceTime as debounceTime$1, distinctUntilChanged as distinctUntilChanged$1, switchMap, tap } from 'rxjs/operators';
|
|
28
28
|
import { MatMenuModule } from '@angular/material/menu';
|
|
29
29
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
30
|
-
import * as i3
|
|
30
|
+
import * as i3 from '@angular/cdk/drag-drop';
|
|
31
31
|
import { moveItemInArray, DragDropModule } from '@angular/cdk/drag-drop';
|
|
32
32
|
import { TemplatePortal, PortalModule } from '@angular/cdk/portal';
|
|
33
33
|
import * as i1$3 from '@angular/cdk/overlay';
|
|
@@ -91,11 +91,11 @@ class DeleteConfirmation {
|
|
|
91
91
|
this.dialogRef.close(false);
|
|
92
92
|
}
|
|
93
93
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DeleteConfirmation, deps: [{ token: i1.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
|
|
94
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: DeleteConfirmation, isStandalone: true, selector: "delete-confirmation", ngImport: i0, template: "<div class=\"delete-dialog-container\">\
|
|
94
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.9", type: DeleteConfirmation, isStandalone: true, selector: "delete-confirmation", ngImport: i0, template: "<div class=\"delete-dialog-container\">\n\n <!-- Header: centered icon + top-right close -->\n <div class=\"delete-dialog-header\">\n <div class=\"delete-icon-wrapper\">\n <div class=\"delete-icon-ring\">\n <svg class=\"delete-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12 1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z\"/>\n </svg>\n </div>\n </div>\n <button mat-icon-button class=\"close-btn\" (click)=\"cancel()\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n <path d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\n </svg>\n </button>\n </div>\n\n <!-- Body -->\n <mat-dialog-content class=\"delete-dialog-content\">\n <h2 class=\"delete-title\">{{ data.title }}</h2>\n <p class=\"delete-message\">{{ data.message }}</p>\n </mat-dialog-content>\n\n <!-- Actions -->\n <mat-dialog-actions class=\"delete-dialog-actions\">\n <button mat-stroked-button class=\"cancel-btn\" (click)=\"cancel()\">\n <svg class=\"btn-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\n </svg>\n {{ data.cancelText }}\n </button>\n <button mat-flat-button class=\"confirm-btn\" (click)=\"confirm()\">\n <svg class=\"btn-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12 1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z\"/>\n </svg>\n {{ data.confirmText }}\n </button>\n </mat-dialog-actions>\n\n</div>\n", styles: [".delete-dialog-container{padding:8px 4px}.delete-dialog-header{display:flex;justify-content:space-between;align-items:flex-start;padding:8px 8px 0}.delete-icon-wrapper{display:flex;align-items:center;justify-content:center;flex:1;padding-left:40px}.delete-icon-ring{width:72px;height:72px;border-radius:50%;background:#dc262614;border:2px solid rgba(220,38,38,.2);display:flex;align-items:center;justify-content:center;animation:pulse-ring 2s ease-in-out infinite}.delete-icon{width:36px;height:36px;color:var(--osl-danger, #dc2626)}.close-btn{color:var(--osl-secondary, #6b7280);flex-shrink:0}.close-btn:hover{color:var(--osl-black, #000)}.delete-dialog-content{text-align:center;padding:16px 24px 8px!important;max-height:unset!important;overflow:visible!important}.delete-title{font-size:20px;font-weight:600;color:var(--osl-black, #111827);margin:0 0 10px;line-height:1.3}.delete-message{font-size:14px;color:var(--osl-secondary, #6b7280);margin:0;line-height:1.6}.delete-dialog-actions{display:flex!important;justify-content:center;gap:12px;padding:16px 24px 20px!important}.delete-dialog-actions .btn-icon{width:17px;height:17px;flex-shrink:0}.delete-dialog-actions .cancel-btn{border-color:var(--osl-border-color, #d1d5db);color:var(--osl-secondary, #6b7280);border-radius:var(--osl-border-radius, 4px);height:38px;font-size:14px;font-weight:500;display:flex;align-items:center;gap:4px;padding:0 18px;transition:all .2s ease}.delete-dialog-actions .cancel-btn:hover{border-color:var(--osl-secondary, #6b7280);background:#f9fafb}.delete-dialog-actions .confirm-btn{background:var(--osl-danger, #dc2626);color:var(--osl-danger-text, #ffffff);border-radius:var(--osl-border-radius, 4px);height:38px;font-size:14px;font-weight:500;display:flex;align-items:center;gap:4px;padding:0 18px;transition:all .2s ease}.delete-dialog-actions .confirm-btn .btn-icon{color:#fff}.delete-dialog-actions .confirm-btn:hover{background:var(--osl-danger-hover, #b91c1c);box-shadow:0 4px 12px #dc262659}@keyframes pulse-ring{0%,to{box-shadow:0 0 #dc262626}50%{box-shadow:0 0 0 8px #dc262600}}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }] });
|
|
95
95
|
}
|
|
96
96
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DeleteConfirmation, decorators: [{
|
|
97
97
|
type: Component,
|
|
98
|
-
args: [{ selector: 'delete-confirmation', imports: [MatDialogModule,
|
|
98
|
+
args: [{ selector: 'delete-confirmation', imports: [MatDialogModule, MatButtonModule], template: "<div class=\"delete-dialog-container\">\n\n <!-- Header: centered icon + top-right close -->\n <div class=\"delete-dialog-header\">\n <div class=\"delete-icon-wrapper\">\n <div class=\"delete-icon-ring\">\n <svg class=\"delete-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12 1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z\"/>\n </svg>\n </div>\n </div>\n <button mat-icon-button class=\"close-btn\" (click)=\"cancel()\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n <path d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\n </svg>\n </button>\n </div>\n\n <!-- Body -->\n <mat-dialog-content class=\"delete-dialog-content\">\n <h2 class=\"delete-title\">{{ data.title }}</h2>\n <p class=\"delete-message\">{{ data.message }}</p>\n </mat-dialog-content>\n\n <!-- Actions -->\n <mat-dialog-actions class=\"delete-dialog-actions\">\n <button mat-stroked-button class=\"cancel-btn\" (click)=\"cancel()\">\n <svg class=\"btn-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\n </svg>\n {{ data.cancelText }}\n </button>\n <button mat-flat-button class=\"confirm-btn\" (click)=\"confirm()\">\n <svg class=\"btn-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12 1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z\"/>\n </svg>\n {{ data.confirmText }}\n </button>\n </mat-dialog-actions>\n\n</div>\n", styles: [".delete-dialog-container{padding:8px 4px}.delete-dialog-header{display:flex;justify-content:space-between;align-items:flex-start;padding:8px 8px 0}.delete-icon-wrapper{display:flex;align-items:center;justify-content:center;flex:1;padding-left:40px}.delete-icon-ring{width:72px;height:72px;border-radius:50%;background:#dc262614;border:2px solid rgba(220,38,38,.2);display:flex;align-items:center;justify-content:center;animation:pulse-ring 2s ease-in-out infinite}.delete-icon{width:36px;height:36px;color:var(--osl-danger, #dc2626)}.close-btn{color:var(--osl-secondary, #6b7280);flex-shrink:0}.close-btn:hover{color:var(--osl-black, #000)}.delete-dialog-content{text-align:center;padding:16px 24px 8px!important;max-height:unset!important;overflow:visible!important}.delete-title{font-size:20px;font-weight:600;color:var(--osl-black, #111827);margin:0 0 10px;line-height:1.3}.delete-message{font-size:14px;color:var(--osl-secondary, #6b7280);margin:0;line-height:1.6}.delete-dialog-actions{display:flex!important;justify-content:center;gap:12px;padding:16px 24px 20px!important}.delete-dialog-actions .btn-icon{width:17px;height:17px;flex-shrink:0}.delete-dialog-actions .cancel-btn{border-color:var(--osl-border-color, #d1d5db);color:var(--osl-secondary, #6b7280);border-radius:var(--osl-border-radius, 4px);height:38px;font-size:14px;font-weight:500;display:flex;align-items:center;gap:4px;padding:0 18px;transition:all .2s ease}.delete-dialog-actions .cancel-btn:hover{border-color:var(--osl-secondary, #6b7280);background:#f9fafb}.delete-dialog-actions .confirm-btn{background:var(--osl-danger, #dc2626);color:var(--osl-danger-text, #ffffff);border-radius:var(--osl-border-radius, 4px);height:38px;font-size:14px;font-weight:500;display:flex;align-items:center;gap:4px;padding:0 18px;transition:all .2s ease}.delete-dialog-actions .confirm-btn .btn-icon{color:#fff}.delete-dialog-actions .confirm-btn:hover{background:var(--osl-danger-hover, #b91c1c);box-shadow:0 4px 12px #dc262659}@keyframes pulse-ring{0%,to{box-shadow:0 0 #dc262626}50%{box-shadow:0 0 0 8px #dc262600}}\n"] }]
|
|
99
99
|
}], ctorParameters: () => [{ type: i1.MatDialogRef }, { type: DeleteConfirmationData, decorators: [{
|
|
100
100
|
type: Inject,
|
|
101
101
|
args: [MAT_DIALOG_DATA]
|
|
@@ -108,6 +108,33 @@ class DeleteConfirmationData {
|
|
|
108
108
|
data;
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
class DirtyStateService {
|
|
112
|
+
_isDirty = false;
|
|
113
|
+
_saveCallback;
|
|
114
|
+
get isDirty() { return this._isDirty; }
|
|
115
|
+
get hasSaveCallback() { return !!this._saveCallback; }
|
|
116
|
+
markDirty(saveCallback) {
|
|
117
|
+
this._isDirty = true;
|
|
118
|
+
if (saveCallback)
|
|
119
|
+
this._saveCallback = saveCallback;
|
|
120
|
+
}
|
|
121
|
+
markClean() {
|
|
122
|
+
this._isDirty = false;
|
|
123
|
+
this._saveCallback = undefined;
|
|
124
|
+
}
|
|
125
|
+
async executeSave() {
|
|
126
|
+
if (this._saveCallback)
|
|
127
|
+
return this._saveCallback();
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DirtyStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
131
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DirtyStateService, providedIn: 'root' });
|
|
132
|
+
}
|
|
133
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DirtyStateService, decorators: [{
|
|
134
|
+
type: Injectable,
|
|
135
|
+
args: [{ providedIn: 'root' }]
|
|
136
|
+
}] });
|
|
137
|
+
|
|
111
138
|
class baseComponent {
|
|
112
139
|
_injector = inject(Injector);
|
|
113
140
|
showSuccess(message) {
|
|
@@ -233,6 +260,18 @@ class baseComponent {
|
|
|
233
260
|
}
|
|
234
261
|
}, 300);
|
|
235
262
|
}
|
|
263
|
+
markFormDirty(saveCallback) {
|
|
264
|
+
this._injector.get(DirtyStateService).markDirty(saveCallback);
|
|
265
|
+
}
|
|
266
|
+
markFormClean() {
|
|
267
|
+
this._injector.get(DirtyStateService).markClean();
|
|
268
|
+
}
|
|
269
|
+
onBeforeUnload(event) {
|
|
270
|
+
if (this._injector.get(DirtyStateService).isDirty) {
|
|
271
|
+
event.preventDefault();
|
|
272
|
+
event.returnValue = '';
|
|
273
|
+
}
|
|
274
|
+
}
|
|
236
275
|
_findFirstInvalidElement(formElements, model) {
|
|
237
276
|
for (const el of formElements) {
|
|
238
277
|
if (el.elementType === 'fieldset') {
|
|
@@ -261,7 +300,10 @@ class baseComponent {
|
|
|
261
300
|
}
|
|
262
301
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: baseComponent, decorators: [{
|
|
263
302
|
type: Injectable
|
|
264
|
-
}]
|
|
303
|
+
}], propDecorators: { onBeforeUnload: [{
|
|
304
|
+
type: HostListener,
|
|
305
|
+
args: ['window:beforeunload', ['$event']]
|
|
306
|
+
}] } });
|
|
265
307
|
|
|
266
308
|
class Httpbase {
|
|
267
309
|
controller;
|
|
@@ -1268,7 +1310,7 @@ class Oslinput {
|
|
|
1268
1310
|
return result;
|
|
1269
1311
|
}
|
|
1270
1312
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: Oslinput, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1271
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: Oslinput, isStandalone: false, selector: "osl-input", inputs: { label: "label", required: "required", disabled: "disabled", model: "model", type: "type", placeholder: "placeholder", mask: "mask", min: "min", max: "max", minLength: "minLength", maxLength: "maxLength", prefixIcon: "prefixIcon", suffixIcon: "suffixIcon", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme", onlyChars: "onlyChars", isCapitalize: "isCapitalize", decimalPortion: "decimalPortion" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"d-flex flex-column\">\r\n @if(label){\r\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <label class=\"label\" [class.txt-clr-red]=\"myField.touched && myField.invalid\">\r\n <span class=\"label-text\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required?'*':''}}</span>\r\n </label>\r\n </div>\r\n}\r\n\r\n <div class=\"input-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\" [class.plain]=\"!hasWrapper\" [class.error]=\"myField.touched && myField.invalid\" [class.input-disabled]=\"disabled\">\r\n @if(prefixIcon) {\r\n <mat-icon class=\"input-icon prefix-icon\">{{prefixIcon}}</mat-icon>\r\n }\r\n <input\r\n\r\n [type]=\"inputType\"\r\n [ngModel]=\"model\"\r\n (ngModelChange)=\"onModelChange($event)\"\r\n (keydown)=\"onKeyDown($event)\"\r\n (focus)=\"onFocusIn($event)\"\r\n (blur)=\"onFocusOut()\"\r\n (change)=\"onChange()\"\r\n [required]=\"required\"\r\n [disabled]=\"disabled\"\r\n [placeholder]=\"placeholder\"\r\n [attr.min]=\"min || null\"\r\n [attr.max]=\"max || null\"\r\n [attr.minlength]=\"minLength\"\r\n [attr.maxlength]=\"maxLength\"\r\n [email]=\"type === 'email'\"\r\n #myField=\"ngModel\"\r\n class=\"inner-input\"\r\n >\r\n @if(type === 'password') {\r\n <button type=\"button\" class=\"password-toggle\" (click)=\"togglePassword()\" tabindex=\"-1\">\r\n <mat-icon>{{showPassword ? 'visibility_off' : 'visibility'}}</mat-icon>\r\n </button>\r\n }\r\n @if(suffixIcon && type !== 'password') {\r\n <mat-icon class=\"input-icon suffix-icon\">{{suffixIcon}}</mat-icon>\r\n }\r\n </div>\r\n\r\n @if(myField.touched && myField.invalid) {\r\n @if(myField.errors?.['required']) {\r\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\r\n } @else if(myField.errors?.['email']) {\r\n <mat-hint class=\"hint\">Please enter a valid email address.</mat-hint>\r\n } @else if(myField.errors?.['minlength']) {\r\n <mat-hint class=\"hint\">Minimum {{minLength}} characters required.</mat-hint>\r\n } @else if(myField.errors?.['maxlength']) {\r\n <mat-hint class=\"hint\">Maximum {{maxLength}} characters allowed.</mat-hint>\r\n } @else if(myField.errors?.['min']) {\r\n <mat-hint class=\"hint\">Value must be at least {{min}}.</mat-hint>\r\n } @else if(myField.errors?.['max']) {\r\n <mat-hint class=\"hint\">Value must be at most {{max}}.</mat-hint>\r\n }\r\n }\r\n</div>\r\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px;display:flex;align-items:center;overflow:hidden}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}.input-wrapper{display:flex;align-items:center;height:var(--osl-control-height);width:100%;border-radius:var(--osl-border-radius);border:1px solid var(--osl-border-color);padding:0 8px;gap:6px;transition:border-color .5s}.input-wrapper:focus-within{border-color:var(--osl-focus-border-color)}.input-wrapper.error{border-color:var(--osl-error-color)}.input-wrapper.input-disabled{background:#f5f5f5;opacity:.7;cursor:not-allowed}.input-wrapper.plain{padding:0;border:none;height:auto;gap:0}.input-wrapper.plain .inner-input{border:1px solid var(--osl-border-color);border-radius:var(--osl-border-radius);padding:5px;height:var(--osl-control-height)}.input-wrapper.plain .inner-input:focus{border-color:var(--osl-focus-border-color)}.input-wrapper.plain .inner-input.error{border-color:var(--osl-error-color)}.inner-input{flex:1;min-width:0;height:100%;border:none;outline:none;font-size:var(--osl-text-font-size);background:transparent;width:100%}.inner-input:disabled{cursor:not-allowed;background:transparent}.inner-input::placeholder{font-size:var(--osl-label-font-size);color:#aaa}.input-icon{font-size:18px;width:18px;height:18px;color:#888;flex-shrink:0;-webkit-user-select:none;user-select:none}.password-toggle{display:flex;align-items:center;justify-content:center;background:none;border:none;padding:0;cursor:pointer;color:#888;flex-shrink:0}.password-toggle mat-icon{font-size:18px;width:18px;height:18px}.password-toggle:hover{color:#333}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.txt-clr-red{color:var(--osl-error-color)}\n"], dependencies: [{ kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$
|
|
1313
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: Oslinput, isStandalone: false, selector: "osl-input", inputs: { label: "label", required: "required", disabled: "disabled", model: "model", type: "type", placeholder: "placeholder", mask: "mask", min: "min", max: "max", minLength: "minLength", maxLength: "maxLength", prefixIcon: "prefixIcon", suffixIcon: "suffixIcon", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme", onlyChars: "onlyChars", isCapitalize: "isCapitalize", decimalPortion: "decimalPortion" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"d-flex flex-column\">\r\n @if(label){\r\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <label class=\"label\" [class.txt-clr-red]=\"myField.touched && myField.invalid\">\r\n <span class=\"label-text\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required?'*':''}}</span>\r\n </label>\r\n </div>\r\n}\r\n\r\n <div class=\"input-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\" [class.plain]=\"!hasWrapper\" [class.error]=\"myField.touched && myField.invalid\" [class.input-disabled]=\"disabled\">\r\n @if(prefixIcon) {\r\n <mat-icon class=\"input-icon prefix-icon\">{{prefixIcon}}</mat-icon>\r\n }\r\n <input\r\n\r\n [type]=\"inputType\"\r\n [ngModel]=\"model\"\r\n (ngModelChange)=\"onModelChange($event)\"\r\n (keydown)=\"onKeyDown($event)\"\r\n (focus)=\"onFocusIn($event)\"\r\n (blur)=\"onFocusOut()\"\r\n (change)=\"onChange()\"\r\n [required]=\"required\"\r\n [disabled]=\"disabled\"\r\n [placeholder]=\"placeholder\"\r\n [attr.min]=\"min || null\"\r\n [attr.max]=\"max || null\"\r\n [attr.minlength]=\"minLength\"\r\n [attr.maxlength]=\"maxLength\"\r\n [email]=\"type === 'email'\"\r\n #myField=\"ngModel\"\r\n class=\"inner-input\"\r\n >\r\n @if(type === 'password') {\r\n <button type=\"button\" class=\"password-toggle\" (click)=\"togglePassword()\" tabindex=\"-1\">\r\n <mat-icon>{{showPassword ? 'visibility_off' : 'visibility'}}</mat-icon>\r\n </button>\r\n }\r\n @if(suffixIcon && type !== 'password') {\r\n <mat-icon class=\"input-icon suffix-icon\">{{suffixIcon}}</mat-icon>\r\n }\r\n </div>\r\n\r\n @if(myField.touched && myField.invalid) {\r\n @if(myField.errors?.['required']) {\r\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\r\n } @else if(myField.errors?.['email']) {\r\n <mat-hint class=\"hint\">Please enter a valid email address.</mat-hint>\r\n } @else if(myField.errors?.['minlength']) {\r\n <mat-hint class=\"hint\">Minimum {{minLength}} characters required.</mat-hint>\r\n } @else if(myField.errors?.['maxlength']) {\r\n <mat-hint class=\"hint\">Maximum {{maxLength}} characters allowed.</mat-hint>\r\n } @else if(myField.errors?.['min']) {\r\n <mat-hint class=\"hint\">Value must be at least {{min}}.</mat-hint>\r\n } @else if(myField.errors?.['max']) {\r\n <mat-hint class=\"hint\">Value must be at most {{max}}.</mat-hint>\r\n }\r\n }\r\n</div>\r\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px;display:flex;align-items:center;overflow:hidden}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}.input-wrapper{display:flex;align-items:center;height:var(--osl-control-height);width:100%;border-radius:var(--osl-border-radius);border:1px solid var(--osl-border-color);padding:0 8px;gap:6px;transition:border-color .5s}.input-wrapper:focus-within{border-color:var(--osl-focus-border-color)}.input-wrapper.error{border-color:var(--osl-error-color)}.input-wrapper.input-disabled{background:#f5f5f5;opacity:.7;cursor:not-allowed}.input-wrapper.plain{padding:0;border:none;height:auto;gap:0}.input-wrapper.plain .inner-input{border:1px solid var(--osl-border-color);border-radius:var(--osl-border-radius);padding:5px;height:var(--osl-control-height)}.input-wrapper.plain .inner-input:focus{border-color:var(--osl-focus-border-color)}.input-wrapper.plain .inner-input.error{border-color:var(--osl-error-color)}.inner-input{flex:1;min-width:0;height:100%;border:none;outline:none;font-size:var(--osl-text-font-size);background:transparent;width:100%}.inner-input:disabled{cursor:not-allowed;background:transparent}.inner-input::placeholder{font-size:var(--osl-label-font-size);color:#aaa}.input-icon{font-size:18px;width:18px;height:18px;color:#888;flex-shrink:0;-webkit-user-select:none;user-select:none}.password-toggle{display:flex;align-items:center;justify-content:center;background:none;border:none;padding:0;cursor:pointer;color:#888;flex-shrink:0}.password-toggle mat-icon{font-size:18px;width:18px;height:18px}.password-toggle:hover{color:#333}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.txt-clr-red{color:var(--osl-error-color)}\n"], dependencies: [{ kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: OslSkeletonDirective, selector: "[oslSkeleton]", inputs: ["oslSkeleton", "oslSkeletonType", "oslSkeletonAnimation", "oslSkeletonTheme", "oslSkeletonColor", "oslSkeletonHighlight", "oslSkeletonRadius", "oslSkeletonRows", "oslSkeletonRowGap", "oslSkeletonZIndex", "oslSkeletonDelay", "oslSkeletonDuration", "oslSkeletonMinHeight", "oslSkeletonForceReread", "oslSkeletonCircleSize", "oslSkeletonListItems", "oslSkeletonTableRows", "oslSkeletonTableCols", "oslSkeletonCardLines", "oslSkeletonBgColor"] }] });
|
|
1272
1314
|
}
|
|
1273
1315
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: Oslinput, decorators: [{
|
|
1274
1316
|
type: Component,
|
|
@@ -1364,7 +1406,7 @@ class Osltextarea {
|
|
|
1364
1406
|
this.modelChange.emit(this.model);
|
|
1365
1407
|
}
|
|
1366
1408
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: Osltextarea, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1367
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: Osltextarea, isStandalone: false, selector: "osl-textarea", inputs: { label: "label", rows: "rows", required: "required", disabled: "disabled", model: "model", placeholder: "placeholder", maxLength: "maxLength", minLength: "minLength", characterCounter: "characterCounter", resize: "resize", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, ngImport: i0, template: "<div class=\"d-flex flex-column\">\r\n @if(label){\r\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <label class=\"label\" [class.txt-clr-red]=\"myField.touched && myField.invalid\">\r\n <span class=\"label-text\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required?'*':''}}</span>\r\n </label>\r\n \r\n </div>\r\n }\r\n \r\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <textarea\r\n [rows]=\"rows\"\r\n [ngModel]=\"model\"\r\n (ngModelChange)=\"onModelChange($event)\"\r\n (change)=\"changeEv.emit(model)\"\r\n [required]=\"required\"\r\n [disabled]=\"disabled\"\r\n [placeholder]=\"placeholder\"\r\n [attr.maxlength]=\"maxLength\"\r\n [attr.minlength]=\"minLength\"\r\n [style.resize]=\"resize\"\r\n #myField=\"ngModel\"\r\n [class.error]=\"myField.touched && myField.invalid\"\r\n ></textarea>\r\n </div>\r\n <div class=\"textarea-footer\">\r\n @if(myField.touched && myField.invalid) {\r\n @if(myField.errors?.['required']) {\r\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\r\n } @else if(myField.errors?.['minlength']) {\r\n <mat-hint class=\"hint\">Minimum {{minLength}} characters required.</mat-hint>\r\n } @else if(myField.errors?.['maxlength']) {\r\n <mat-hint class=\"hint\">Maximum {{maxLength}} characters allowed.</mat-hint>\r\n }\r\n } @else {\r\n <span></span>\r\n }\r\n @if(showCounter) {\r\n <span class=\"char-counter\" [class.counter-limit]=\"currentLength >= maxLength!\">\r\n {{currentLength}}/{{maxLength}}\r\n </span>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px;display:flex;align-items:center;overflow:hidden}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}textarea{width:100%;border-radius:var(--osl-border-radius);outline:none;padding:6px 8px;font-size:var(--osl-text-font-size);border:1px solid var(--osl-border-color);resize:none;font-family:inherit;transition:border-color .5s}textarea:focus{border-color:var(--osl-focus-border-color)}textarea:disabled{background:#f5f5f5;cursor:not-allowed;opacity:.7}textarea::placeholder{font-size:var(--osl-label-font-size);color:#aaa}textarea.error{border-color:var(--osl-error-color)}.textarea-footer{display:flex;justify-content:space-between;align-items:center;margin-top:2px}.hint{color:var(--osl-error-color);font-size:var(--osl-hint-font-size)}.char-counter{font-size:var(--osl-hint-font-size);color:#888;margin-left:auto}.char-counter.counter-limit{color:var(--osl-error-color);font-weight:500}.txt-clr-red{color:var(--osl-error-color)}\n"], dependencies: [{ kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$
|
|
1409
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: Osltextarea, isStandalone: false, selector: "osl-textarea", inputs: { label: "label", rows: "rows", required: "required", disabled: "disabled", model: "model", placeholder: "placeholder", maxLength: "maxLength", minLength: "minLength", characterCounter: "characterCounter", resize: "resize", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, ngImport: i0, template: "<div class=\"d-flex flex-column\">\r\n @if(label){\r\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <label class=\"label\" [class.txt-clr-red]=\"myField.touched && myField.invalid\">\r\n <span class=\"label-text\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required?'*':''}}</span>\r\n </label>\r\n \r\n </div>\r\n }\r\n \r\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <textarea\r\n [rows]=\"rows\"\r\n [ngModel]=\"model\"\r\n (ngModelChange)=\"onModelChange($event)\"\r\n (change)=\"changeEv.emit(model)\"\r\n [required]=\"required\"\r\n [disabled]=\"disabled\"\r\n [placeholder]=\"placeholder\"\r\n [attr.maxlength]=\"maxLength\"\r\n [attr.minlength]=\"minLength\"\r\n [style.resize]=\"resize\"\r\n #myField=\"ngModel\"\r\n [class.error]=\"myField.touched && myField.invalid\"\r\n ></textarea>\r\n </div>\r\n <div class=\"textarea-footer\">\r\n @if(myField.touched && myField.invalid) {\r\n @if(myField.errors?.['required']) {\r\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\r\n } @else if(myField.errors?.['minlength']) {\r\n <mat-hint class=\"hint\">Minimum {{minLength}} characters required.</mat-hint>\r\n } @else if(myField.errors?.['maxlength']) {\r\n <mat-hint class=\"hint\">Maximum {{maxLength}} characters allowed.</mat-hint>\r\n }\r\n } @else {\r\n <span></span>\r\n }\r\n @if(showCounter) {\r\n <span class=\"char-counter\" [class.counter-limit]=\"currentLength >= maxLength!\">\r\n {{currentLength}}/{{maxLength}}\r\n </span>\r\n }\r\n </div>\r\n</div>\r\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px;display:flex;align-items:center;overflow:hidden}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}textarea{width:100%;border-radius:var(--osl-border-radius);outline:none;padding:6px 8px;font-size:var(--osl-text-font-size);border:1px solid var(--osl-border-color);resize:none;font-family:inherit;transition:border-color .5s}textarea:focus{border-color:var(--osl-focus-border-color)}textarea:disabled{background:#f5f5f5;cursor:not-allowed;opacity:.7}textarea::placeholder{font-size:var(--osl-label-font-size);color:#aaa}textarea.error{border-color:var(--osl-error-color)}.textarea-footer{display:flex;justify-content:space-between;align-items:center;margin-top:2px}.hint{color:var(--osl-error-color);font-size:var(--osl-hint-font-size)}.char-counter{font-size:var(--osl-hint-font-size);color:#888;margin-left:auto}.char-counter.counter-limit{color:var(--osl-error-color);font-weight:500}.txt-clr-red{color:var(--osl-error-color)}\n"], dependencies: [{ kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: OslSkeletonDirective, selector: "[oslSkeleton]", inputs: ["oslSkeleton", "oslSkeletonType", "oslSkeletonAnimation", "oslSkeletonTheme", "oslSkeletonColor", "oslSkeletonHighlight", "oslSkeletonRadius", "oslSkeletonRows", "oslSkeletonRowGap", "oslSkeletonZIndex", "oslSkeletonDelay", "oslSkeletonDuration", "oslSkeletonMinHeight", "oslSkeletonForceReread", "oslSkeletonCircleSize", "oslSkeletonListItems", "oslSkeletonTableRows", "oslSkeletonTableCols", "oslSkeletonCardLines", "oslSkeletonBgColor"] }] });
|
|
1368
1410
|
}
|
|
1369
1411
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: Osltextarea, decorators: [{
|
|
1370
1412
|
type: Component,
|
|
@@ -1449,7 +1491,7 @@ class OslSelect {
|
|
|
1449
1491
|
return this.model !== null && this.model !== undefined && this.model !== '';
|
|
1450
1492
|
}
|
|
1451
1493
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1452
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslSelect, isStandalone: false, selector: "osl-select", inputs: { label: "label", required: "required", disabled: "disabled", model: "model", datasource: "datasource", displayField: "displayField", valueField: "valueField", placeholder: "placeholder", loading: "loading", clearable: "clearable", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, ngImport: i0, template: "<div class=\"d-flex flex-column\">\r\n @if (label) {\r\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <label class=\"label\" [class.txt-clr-red]=\"myField.touched && myField.invalid\">\r\n <span class=\"label-text\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required?'*':''}}</span>\r\n </label>\r\n \r\n </div>\r\n\r\n }\r\n\r\n <div class=\"select-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\" [class.error]=\"myField.touched && myField.invalid\" [class.select-disabled]=\"disabled || loading\">\r\n @if(loading) {\r\n <span class=\"select-spinner\"></span>\r\n }\r\n <select\r\n [ngModel]=\"model\"\r\n (ngModelChange)=\"onModelChange($event)\"\r\n (change)=\"onChange()\"\r\n [required]=\"required\"\r\n [disabled]=\"disabled || loading\"\r\n #myField=\"ngModel\"\r\n >\r\n <option value=\"\" [disabled]=\"required\" [hidden]=\"required\" selected>{{placeholder}}</option>\r\n @for(item of datasource; track item) {\r\n <option [value]=\"getValue(item)\">{{getDisplay(item)}}</option>\r\n }\r\n </select>\r\n @if(clearable && hasValue && !disabled && !loading) {\r\n <button type=\"button\" class=\"select-clear\" (click)=\"onClear($event)\" title=\"Clear\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n <mat-icon class=\"select-arrow\">expand_more</mat-icon>\r\n </div>\r\n\r\n @if(myField.touched && myField.invalid && required) {\r\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\r\n }\r\n</div>\r\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px;display:flex;align-items:center;overflow:hidden}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}.select-wrapper{position:relative;display:flex;align-items:center;height:var(--osl-control-height);width:100%;border-radius:var(--osl-border-radius);border:1px solid var(--osl-border-color);transition:border-color .5s}.select-wrapper:focus-within{border-color:var(--osl-focus-border-color)}.select-wrapper.error{border-color:var(--osl-error-color)}.select-wrapper.select-disabled{background:#f5f5f5;opacity:.7;cursor:not-allowed}.select-wrapper select{flex:1;height:100%;width:100%;font-size:var(--osl-text-font-size);border:none;outline:none;background:transparent;padding:0 32px 0 8px;cursor:pointer;appearance:none;-webkit-appearance:none}.select-wrapper select:disabled{cursor:not-allowed}.select-arrow{position:absolute;right:6px;font-size:18px;width:18px;height:18px;color:#888;pointer-events:none;-webkit-user-select:none;user-select:none}.select-clear{display:flex;align-items:center;justify-content:center;position:absolute;right:28px;background:none;border:none;padding:0;cursor:pointer;color:#aaa;z-index:1}.select-clear mat-icon{font-size:16px;width:16px;height:16px}.select-clear:hover{color:#333}.select-spinner{position:absolute;left:8px;width:14px;height:14px;border:2px solid var(--osl-border-color);border-top-color:var(--osl-focus-border-color);border-radius:50%;animation:select-spin .7s linear infinite;pointer-events:none}@keyframes select-spin{to{transform:rotate(360deg)}}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.txt-clr-red{color:var(--osl-error-color)}\n"], dependencies: [{ 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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$
|
|
1494
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslSelect, isStandalone: false, selector: "osl-select", inputs: { label: "label", required: "required", disabled: "disabled", model: "model", datasource: "datasource", displayField: "displayField", valueField: "valueField", placeholder: "placeholder", loading: "loading", clearable: "clearable", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, ngImport: i0, template: "<div class=\"d-flex flex-column\">\r\n @if (label) {\r\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <label class=\"label\" [class.txt-clr-red]=\"myField.touched && myField.invalid\">\r\n <span class=\"label-text\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required?'*':''}}</span>\r\n </label>\r\n \r\n </div>\r\n\r\n }\r\n\r\n <div class=\"select-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\" [class.error]=\"myField.touched && myField.invalid\" [class.select-disabled]=\"disabled || loading\">\r\n @if(loading) {\r\n <span class=\"select-spinner\"></span>\r\n }\r\n <select\r\n [ngModel]=\"model\"\r\n (ngModelChange)=\"onModelChange($event)\"\r\n (change)=\"onChange()\"\r\n [required]=\"required\"\r\n [disabled]=\"disabled || loading\"\r\n #myField=\"ngModel\"\r\n >\r\n <option value=\"\" [disabled]=\"required\" [hidden]=\"required\" selected>{{placeholder}}</option>\r\n @for(item of datasource; track item) {\r\n <option [value]=\"getValue(item)\">{{getDisplay(item)}}</option>\r\n }\r\n </select>\r\n @if(clearable && hasValue && !disabled && !loading) {\r\n <button type=\"button\" class=\"select-clear\" (click)=\"onClear($event)\" title=\"Clear\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n }\r\n <mat-icon class=\"select-arrow\">expand_more</mat-icon>\r\n </div>\r\n\r\n @if(myField.touched && myField.invalid && required) {\r\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\r\n }\r\n</div>\r\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px;display:flex;align-items:center;overflow:hidden}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}.select-wrapper{position:relative;display:flex;align-items:center;height:var(--osl-control-height);width:100%;border-radius:var(--osl-border-radius);border:1px solid var(--osl-border-color);transition:border-color .5s}.select-wrapper:focus-within{border-color:var(--osl-focus-border-color)}.select-wrapper.error{border-color:var(--osl-error-color)}.select-wrapper.select-disabled{background:#f5f5f5;opacity:.7;cursor:not-allowed}.select-wrapper select{flex:1;height:100%;width:100%;font-size:var(--osl-text-font-size);border:none;outline:none;background:transparent;padding:0 32px 0 8px;cursor:pointer;appearance:none;-webkit-appearance:none}.select-wrapper select:disabled{cursor:not-allowed}.select-arrow{position:absolute;right:6px;font-size:18px;width:18px;height:18px;color:#888;pointer-events:none;-webkit-user-select:none;user-select:none}.select-clear{display:flex;align-items:center;justify-content:center;position:absolute;right:28px;background:none;border:none;padding:0;cursor:pointer;color:#aaa;z-index:1}.select-clear mat-icon{font-size:16px;width:16px;height:16px}.select-clear:hover{color:#333}.select-spinner{position:absolute;left:8px;width:14px;height:14px;border:2px solid var(--osl-border-color);border-top-color:var(--osl-focus-border-color);border-radius:50%;animation:select-spin .7s linear infinite;pointer-events:none}@keyframes select-spin{to{transform:rotate(360deg)}}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.txt-clr-red{color:var(--osl-error-color)}\n"], dependencies: [{ 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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: OslSkeletonDirective, selector: "[oslSkeleton]", inputs: ["oslSkeleton", "oslSkeletonType", "oslSkeletonAnimation", "oslSkeletonTheme", "oslSkeletonColor", "oslSkeletonHighlight", "oslSkeletonRadius", "oslSkeletonRows", "oslSkeletonRowGap", "oslSkeletonZIndex", "oslSkeletonDelay", "oslSkeletonDuration", "oslSkeletonMinHeight", "oslSkeletonForceReread", "oslSkeletonCircleSize", "oslSkeletonListItems", "oslSkeletonTableRows", "oslSkeletonTableCols", "oslSkeletonCardLines", "oslSkeletonBgColor"] }] });
|
|
1453
1495
|
}
|
|
1454
1496
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslSelect, decorators: [{
|
|
1455
1497
|
type: Component,
|
|
@@ -1884,7 +1926,7 @@ class OslAutocomplete extends baseComponent {
|
|
|
1884
1926
|
}
|
|
1885
1927
|
}
|
|
1886
1928
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslAutocomplete, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
1887
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslAutocomplete, isStandalone: false, selector: "osl-autocomplete", inputs: { label: "label", required: "required", disabled: "disabled", model: "model", datasource: "datasource", displayField: "displayField", valueField: "valueField", placeholder: "placeholder", loading: "loading", searchType: "searchType", methodName: "methodName", configMethodName: "configMethodName", service: "service", object: "object", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme", isLister: "isLister", apiBody: "apiBody" }, outputs: { datasourceChange: "datasourceChange", modelChange: "modelChange", changeEv: "changeEv" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"d-flex flex-column\">\n @if(label){\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n <label class=\"label\" [class.txt-clr-red]=\"isInvalid\">\n <span class=\"label-text\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required?'*':''}}</span>\n @if(service?.route && !disabled) {\n <svg class=\"ac-hint-icon\"\n [matTooltip]=\"model ? 'Edit ' + label : 'Add New'\"\n matTooltipPosition=\"above\"\n (click)=\"onHintClick($event)\"\n xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z\"/>\n </svg>\n }\n </label>\n </div>\n }\n <div class=\"autocomplete-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\" [class.error]=\"isInvalid\" [class.autocomplete-wrapper--api]=\"searchType == 'Api'\">\n <input\n (input)=\"onInput($any($event.target).value)\"\n (focus)=\"onFocus()\"\n (focusout)=\"onFocusOut()\"\n [disabled]=\"disabled || loading\"\n [formControl]=\"inputControl\"\n [class.error]=\"isInvalid\"\n [placeholder]=\"loading ? 'Loading...' : placeholder\"\n autocomplete=\"off\"\n (keyup.enter)=\"searchType == 'Api' ? openLister() : null\"\n >\n @if(loading) {\n <span class=\"ac-spinner\"></span>\n } @else {\n @if(inputControl.value && !disabled) {\n <button type=\"button\" class=\"ac-clear\" [class.ac-clear--shifted]=\"searchType == 'Api'\" (mousedown)=\"clearValue($event)\" tabindex=\"-1\" title=\"Clear\">\n <mat-icon>close</mat-icon>\n </button>\n }\n @if(searchType == 'Api' && isLister && !disabled) {\n <button type=\"button\" class=\"ac-lister-btn\" (mousedown)=\"$event.preventDefault(); openLister()\" tabindex=\"-1\" title=\"Browse list or press Enter\">\n <mat-icon>manage_search</mat-icon>\n </button>\n }\n }\n @if(showDropdown && !loading) {\n <div class=\"dropdown\" [ngStyle]=\"dropdownStyle\">\n @if(filteredItems.length > 0) {\n @for(item of filteredItems; track item) {\n <div class=\"dropdown-item\" (mousedown)=\"selectItem(item)\">{{getDisplay(item)}}</div>\n }\n } @else {\n @if(searchType == 'Local'){\n <div class=\"no-results\">No results found</div>\n }\n }\n </div>\n }\n </div>\n @if(isInvalid) {\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\n }\n</div>\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px;display:flex;align-items:center;overflow:hidden}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}.autocomplete-wrapper{position:relative;display:flex;align-items:center}.autocomplete-wrapper input{height:var(--osl-control-height);width:100%;border-radius:var(--osl-border-radius);outline:none;border:1px solid var(--osl-border-color);padding:0 30px 0 8px;font-size:var(--osl-text-font-size);transition:border-color .5s}.autocomplete-wrapper input:focus{border-color:var(--osl-focus-border-color)}.autocomplete-wrapper input:disabled{background:#f5f5f5;cursor:not-allowed;opacity:.7}.autocomplete-wrapper input::placeholder{font-size:var(--osl-label-font-size);color:#aaa;font-size:12px}.autocomplete-wrapper input.error{border-color:var(--osl-error-color)}.ac-spinner{position:absolute;right:8px;width:14px;height:14px;border:2px solid var(--osl-border-color);border-top-color:var(--osl-focus-border-color);border-radius:50%;animation:ac-spin .7s linear infinite;pointer-events:none}@keyframes ac-spin{to{transform:rotate(360deg)}}.ac-clear{position:absolute;right:6px;display:flex;align-items:center;background:none;border:none;padding:0;cursor:pointer;color:#aaa}.ac-clear mat-icon{font-size:16px;width:16px;height:16px}.ac-clear:hover{color:#333}.ac-clear--shifted{right:40px}.ac-lister-btn{position:absolute;right:5px;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;background:none;border:none;padding:2px;cursor:pointer;border-radius:4px;color:#aaa;opacity:.75;transition:opacity .15s,transform .1s}.ac-lister-btn:hover{opacity:1}.ac-lister-btn:active{transform:translateY(calc(-50% + 1px))}.ac-lister-img{width:24px;height:24px;display:block;pointer-events:none}.autocomplete-wrapper--api input{padding-right:64px}.dropdown{z-index:9999;background:#fff;border:1px solid var(--osl-border-color);border-top:none;border-radius:0 0 var(--osl-border-radius) var(--osl-border-radius);max-height:200px;overflow-y:auto;box-shadow:0 4px 8px #00000014}.dropdown-item{padding:8px 10px;font-size:var(--osl-label-font-size);cursor:pointer}.dropdown-item:hover{background:#f5f5f5}.no-results{padding:8px 10px;font-size:var(--osl-label-font-size);color:#aaa;font-style:italic}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.txt-clr-red{color:var(--osl-error-color)}.ac-hint-icon{width:12px;height:12px;vertical-align:middle;margin-left:4px;color:var(--osl-focus-border-color, #1976d2);cursor:pointer;opacity:.6;transition:opacity .15s}.ac-hint-icon:hover{opacity:1}\n"], dependencies: [{ kind: "directive", type: i1$2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$
|
|
1929
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslAutocomplete, isStandalone: false, selector: "osl-autocomplete", inputs: { label: "label", required: "required", disabled: "disabled", model: "model", datasource: "datasource", displayField: "displayField", valueField: "valueField", placeholder: "placeholder", loading: "loading", searchType: "searchType", methodName: "methodName", configMethodName: "configMethodName", service: "service", object: "object", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme", isLister: "isLister", apiBody: "apiBody" }, outputs: { datasourceChange: "datasourceChange", modelChange: "modelChange", changeEv: "changeEv" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"d-flex flex-column\">\n @if(label){\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n <label class=\"label\" [class.txt-clr-red]=\"isInvalid\">\n <span class=\"label-text\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required?'*':''}}</span>\n @if(service?.route && !disabled) {\n <svg class=\"ac-hint-icon\"\n [matTooltip]=\"model ? 'Edit ' + label : 'Add New'\"\n matTooltipPosition=\"above\"\n (click)=\"onHintClick($event)\"\n xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z\"/>\n </svg>\n }\n </label>\n </div>\n }\n <div class=\"autocomplete-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\" [class.error]=\"isInvalid\" [class.autocomplete-wrapper--api]=\"searchType == 'Api'\">\n <input\n (input)=\"onInput($any($event.target).value)\"\n (focus)=\"onFocus()\"\n (focusout)=\"onFocusOut()\"\n [disabled]=\"disabled || loading\"\n [formControl]=\"inputControl\"\n [class.error]=\"isInvalid\"\n [placeholder]=\"loading ? 'Loading...' : placeholder\"\n autocomplete=\"off\"\n (keyup.enter)=\"searchType == 'Api' ? openLister() : null\"\n >\n @if(loading) {\n <span class=\"ac-spinner\"></span>\n } @else {\n @if(inputControl.value && !disabled) {\n <button type=\"button\" class=\"ac-clear\" [class.ac-clear--shifted]=\"searchType == 'Api'\" (mousedown)=\"clearValue($event)\" tabindex=\"-1\" title=\"Clear\">\n <mat-icon>close</mat-icon>\n </button>\n }\n @if(searchType == 'Api' && isLister && !disabled) {\n <button type=\"button\" class=\"ac-lister-btn\" (mousedown)=\"$event.preventDefault(); openLister()\" tabindex=\"-1\" title=\"Browse list or press Enter\">\n <mat-icon>manage_search</mat-icon>\n </button>\n }\n }\n @if(showDropdown && !loading) {\n <div class=\"dropdown\" [ngStyle]=\"dropdownStyle\">\n @if(filteredItems.length > 0) {\n @for(item of filteredItems; track item) {\n <div class=\"dropdown-item\" (mousedown)=\"selectItem(item)\">{{getDisplay(item)}}</div>\n }\n } @else {\n @if(searchType == 'Local'){\n <div class=\"no-results\">No results found</div>\n }\n }\n </div>\n }\n </div>\n @if(isInvalid) {\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\n }\n</div>\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px;display:flex;align-items:center;overflow:hidden}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}.autocomplete-wrapper{position:relative;display:flex;align-items:center}.autocomplete-wrapper input{height:var(--osl-control-height);width:100%;border-radius:var(--osl-border-radius);outline:none;border:1px solid var(--osl-border-color);padding:0 30px 0 8px;font-size:var(--osl-text-font-size);transition:border-color .5s}.autocomplete-wrapper input:focus{border-color:var(--osl-focus-border-color)}.autocomplete-wrapper input:disabled{background:#f5f5f5;cursor:not-allowed;opacity:.7}.autocomplete-wrapper input::placeholder{font-size:var(--osl-label-font-size);color:#aaa;font-size:12px}.autocomplete-wrapper input.error{border-color:var(--osl-error-color)}.ac-spinner{position:absolute;right:8px;width:14px;height:14px;border:2px solid var(--osl-border-color);border-top-color:var(--osl-focus-border-color);border-radius:50%;animation:ac-spin .7s linear infinite;pointer-events:none}@keyframes ac-spin{to{transform:rotate(360deg)}}.ac-clear{position:absolute;right:6px;display:flex;align-items:center;background:none;border:none;padding:0;cursor:pointer;color:#aaa}.ac-clear mat-icon{font-size:16px;width:16px;height:16px}.ac-clear:hover{color:#333}.ac-clear--shifted{right:40px}.ac-lister-btn{position:absolute;right:5px;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;justify-content:center;background:none;border:none;padding:2px;cursor:pointer;border-radius:4px;color:#aaa;opacity:.75;transition:opacity .15s,transform .1s}.ac-lister-btn:hover{opacity:1}.ac-lister-btn:active{transform:translateY(calc(-50% + 1px))}.ac-lister-img{width:24px;height:24px;display:block;pointer-events:none}.autocomplete-wrapper--api input{padding-right:64px}.dropdown{z-index:9999;background:#fff;border:1px solid var(--osl-border-color);border-top:none;border-radius:0 0 var(--osl-border-radius) var(--osl-border-radius);max-height:200px;overflow-y:auto;box-shadow:0 4px 8px #00000014}.dropdown-item{padding:8px 10px;font-size:var(--osl-label-font-size);cursor:pointer}.dropdown-item:hover{background:#f5f5f5}.no-results{padding:8px 10px;font-size:var(--osl-label-font-size);color:#aaa;font-style:italic}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.txt-clr-red{color:var(--osl-error-color)}.ac-hint-icon{width:12px;height:12px;vertical-align:middle;margin-left:4px;color:var(--osl-focus-border-color, #1976d2);cursor:pointer;opacity:.6;transition:opacity .15s}.ac-hint-icon:hover{opacity:1}\n"], dependencies: [{ kind: "directive", type: i1$2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: OslSkeletonDirective, selector: "[oslSkeleton]", inputs: ["oslSkeleton", "oslSkeletonType", "oslSkeletonAnimation", "oslSkeletonTheme", "oslSkeletonColor", "oslSkeletonHighlight", "oslSkeletonRadius", "oslSkeletonRows", "oslSkeletonRowGap", "oslSkeletonZIndex", "oslSkeletonDelay", "oslSkeletonDuration", "oslSkeletonMinHeight", "oslSkeletonForceReread", "oslSkeletonCircleSize", "oslSkeletonListItems", "oslSkeletonTableRows", "oslSkeletonTableCols", "oslSkeletonCardLines", "oslSkeletonBgColor"] }, { kind: "directive", type: i6.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
|
|
1888
1930
|
}
|
|
1889
1931
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslAutocomplete, decorators: [{
|
|
1890
1932
|
type: Component,
|
|
@@ -2046,7 +2088,7 @@ class OslFileUpload {
|
|
|
2046
2088
|
return `Max ${this.maxSize} B`;
|
|
2047
2089
|
}
|
|
2048
2090
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslFileUpload, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2049
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslFileUpload, isStandalone: false, selector: "osl-file-upload", inputs: { label: "label", required: "required", disabled: "disabled", model: "model", accept: "accept", multiple: "multiple", maxSize: "maxSize", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, ngImport: i0, template: "<div class=\"d-flex flex-column\">\r\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <label class=\"label\" [class.txt-clr-red]=\"isInvalid\">\r\n <span class=\"label-text\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required?'*':''}}</span>\r\n </label>\r\n </div>\r\n\r\n <input\r\n type=\"file\"\r\n [accept]=\"accept\"\r\n [multiple]=\"multiple\"\r\n [disabled]=\"disabled\"\r\n (change)=\"onFileChange($event)\"\r\n #fileInput\r\n class=\"file-input\"\r\n >\r\n\r\n <div\r\n [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\"\r\n class=\"drop-zone\"\r\n [class.error]=\"isInvalid || sizeError\"\r\n [class.file-disabled]=\"disabled\"\r\n [class.drag-over]=\"isDragOver\"\r\n [class.has-file]=\"!!model\"\r\n (click)=\"triggerInput(fileInput)\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave()\"\r\n (drop)=\"onDrop($event)\"\r\n >\r\n @if(!model) {\r\n <div class=\"drop-content\">\r\n <mat-icon class=\"upload-icon\">upload_file</mat-icon>\r\n <div class=\"drop-text\">\r\n <span class=\"browse-link\">Browse</span> or drag & drop\r\n </div>\r\n @if(accept) { <div class=\"drop-meta\">{{accept}}</div> }\r\n @if(maxSize) { <div class=\"drop-meta\">{{maxSizeLabel}}</div> }\r\n </div>\r\n } @else {\r\n <div class=\"file-list\">\r\n <mat-icon class=\"file-icon\">insert_drive_file</mat-icon>\r\n <span class=\"file-name\">{{fileNames}}</span>\r\n <button type=\"button\" class=\"clear-btn\" (click)=\"clearFiles($event)\" title=\"Remove\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if(sizeError) {\r\n <mat-hint class=\"hint\">File exceeds the {{maxSizeLabel}} limit.</mat-hint>\r\n } @else if(isInvalid) {\r\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\r\n }\r\n</div>\r\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px;display:flex;align-items:center;overflow:hidden}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}.file-input{display:none}.drop-zone{width:100%;min-height:var(--osl-control-height);border-radius:var(--osl-border-radius);border:1.5px dashed var(--osl-border-color);cursor:pointer;transition:border-color .3s,background .3s;display:flex;align-items:center;justify-content:center;padding:8px}.drop-zone:hover:not(.file-disabled){border-color:var(--osl-focus-border-color);background:#00000003}.drop-zone.drag-over{border-color:var(--osl-focus-border-color);background:#00000008}.drop-zone.has-file{border-style:solid;border-color:var(--osl-border-color)}.drop-zone.error{border-color:var(--osl-error-color)}.drop-zone.file-disabled{opacity:.6;cursor:not-allowed;background:#f5f5f5}.drop-content{display:flex;flex-direction:column;align-items:center;gap:4px;color:#888;pointer-events:none}.upload-icon{font-size:28px;width:28px;height:28px;color:#bbb}.drop-text{font-size:var(--osl-label-font-size)}.browse-link{color:var(--osl-focus-border-color);font-weight:500}.drop-meta{font-size:var(--osl-hint-font-size);color:#bbb}.file-list{display:flex;align-items:center;gap:8px;width:100%;pointer-events:none}.file-icon{font-size:20px;width:20px;height:20px;color:#888;flex-shrink:0}.file-name{flex:1;font-size:var(--osl-text-font-size);color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.clear-btn{pointer-events:all;display:flex;align-items:center;background:none;border:none;cursor:pointer;color:#aaa;padding:0;flex-shrink:0}.clear-btn mat-icon{font-size:16px;width:16px;height:16px}.clear-btn:hover{color:var(--osl-error-color)}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.txt-clr-red{color:var(--osl-error-color)}\n"], dependencies: [{ kind: "directive", type: i2$
|
|
2091
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslFileUpload, isStandalone: false, selector: "osl-file-upload", inputs: { label: "label", required: "required", disabled: "disabled", model: "model", accept: "accept", multiple: "multiple", maxSize: "maxSize", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, ngImport: i0, template: "<div class=\"d-flex flex-column\">\r\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <label class=\"label\" [class.txt-clr-red]=\"isInvalid\">\r\n <span class=\"label-text\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required?'*':''}}</span>\r\n </label>\r\n </div>\r\n\r\n <input\r\n type=\"file\"\r\n [accept]=\"accept\"\r\n [multiple]=\"multiple\"\r\n [disabled]=\"disabled\"\r\n (change)=\"onFileChange($event)\"\r\n #fileInput\r\n class=\"file-input\"\r\n >\r\n\r\n <div\r\n [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\"\r\n class=\"drop-zone\"\r\n [class.error]=\"isInvalid || sizeError\"\r\n [class.file-disabled]=\"disabled\"\r\n [class.drag-over]=\"isDragOver\"\r\n [class.has-file]=\"!!model\"\r\n (click)=\"triggerInput(fileInput)\"\r\n (dragover)=\"onDragOver($event)\"\r\n (dragleave)=\"onDragLeave()\"\r\n (drop)=\"onDrop($event)\"\r\n >\r\n @if(!model) {\r\n <div class=\"drop-content\">\r\n <mat-icon class=\"upload-icon\">upload_file</mat-icon>\r\n <div class=\"drop-text\">\r\n <span class=\"browse-link\">Browse</span> or drag & drop\r\n </div>\r\n @if(accept) { <div class=\"drop-meta\">{{accept}}</div> }\r\n @if(maxSize) { <div class=\"drop-meta\">{{maxSizeLabel}}</div> }\r\n </div>\r\n } @else {\r\n <div class=\"file-list\">\r\n <mat-icon class=\"file-icon\">insert_drive_file</mat-icon>\r\n <span class=\"file-name\">{{fileNames}}</span>\r\n <button type=\"button\" class=\"clear-btn\" (click)=\"clearFiles($event)\" title=\"Remove\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if(sizeError) {\r\n <mat-hint class=\"hint\">File exceeds the {{maxSizeLabel}} limit.</mat-hint>\r\n } @else if(isInvalid) {\r\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\r\n }\r\n</div>\r\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px;display:flex;align-items:center;overflow:hidden}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}.file-input{display:none}.drop-zone{width:100%;min-height:var(--osl-control-height);border-radius:var(--osl-border-radius);border:1.5px dashed var(--osl-border-color);cursor:pointer;transition:border-color .3s,background .3s;display:flex;align-items:center;justify-content:center;padding:8px}.drop-zone:hover:not(.file-disabled){border-color:var(--osl-focus-border-color);background:#00000003}.drop-zone.drag-over{border-color:var(--osl-focus-border-color);background:#00000008}.drop-zone.has-file{border-style:solid;border-color:var(--osl-border-color)}.drop-zone.error{border-color:var(--osl-error-color)}.drop-zone.file-disabled{opacity:.6;cursor:not-allowed;background:#f5f5f5}.drop-content{display:flex;flex-direction:column;align-items:center;gap:4px;color:#888;pointer-events:none}.upload-icon{font-size:28px;width:28px;height:28px;color:#bbb}.drop-text{font-size:var(--osl-label-font-size)}.browse-link{color:var(--osl-focus-border-color);font-weight:500}.drop-meta{font-size:var(--osl-hint-font-size);color:#bbb}.file-list{display:flex;align-items:center;gap:8px;width:100%;pointer-events:none}.file-icon{font-size:20px;width:20px;height:20px;color:#888;flex-shrink:0}.file-name{flex:1;font-size:var(--osl-text-font-size);color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.clear-btn{pointer-events:all;display:flex;align-items:center;background:none;border:none;cursor:pointer;color:#aaa;padding:0;flex-shrink:0}.clear-btn mat-icon{font-size:16px;width:16px;height:16px}.clear-btn:hover{color:var(--osl-error-color)}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.txt-clr-red{color:var(--osl-error-color)}\n"], dependencies: [{ kind: "directive", type: i2$2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: OslSkeletonDirective, selector: "[oslSkeleton]", inputs: ["oslSkeleton", "oslSkeletonType", "oslSkeletonAnimation", "oslSkeletonTheme", "oslSkeletonColor", "oslSkeletonHighlight", "oslSkeletonRadius", "oslSkeletonRows", "oslSkeletonRowGap", "oslSkeletonZIndex", "oslSkeletonDelay", "oslSkeletonDuration", "oslSkeletonMinHeight", "oslSkeletonForceReread", "oslSkeletonCircleSize", "oslSkeletonListItems", "oslSkeletonTableRows", "oslSkeletonTableCols", "oslSkeletonCardLines", "oslSkeletonBgColor"] }] });
|
|
2050
2092
|
}
|
|
2051
2093
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslFileUpload, decorators: [{
|
|
2052
2094
|
type: Component,
|
|
@@ -2142,7 +2184,7 @@ class OslDatepicker {
|
|
|
2142
2184
|
return localISO;
|
|
2143
2185
|
}
|
|
2144
2186
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslDatepicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2145
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslDatepicker, isStandalone: false, selector: "osl-datepicker", inputs: { label: "label", required: "required", disabled: "disabled", model: "model", dateType: "dateType", placeholder: "placeholder", minDate: "minDate", maxDate: "maxDate", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, ngImport: i0, template: "<div class=\"d-flex flex-column\">\r\n @if (label) {\r\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <label class=\"label\" [class.txt-clr-red]=\"myField.touched && myField.invalid\">\r\n <span class=\"label-text\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required ? '*' : ''}}</span>\r\n </label>\r\n </div>\r\n }\r\n <div class=\"input-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <input\r\n class=\"date-input\"\r\n [matDatepicker]=\"picker\"\r\n [ngModel]=\"model\"\r\n \r\n (dateChange)=\"onDateChange($event.value)\"\r\n [required]=\"required\"\r\n [disabled]=\"disabled\"\r\n [min]=\"minDateObj\"\r\n [max]=\"maxDateObj\"\r\n [placeholder]=\"placeholder || 'DD/MMM/YYYY'\"\r\n [class.error]=\"myField.touched && myField.invalid\"\r\n #myField=\"ngModel\"\r\n >\r\n <mat-datepicker-toggle class=\"picker-toggle\" [for]=\"picker\" [disabled]=\"disabled\">\r\n <mat-icon matDatepickerToggleIcon>calendar_today</mat-icon>\r\n </mat-datepicker-toggle>\r\n <mat-datepicker #picker></mat-datepicker>\r\n </div>\r\n @if (myField.touched && myField.invalid && required) {\r\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\r\n }\r\n</div>\r\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px;display:flex;align-items:center;overflow:hidden}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}.txt-clr-red{color:var(--osl-error-color)}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.input-wrapper{position:relative;display:flex;align-items:center}.date-input{height:var(--osl-control-height);width:100%;border-radius:var(--osl-border-radius);outline:none;border:1px solid var(--osl-border-color);padding:0 36px 0 10px;font-size:var(--osl-text-font-size);font-family:inherit;color:#333;background-color:#fff;transition:border-color .2s,box-shadow .2s;letter-spacing:.01em;box-sizing:border-box}.date-input::placeholder{color:#bbb}.date-input:hover:not(:disabled){border-color:var(--osl-focus-border-color)}.date-input:focus{border-color:var(--osl-focus-border-color);box-shadow:0 0 0 3px var(--osl-focus-shadow, rgba(99, 102, 241, .14))}.date-input:disabled{background-color:#f5f5f5;cursor:not-allowed;opacity:.7}.date-input.error{border-color:var(--osl-error-color)}.date-input.error:focus{box-shadow:0 0 0 3px var(--osl-error-shadow, rgba(220, 38, 38, .14))}.picker-toggle{position:absolute;right:4px;display:flex;align-items:center}.picker-toggle ::ng-deep .mat-mdc-icon-button{width:28px;height:28px;padding:4px;line-height:1;display:flex;align-items:center;justify-content:center}.picker-toggle ::ng-deep .mat-mdc-icon-button:hover .mat-mdc-button-persistent-ripple:before{opacity:0}.picker-toggle ::ng-deep .mat-mdc-icon-button .mat-icon{font-size:16px;width:16px;height:16px;color:#333;opacity:.45;transition:opacity .15s,background-color .15s;border-radius:4px;padding:3px;box-sizing:content-box}.picker-toggle ::ng-deep .mat-mdc-icon-button:hover:not([disabled]) .mat-icon{opacity:.9;background-color:#00000012}.picker-toggle ::ng-deep .mat-mdc-icon-button[disabled]{pointer-events:none;opacity:.4}\n"], dependencies: [{ kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$
|
|
2187
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslDatepicker, isStandalone: false, selector: "osl-datepicker", inputs: { label: "label", required: "required", disabled: "disabled", model: "model", dateType: "dateType", placeholder: "placeholder", minDate: "minDate", maxDate: "maxDate", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, ngImport: i0, template: "<div class=\"d-flex flex-column\">\r\n @if (label) {\r\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <label class=\"label\" [class.txt-clr-red]=\"myField.touched && myField.invalid\">\r\n <span class=\"label-text\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required ? '*' : ''}}</span>\r\n </label>\r\n </div>\r\n }\r\n <div class=\"input-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <input\r\n class=\"date-input\"\r\n [matDatepicker]=\"picker\"\r\n [ngModel]=\"model\"\r\n \r\n (dateChange)=\"onDateChange($event.value)\"\r\n [required]=\"required\"\r\n [disabled]=\"disabled\"\r\n [min]=\"minDateObj\"\r\n [max]=\"maxDateObj\"\r\n [placeholder]=\"placeholder || 'DD/MMM/YYYY'\"\r\n [class.error]=\"myField.touched && myField.invalid\"\r\n #myField=\"ngModel\"\r\n >\r\n <mat-datepicker-toggle class=\"picker-toggle\" [for]=\"picker\" [disabled]=\"disabled\">\r\n <mat-icon matDatepickerToggleIcon>calendar_today</mat-icon>\r\n </mat-datepicker-toggle>\r\n <mat-datepicker #picker></mat-datepicker>\r\n </div>\r\n @if (myField.touched && myField.invalid && required) {\r\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\r\n }\r\n</div>\r\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px;display:flex;align-items:center;overflow:hidden}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}.txt-clr-red{color:var(--osl-error-color)}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.input-wrapper{position:relative;display:flex;align-items:center}.date-input{height:var(--osl-control-height);width:100%;border-radius:var(--osl-border-radius);outline:none;border:1px solid var(--osl-border-color);padding:0 36px 0 10px;font-size:var(--osl-text-font-size);font-family:inherit;color:#333;background-color:#fff;transition:border-color .2s,box-shadow .2s;letter-spacing:.01em;box-sizing:border-box}.date-input::placeholder{color:#bbb}.date-input:hover:not(:disabled){border-color:var(--osl-focus-border-color)}.date-input:focus{border-color:var(--osl-focus-border-color);box-shadow:0 0 0 3px var(--osl-focus-shadow, rgba(99, 102, 241, .14))}.date-input:disabled{background-color:#f5f5f5;cursor:not-allowed;opacity:.7}.date-input.error{border-color:var(--osl-error-color)}.date-input.error:focus{box-shadow:0 0 0 3px var(--osl-error-shadow, rgba(220, 38, 38, .14))}.picker-toggle{position:absolute;right:4px;display:flex;align-items:center}.picker-toggle ::ng-deep .mat-mdc-icon-button{width:28px;height:28px;padding:4px;line-height:1;display:flex;align-items:center;justify-content:center}.picker-toggle ::ng-deep .mat-mdc-icon-button:hover .mat-mdc-button-persistent-ripple:before{opacity:0}.picker-toggle ::ng-deep .mat-mdc-icon-button .mat-icon{font-size:16px;width:16px;height:16px;color:#333;opacity:.45;transition:opacity .15s,background-color .15s;border-radius:4px;padding:3px;box-sizing:content-box}.picker-toggle ::ng-deep .mat-mdc-icon-button:hover:not([disabled]) .mat-icon{opacity:.9;background-color:#00000012}.picker-toggle ::ng-deep .mat-mdc-icon-button[disabled]{pointer-events:none;opacity:.4}\n"], dependencies: [{ kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "component", type: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: OslSkeletonDirective, selector: "[oslSkeleton]", inputs: ["oslSkeleton", "oslSkeletonType", "oslSkeletonAnimation", "oslSkeletonTheme", "oslSkeletonColor", "oslSkeletonHighlight", "oslSkeletonRadius", "oslSkeletonRows", "oslSkeletonRowGap", "oslSkeletonZIndex", "oslSkeletonDelay", "oslSkeletonDuration", "oslSkeletonMinHeight", "oslSkeletonForceReread", "oslSkeletonCircleSize", "oslSkeletonListItems", "oslSkeletonTableRows", "oslSkeletonTableCols", "oslSkeletonCardLines", "oslSkeletonBgColor"] }, { kind: "component", type: i5.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i5.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i5.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "directive", type: i5.MatDatepickerToggleIcon, selector: "[matDatepickerToggleIcon]" }] });
|
|
2146
2188
|
}
|
|
2147
2189
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslDatepicker, decorators: [{
|
|
2148
2190
|
type: Component,
|
|
@@ -2183,462 +2225,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
2183
2225
|
type: Output
|
|
2184
2226
|
}] } });
|
|
2185
2227
|
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
return value instanceof Date && !isNaN(value.getTime());
|
|
2205
|
-
}
|
|
2206
|
-
/**
|
|
2207
|
-
* Formats a Date using a pattern string.
|
|
2208
|
-
* Tokens: YYYY, YY, MM, M, DD, D, HH, H, mm, m, ss, s,
|
|
2209
|
-
* MMM (short month), MMMM (full month),
|
|
2210
|
-
* ddd (short day), dddd (full day).
|
|
2211
|
-
*/
|
|
2212
|
-
function formatDate(date, pattern) {
|
|
2213
|
-
const d = new Date(date);
|
|
2214
|
-
if (!isValidDate$1(d))
|
|
2215
|
-
return '';
|
|
2216
|
-
const year = d.getFullYear();
|
|
2217
|
-
const month = d.getMonth();
|
|
2218
|
-
const day = d.getDate();
|
|
2219
|
-
const hours = d.getHours();
|
|
2220
|
-
const mins = d.getMinutes();
|
|
2221
|
-
const secs = d.getSeconds();
|
|
2222
|
-
const dayOfWeek = d.getDay();
|
|
2223
|
-
return pattern
|
|
2224
|
-
.replace('YYYY', String(year))
|
|
2225
|
-
.replace('YY', String(year).slice(-2))
|
|
2226
|
-
.replace('MMMM', MONTH_NAMES_FULL[month])
|
|
2227
|
-
.replace('MMM', MONTH_NAMES_SHORT[month])
|
|
2228
|
-
.replace('MM', pad(month + 1))
|
|
2229
|
-
.replace('M', String(month + 1))
|
|
2230
|
-
.replace('dddd', DAY_NAMES_FULL[dayOfWeek])
|
|
2231
|
-
.replace('ddd', DAY_NAMES_SHORT[dayOfWeek])
|
|
2232
|
-
.replace('DD', pad(day))
|
|
2233
|
-
.replace('D', String(day))
|
|
2234
|
-
.replace('HH', pad(hours))
|
|
2235
|
-
.replace('H', String(hours))
|
|
2236
|
-
.replace('mm', pad(mins))
|
|
2237
|
-
.replace('m', String(mins))
|
|
2238
|
-
.replace('ss', pad(secs))
|
|
2239
|
-
.replace('s', String(secs));
|
|
2240
|
-
}
|
|
2241
|
-
/** Returns date as `YYYY-MM-DD`. */
|
|
2242
|
-
function toDateOnly(date) {
|
|
2243
|
-
return formatDate(date, 'YYYY-MM-DD');
|
|
2244
|
-
}
|
|
2245
|
-
/** Returns the ISO 8601 string for a date. */
|
|
2246
|
-
function toISOString(date) {
|
|
2247
|
-
return date.toISOString();
|
|
2248
|
-
}
|
|
2249
|
-
/** Returns a new date with `days` added. */
|
|
2250
|
-
function addDays(date, days) {
|
|
2251
|
-
const result = new Date(date);
|
|
2252
|
-
result.setDate(result.getDate() + days);
|
|
2253
|
-
return result;
|
|
2254
|
-
}
|
|
2255
|
-
/** Returns a new date with `months` added. */
|
|
2256
|
-
function addMonths(date, months) {
|
|
2257
|
-
const result = new Date(date);
|
|
2258
|
-
result.setMonth(result.getMonth() + months);
|
|
2259
|
-
return result;
|
|
2260
|
-
}
|
|
2261
|
-
/** Returns a new date with `years` added. */
|
|
2262
|
-
function addYears(date, years) {
|
|
2263
|
-
const result = new Date(date);
|
|
2264
|
-
result.setFullYear(result.getFullYear() + years);
|
|
2265
|
-
return result;
|
|
2266
|
-
}
|
|
2267
|
-
/** Returns a new date with `hours` added. */
|
|
2268
|
-
function addHours(date, hours) {
|
|
2269
|
-
return new Date(date.getTime() + hours * 3_600_000);
|
|
2270
|
-
}
|
|
2271
|
-
/** Returns a new date with `minutes` added. */
|
|
2272
|
-
function addMinutes(date, minutes) {
|
|
2273
|
-
return new Date(date.getTime() + minutes * 60_000);
|
|
2274
|
-
}
|
|
2275
|
-
/** Returns a new date with `days` subtracted. */
|
|
2276
|
-
function subtractDays(date, days) {
|
|
2277
|
-
return addDays(date, -days);
|
|
2278
|
-
}
|
|
2279
|
-
/** Returns a new date with `months` subtracted. */
|
|
2280
|
-
function subtractMonths(date, months) {
|
|
2281
|
-
return addMonths(date, -months);
|
|
2282
|
-
}
|
|
2283
|
-
/** Returns a new date with `years` subtracted. */
|
|
2284
|
-
function subtractYears(date, years) {
|
|
2285
|
-
return addYears(date, -years);
|
|
2286
|
-
}
|
|
2287
|
-
/** Returns the absolute difference in whole days between two dates. */
|
|
2288
|
-
function diffInDays(date1, date2) {
|
|
2289
|
-
return Math.abs(Math.floor((date1.getTime() - date2.getTime()) / 86_400_000));
|
|
2290
|
-
}
|
|
2291
|
-
/** Returns the absolute difference in whole months between two dates. */
|
|
2292
|
-
function diffInMonths(date1, date2) {
|
|
2293
|
-
return Math.abs((date1.getFullYear() - date2.getFullYear()) * 12 +
|
|
2294
|
-
(date1.getMonth() - date2.getMonth()));
|
|
2295
|
-
}
|
|
2296
|
-
/** Returns the absolute difference in whole years between two dates. */
|
|
2297
|
-
function diffInYears(date1, date2) {
|
|
2298
|
-
return Math.abs(date1.getFullYear() - date2.getFullYear());
|
|
2299
|
-
}
|
|
2300
|
-
/** Returns the difference in minutes between two dates (date1 - date2). */
|
|
2301
|
-
function diffInMinutes(date1, date2) {
|
|
2302
|
-
return Math.floor((date1.getTime() - date2.getTime()) / 60_000);
|
|
2303
|
-
}
|
|
2304
|
-
/** Returns true if date1 is strictly before date2. */
|
|
2305
|
-
function isBefore(date1, date2) {
|
|
2306
|
-
return date1.getTime() < date2.getTime();
|
|
2307
|
-
}
|
|
2308
|
-
/** Returns true if date1 is strictly after date2. */
|
|
2309
|
-
function isAfter(date1, date2) {
|
|
2310
|
-
return date1.getTime() > date2.getTime();
|
|
2311
|
-
}
|
|
2312
|
-
/** Returns true if both dates fall on the same calendar day. */
|
|
2313
|
-
function isSameDay(date1, date2) {
|
|
2314
|
-
return (date1.getFullYear() === date2.getFullYear() &&
|
|
2315
|
-
date1.getMonth() === date2.getMonth() &&
|
|
2316
|
-
date1.getDate() === date2.getDate());
|
|
2317
|
-
}
|
|
2318
|
-
/** Returns true if the date falls on a Saturday or Sunday. */
|
|
2319
|
-
function isWeekend(date) {
|
|
2320
|
-
return date.getDay() === 0 || date.getDay() === 6;
|
|
2321
|
-
}
|
|
2322
|
-
/** Returns true if the date is today. */
|
|
2323
|
-
function isToday(date) {
|
|
2324
|
-
return isSameDay(date, new Date());
|
|
2325
|
-
}
|
|
2326
|
-
/** Returns true if the date falls in the past (before now). */
|
|
2327
|
-
function isPast(date) {
|
|
2328
|
-
return date.getTime() < Date.now();
|
|
2329
|
-
}
|
|
2330
|
-
/** Returns true if the date falls in the future (after now). */
|
|
2331
|
-
function isFuture(date) {
|
|
2332
|
-
return date.getTime() > Date.now();
|
|
2333
|
-
}
|
|
2334
|
-
/** Returns the date with time set to 00:00:00.000. */
|
|
2335
|
-
function startOfDay(date) {
|
|
2336
|
-
const result = new Date(date);
|
|
2337
|
-
result.setHours(0, 0, 0, 0);
|
|
2338
|
-
return result;
|
|
2339
|
-
}
|
|
2340
|
-
/** Returns the date with time set to 23:59:59.999. */
|
|
2341
|
-
function endOfDay(date) {
|
|
2342
|
-
const result = new Date(date);
|
|
2343
|
-
result.setHours(23, 59, 59, 999);
|
|
2344
|
-
return result;
|
|
2345
|
-
}
|
|
2346
|
-
/** Returns the first day of the month (time 00:00:00.000). */
|
|
2347
|
-
function startOfMonth(date) {
|
|
2348
|
-
return new Date(date.getFullYear(), date.getMonth(), 1);
|
|
2349
|
-
}
|
|
2350
|
-
/** Returns the last day of the month (time 23:59:59.999). */
|
|
2351
|
-
function endOfMonth(date) {
|
|
2352
|
-
const result = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
2353
|
-
result.setHours(23, 59, 59, 999);
|
|
2354
|
-
return result;
|
|
2355
|
-
}
|
|
2356
|
-
/** Returns the first day of the year (Jan 1, 00:00:00.000). */
|
|
2357
|
-
function startOfYear(date) {
|
|
2358
|
-
return new Date(date.getFullYear(), 0, 1);
|
|
2359
|
-
}
|
|
2360
|
-
/** Returns the last day of the year (Dec 31, 23:59:59.999). */
|
|
2361
|
-
function endOfYear(date) {
|
|
2362
|
-
const result = new Date(date.getFullYear(), 11, 31);
|
|
2363
|
-
result.setHours(23, 59, 59, 999);
|
|
2364
|
-
return result;
|
|
2365
|
-
}
|
|
2366
|
-
/** Calculates the age in years from a birth date. */
|
|
2367
|
-
function getAge(birthDate) {
|
|
2368
|
-
const n = new Date();
|
|
2369
|
-
let age = n.getFullYear() - birthDate.getFullYear();
|
|
2370
|
-
const monthDiff = n.getMonth() - birthDate.getMonth();
|
|
2371
|
-
if (monthDiff < 0 || (monthDiff === 0 && n.getDate() < birthDate.getDate()))
|
|
2372
|
-
age--;
|
|
2373
|
-
return age;
|
|
2374
|
-
}
|
|
2375
|
-
/** Returns the number of days in a given month (0-based month). */
|
|
2376
|
-
function daysInMonth(year, month) {
|
|
2377
|
-
return new Date(year, month + 1, 0).getDate();
|
|
2378
|
-
}
|
|
2379
|
-
/** Returns true if the given year is a leap year. */
|
|
2380
|
-
function isLeapYear(year) {
|
|
2381
|
-
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
2382
|
-
}
|
|
2383
|
-
/**
|
|
2384
|
-
* Returns a human-friendly relative time string.
|
|
2385
|
-
* @example timeAgo(new Date(Date.now() - 3600_000)) → '1 hour ago'
|
|
2386
|
-
*/
|
|
2387
|
-
function timeAgo(date) {
|
|
2388
|
-
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
2389
|
-
const abs = Math.abs(seconds);
|
|
2390
|
-
const future = seconds < 0;
|
|
2391
|
-
const intervals = [
|
|
2392
|
-
[31_536_000, 'year'],
|
|
2393
|
-
[2_592_000, 'month'],
|
|
2394
|
-
[604_800, 'week'],
|
|
2395
|
-
[86_400, 'day'],
|
|
2396
|
-
[3_600, 'hour'],
|
|
2397
|
-
[60, 'minute'],
|
|
2398
|
-
[1, 'second'],
|
|
2399
|
-
];
|
|
2400
|
-
for (const [secs, label] of intervals) {
|
|
2401
|
-
const count = Math.floor(abs / secs);
|
|
2402
|
-
if (count >= 1) {
|
|
2403
|
-
const unit = count === 1 ? label : `${label}s`;
|
|
2404
|
-
return future ? `in ${count} ${unit}` : `${count} ${unit} ago`;
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
return 'just now';
|
|
2408
|
-
}
|
|
2409
|
-
/** Returns the ISO week number (1–53) for the given date. */
|
|
2410
|
-
function getWeekNumber(date) {
|
|
2411
|
-
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
|
2412
|
-
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));
|
|
2413
|
-
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
2414
|
-
return Math.ceil((((d.getTime() - yearStart.getTime()) / 86_400_000) + 1) / 7);
|
|
2415
|
-
}
|
|
2416
|
-
/** Returns the next weekday (Mon–Fri) after the given date. */
|
|
2417
|
-
function nextWorkday(date) {
|
|
2418
|
-
const result = addDays(date, 1);
|
|
2419
|
-
while (isWeekend(result)) {
|
|
2420
|
-
result.setDate(result.getDate() + 1);
|
|
2421
|
-
}
|
|
2422
|
-
return result;
|
|
2423
|
-
}
|
|
2424
|
-
/** Returns true if a date falls within the range [start, end] (inclusive). */
|
|
2425
|
-
function inRange$2(date, start, end) {
|
|
2426
|
-
return date >= start && date <= end;
|
|
2427
|
-
}
|
|
2428
|
-
/** Parses a `YYYY-MM-DD` string and returns a local-midnight Date or null. */
|
|
2429
|
-
function parseDate(dateStr) {
|
|
2430
|
-
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(dateStr);
|
|
2431
|
-
if (!match)
|
|
2432
|
-
return null;
|
|
2433
|
-
const [, y, mo, d] = match.map(Number);
|
|
2434
|
-
const result = new Date(y, mo - 1, d);
|
|
2435
|
-
return isValidDate$1(result) ? result : null;
|
|
2436
|
-
}
|
|
2437
|
-
function localeDate(dateStr) {
|
|
2438
|
-
const date = new Date(dateStr);
|
|
2439
|
-
date.toLocaleString('en-US');
|
|
2440
|
-
}
|
|
2441
|
-
|
|
2442
|
-
var date_util = /*#__PURE__*/Object.freeze({
|
|
2443
|
-
__proto__: null,
|
|
2444
|
-
addDays: addDays,
|
|
2445
|
-
addHours: addHours,
|
|
2446
|
-
addMinutes: addMinutes,
|
|
2447
|
-
addMonths: addMonths,
|
|
2448
|
-
addYears: addYears,
|
|
2449
|
-
daysInMonth: daysInMonth,
|
|
2450
|
-
diffInDays: diffInDays,
|
|
2451
|
-
diffInMinutes: diffInMinutes,
|
|
2452
|
-
diffInMonths: diffInMonths,
|
|
2453
|
-
diffInYears: diffInYears,
|
|
2454
|
-
endOfDay: endOfDay,
|
|
2455
|
-
endOfMonth: endOfMonth,
|
|
2456
|
-
endOfYear: endOfYear,
|
|
2457
|
-
formatDate: formatDate,
|
|
2458
|
-
getAge: getAge,
|
|
2459
|
-
getWeekNumber: getWeekNumber,
|
|
2460
|
-
inRange: inRange$2,
|
|
2461
|
-
isAfter: isAfter,
|
|
2462
|
-
isBefore: isBefore,
|
|
2463
|
-
isFuture: isFuture,
|
|
2464
|
-
isLeapYear: isLeapYear,
|
|
2465
|
-
isPast: isPast,
|
|
2466
|
-
isSameDay: isSameDay,
|
|
2467
|
-
isToday: isToday,
|
|
2468
|
-
isValidDate: isValidDate$1,
|
|
2469
|
-
isWeekend: isWeekend,
|
|
2470
|
-
localeDate: localeDate,
|
|
2471
|
-
nextWorkday: nextWorkday,
|
|
2472
|
-
now: now,
|
|
2473
|
-
parseDate: parseDate,
|
|
2474
|
-
startOfDay: startOfDay,
|
|
2475
|
-
startOfMonth: startOfMonth,
|
|
2476
|
-
startOfYear: startOfYear,
|
|
2477
|
-
subtractDays: subtractDays,
|
|
2478
|
-
subtractMonths: subtractMonths,
|
|
2479
|
-
subtractYears: subtractYears,
|
|
2480
|
-
timeAgo: timeAgo,
|
|
2481
|
-
toDateOnly: toDateOnly,
|
|
2482
|
-
toISOString: toISOString,
|
|
2483
|
-
today: today
|
|
2484
|
-
});
|
|
2485
|
-
|
|
2486
|
-
class OslDatetimepicker {
|
|
2487
|
-
dtNativeInput;
|
|
2488
|
-
label = '';
|
|
2489
|
-
required = false;
|
|
2490
|
-
disabled = false;
|
|
2491
|
-
dateModel = null;
|
|
2492
|
-
set model(val) {
|
|
2493
|
-
this.dateModel = this._parseToDate(val);
|
|
2494
|
-
this._scheduleDisplayUpdate();
|
|
2495
|
-
}
|
|
2496
|
-
placeholder = '';
|
|
2497
|
-
minDate = null;
|
|
2498
|
-
maxDate = null;
|
|
2499
|
-
set minDatetime(val) {
|
|
2500
|
-
this.minDate = this._parseToDate(val);
|
|
2501
|
-
}
|
|
2502
|
-
set maxDatetime(val) {
|
|
2503
|
-
this.maxDate = this._parseToDate(val);
|
|
2504
|
-
}
|
|
2505
|
-
/** Emitted string format. Tokens: YYYY MM DD HH mm ss ddd dddd etc. Default: 'YYYY-MM-DDTHH:mm' */
|
|
2506
|
-
format = 'YYYY-MM-DDTHH:mm';
|
|
2507
|
-
/** Display format shown inside the input field. Default: 'ddd DD/MM/YYYY HH:mm' → Sun 18/01/2026 19:18 */
|
|
2508
|
-
set displayFormatInput(val) {
|
|
2509
|
-
this._displayFormat = val || 'ddd DD/MM/YYYY HH:mm';
|
|
2510
|
-
this._scheduleDisplayUpdate();
|
|
2511
|
-
}
|
|
2512
|
-
_displayFormat = 'ddd DD/MM/YYYY HH:mm';
|
|
2513
|
-
showSeconds = false;
|
|
2514
|
-
enableMeridian = false;
|
|
2515
|
-
skeletonLoading = false;
|
|
2516
|
-
skeletonTheme = 'light';
|
|
2517
|
-
modelChange = new EventEmitter();
|
|
2518
|
-
changeEv = new EventEmitter();
|
|
2519
|
-
ngAfterViewInit() {
|
|
2520
|
-
this._updateDisplay();
|
|
2521
|
-
}
|
|
2522
|
-
onDateChange(date) {
|
|
2523
|
-
this.dateModel = date;
|
|
2524
|
-
this._updateDisplay();
|
|
2525
|
-
const str = date ? formatDate(date, this.format) : '';
|
|
2526
|
-
this.modelChange.emit(str);
|
|
2527
|
-
this.changeEv.emit(str);
|
|
2528
|
-
}
|
|
2529
|
-
_updateDisplay() {
|
|
2530
|
-
if (!this.dtNativeInput)
|
|
2531
|
-
return;
|
|
2532
|
-
this.dtNativeInput.nativeElement.value =
|
|
2533
|
-
this.dateModel ? formatDate(this.dateModel, this._displayFormat) : '';
|
|
2534
|
-
}
|
|
2535
|
-
_scheduleDisplayUpdate() {
|
|
2536
|
-
setTimeout(() => this._updateDisplay());
|
|
2537
|
-
}
|
|
2538
|
-
_parseToDate(val) {
|
|
2539
|
-
if (!val)
|
|
2540
|
-
return null;
|
|
2541
|
-
const d = new Date(val);
|
|
2542
|
-
return isNaN(d.getTime()) ? null : d;
|
|
2543
|
-
}
|
|
2544
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslDatetimepicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2545
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslDatetimepicker, isStandalone: false, selector: "osl-datetimepicker", inputs: { label: "label", required: "required", disabled: "disabled", model: "model", placeholder: "placeholder", minDatetime: "minDatetime", maxDatetime: "maxDatetime", format: "format", displayFormatInput: ["displayFormat", "displayFormatInput"], showSeconds: "showSeconds", enableMeridian: "enableMeridian", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, providers: [
|
|
2546
|
-
{
|
|
2547
|
-
provide: MAT_DATE_FORMATS,
|
|
2548
|
-
useValue: {
|
|
2549
|
-
parse: {
|
|
2550
|
-
dateInput: 'DD-MMM-YYYY hh:mm a',
|
|
2551
|
-
},
|
|
2552
|
-
display: {
|
|
2553
|
-
dateInput: 'DD-MMM-YYYY hh:mm a',
|
|
2554
|
-
monthYearLabel: 'MMM YYYY', dateA11yLabel: 'LL', monthYearA11yLabel: 'MMMM YYYY',
|
|
2555
|
-
}
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2558
|
-
], viewQueries: [{ propertyName: "dtNativeInput", first: true, predicate: ["dtNativeInput"], descendants: true }], ngImport: i0, template: "<div class=\"d-flex flex-column\">\n @if (label) {\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n <label class=\"label\" [class.txt-clr-red]=\"dtField.touched && dtField.invalid\">\n {{label}} <span class=\"txt-clr-red\">{{required ? '*' : ''}}</span>\n </label>\n </div>\n }\n <div class=\"input-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n <input\n class=\"dt-input\"\n [ngxMatDatetimePicker]=\"picker\"\n [ngModel]=\"dateModel\"\n (ngModelChange)=\"onDateChange($event)\"\n [required]=\"required\"\n [disabled]=\"disabled\"\n [min]=\"minDate\"\n [max]=\"maxDate\"\n [placeholder]=\"placeholder\"\n [class.error]=\"dtField.touched && dtField.invalid\"\n #dtField=\"ngModel\"\n readonly\n />\n <button\n type=\"button\"\n class=\"dt-icon-btn\"\n (click)=\"picker.open()\"\n [disabled]=\"disabled\"\n tabindex=\"-1\">\n <svg viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"4\" width=\"14\" height=\"14\" rx=\"2\"/>\n <line x1=\"3\" y1=\"8\" x2=\"17\" y2=\"8\"/>\n <line x1=\"7\" y1=\"2\" x2=\"7\" y2=\"6\"/>\n <line x1=\"13\" y1=\"2\" x2=\"13\" y2=\"6\"/>\n </svg>\n </button>\n <ngx-mat-datetime-picker\n #picker\n [showSpinners]=\"true\"\n [showSeconds]=\"showSeconds\"\n [enableMeridian]=\"enableMeridian\">\n </ngx-mat-datetime-picker>\n </div>\n @if (dtField.touched && dtField.invalid && required) {\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\n }\n</div>\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px}.txt-clr-red{color:var(--osl-error-color)}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.input-wrapper{position:relative;display:flex;align-items:center}.dt-icon-btn{position:absolute;right:7px;top:50%;transform:translateY(-50%);width:22px;height:22px;padding:0;border:none;background:transparent;cursor:pointer;color:#9ca3af;display:flex;align-items:center;justify-content:center;transition:color .15s;border-radius:4px}.dt-icon-btn svg{width:16px;height:16px;display:block}.dt-icon-btn:hover:not(:disabled){color:var(--osl-focus-border-color)}.dt-icon-btn:disabled{opacity:.35;cursor:not-allowed}.dt-input{height:var(--osl-control-height);width:100%;border-radius:var(--osl-border-radius);outline:none;border:1px solid var(--osl-border-color);padding:0 38px 0 10px;font-size:var(--osl-text-font-size);font-family:inherit;color:#333;background-color:#fff;transition:border-color .2s,box-shadow .2s;letter-spacing:.01em;box-sizing:border-box}.dt-input::placeholder{color:#bbb}.dt-input:hover:not(:disabled){border-color:var(--osl-focus-border-color)}.dt-input:focus{border-color:var(--osl-focus-border-color);box-shadow:0 0 0 3px var(--osl-focus-shadow, rgba(99, 102, 241, .14))}.dt-input:disabled{background-color:#f5f5f5;cursor:not-allowed;opacity:.7}.dt-input.error{border-color:var(--osl-error-color)}.dt-input.error:focus{box-shadow:0 0 0 3px var(--osl-error-shadow, rgba(220, 38, 38, .14))}\n"], dependencies: [{ kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$1.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: OslSkeletonDirective, selector: "[oslSkeleton]", inputs: ["oslSkeleton", "oslSkeletonType", "oslSkeletonAnimation", "oslSkeletonTheme", "oslSkeletonColor", "oslSkeletonHighlight", "oslSkeletonRadius", "oslSkeletonRows", "oslSkeletonRowGap", "oslSkeletonZIndex", "oslSkeletonDelay", "oslSkeletonDuration", "oslSkeletonMinHeight", "oslSkeletonForceReread", "oslSkeletonCircleSize", "oslSkeletonListItems", "oslSkeletonTableRows", "oslSkeletonTableCols", "oslSkeletonCardLines", "oslSkeletonBgColor"] }, { kind: "component", type: i4.NgxMatDatetimepicker, selector: "ngx-mat-datetime-picker", exportAs: ["ngxMatDatetimePicker"] }, { kind: "directive", type: i4.NgxMatDatepickerInput, selector: "input[ngxMatDatetimePicker]", inputs: ["ngxMatDatetimePicker", "min", "max", "matDatepickerFilter"], exportAs: ["ngxMatDatepickerInput"] }] });
|
|
2559
|
-
}
|
|
2560
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslDatetimepicker, decorators: [{
|
|
2561
|
-
type: Component,
|
|
2562
|
-
args: [{ selector: 'osl-datetimepicker', standalone: false, providers: [
|
|
2563
|
-
{
|
|
2564
|
-
provide: MAT_DATE_FORMATS,
|
|
2565
|
-
useValue: {
|
|
2566
|
-
parse: {
|
|
2567
|
-
dateInput: 'DD-MMM-YYYY hh:mm a',
|
|
2568
|
-
},
|
|
2569
|
-
display: {
|
|
2570
|
-
dateInput: 'DD-MMM-YYYY hh:mm a',
|
|
2571
|
-
monthYearLabel: 'MMM YYYY', dateA11yLabel: 'LL', monthYearA11yLabel: 'MMMM YYYY',
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
2574
|
-
}
|
|
2575
|
-
], template: "<div class=\"d-flex flex-column\">\n @if (label) {\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n <label class=\"label\" [class.txt-clr-red]=\"dtField.touched && dtField.invalid\">\n {{label}} <span class=\"txt-clr-red\">{{required ? '*' : ''}}</span>\n </label>\n </div>\n }\n <div class=\"input-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n <input\n class=\"dt-input\"\n [ngxMatDatetimePicker]=\"picker\"\n [ngModel]=\"dateModel\"\n (ngModelChange)=\"onDateChange($event)\"\n [required]=\"required\"\n [disabled]=\"disabled\"\n [min]=\"minDate\"\n [max]=\"maxDate\"\n [placeholder]=\"placeholder\"\n [class.error]=\"dtField.touched && dtField.invalid\"\n #dtField=\"ngModel\"\n readonly\n />\n <button\n type=\"button\"\n class=\"dt-icon-btn\"\n (click)=\"picker.open()\"\n [disabled]=\"disabled\"\n tabindex=\"-1\">\n <svg viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"4\" width=\"14\" height=\"14\" rx=\"2\"/>\n <line x1=\"3\" y1=\"8\" x2=\"17\" y2=\"8\"/>\n <line x1=\"7\" y1=\"2\" x2=\"7\" y2=\"6\"/>\n <line x1=\"13\" y1=\"2\" x2=\"13\" y2=\"6\"/>\n </svg>\n </button>\n <ngx-mat-datetime-picker\n #picker\n [showSpinners]=\"true\"\n [showSeconds]=\"showSeconds\"\n [enableMeridian]=\"enableMeridian\">\n </ngx-mat-datetime-picker>\n </div>\n @if (dtField.touched && dtField.invalid && required) {\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\n }\n</div>\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px}.txt-clr-red{color:var(--osl-error-color)}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.input-wrapper{position:relative;display:flex;align-items:center}.dt-icon-btn{position:absolute;right:7px;top:50%;transform:translateY(-50%);width:22px;height:22px;padding:0;border:none;background:transparent;cursor:pointer;color:#9ca3af;display:flex;align-items:center;justify-content:center;transition:color .15s;border-radius:4px}.dt-icon-btn svg{width:16px;height:16px;display:block}.dt-icon-btn:hover:not(:disabled){color:var(--osl-focus-border-color)}.dt-icon-btn:disabled{opacity:.35;cursor:not-allowed}.dt-input{height:var(--osl-control-height);width:100%;border-radius:var(--osl-border-radius);outline:none;border:1px solid var(--osl-border-color);padding:0 38px 0 10px;font-size:var(--osl-text-font-size);font-family:inherit;color:#333;background-color:#fff;transition:border-color .2s,box-shadow .2s;letter-spacing:.01em;box-sizing:border-box}.dt-input::placeholder{color:#bbb}.dt-input:hover:not(:disabled){border-color:var(--osl-focus-border-color)}.dt-input:focus{border-color:var(--osl-focus-border-color);box-shadow:0 0 0 3px var(--osl-focus-shadow, rgba(99, 102, 241, .14))}.dt-input:disabled{background-color:#f5f5f5;cursor:not-allowed;opacity:.7}.dt-input.error{border-color:var(--osl-error-color)}.dt-input.error:focus{box-shadow:0 0 0 3px var(--osl-error-shadow, rgba(220, 38, 38, .14))}\n"] }]
|
|
2576
|
-
}], propDecorators: { dtNativeInput: [{
|
|
2577
|
-
type: ViewChild,
|
|
2578
|
-
args: ['dtNativeInput']
|
|
2579
|
-
}], label: [{
|
|
2580
|
-
type: Input,
|
|
2581
|
-
args: ['label']
|
|
2582
|
-
}], required: [{
|
|
2583
|
-
type: Input,
|
|
2584
|
-
args: ['required']
|
|
2585
|
-
}], disabled: [{
|
|
2586
|
-
type: Input,
|
|
2587
|
-
args: ['disabled']
|
|
2588
|
-
}], model: [{
|
|
2589
|
-
type: Input,
|
|
2590
|
-
args: ['model']
|
|
2591
|
-
}], placeholder: [{
|
|
2592
|
-
type: Input,
|
|
2593
|
-
args: ['placeholder']
|
|
2594
|
-
}], minDatetime: [{
|
|
2595
|
-
type: Input,
|
|
2596
|
-
args: ['minDatetime']
|
|
2597
|
-
}], maxDatetime: [{
|
|
2598
|
-
type: Input,
|
|
2599
|
-
args: ['maxDatetime']
|
|
2600
|
-
}], format: [{
|
|
2601
|
-
type: Input,
|
|
2602
|
-
args: ['format']
|
|
2603
|
-
}], displayFormatInput: [{
|
|
2604
|
-
type: Input,
|
|
2605
|
-
args: ['displayFormat']
|
|
2606
|
-
}], showSeconds: [{
|
|
2607
|
-
type: Input,
|
|
2608
|
-
args: ['showSeconds']
|
|
2609
|
-
}], enableMeridian: [{
|
|
2610
|
-
type: Input,
|
|
2611
|
-
args: ['enableMeridian']
|
|
2612
|
-
}], skeletonLoading: [{
|
|
2613
|
-
type: Input,
|
|
2614
|
-
args: ['skeletonLoading']
|
|
2615
|
-
}], skeletonTheme: [{
|
|
2616
|
-
type: Input,
|
|
2617
|
-
args: ['skeletonTheme']
|
|
2618
|
-
}], modelChange: [{
|
|
2619
|
-
type: Output
|
|
2620
|
-
}], changeEv: [{
|
|
2621
|
-
type: Output
|
|
2622
|
-
}] } });
|
|
2623
|
-
|
|
2624
|
-
class OslCheckbox {
|
|
2625
|
-
checkboxEl;
|
|
2626
|
-
label = '';
|
|
2627
|
-
disabled = false;
|
|
2628
|
-
required = false;
|
|
2629
|
-
model = false;
|
|
2630
|
-
indeterminate = false;
|
|
2631
|
-
skeletonLoading = false;
|
|
2632
|
-
skeletonTheme = 'light';
|
|
2633
|
-
modelChange = new EventEmitter();
|
|
2634
|
-
changeEv = new EventEmitter();
|
|
2635
|
-
touched = false;
|
|
2636
|
-
get isInvalid() {
|
|
2637
|
-
return this.touched && this.required && !this.model;
|
|
2638
|
-
}
|
|
2639
|
-
ngOnChanges(changes) {
|
|
2640
|
-
if (changes['indeterminate'] && this.checkboxEl) {
|
|
2641
|
-
this.checkboxEl.nativeElement.indeterminate = this.indeterminate;
|
|
2228
|
+
class OslCheckbox {
|
|
2229
|
+
checkboxEl;
|
|
2230
|
+
label = '';
|
|
2231
|
+
disabled = false;
|
|
2232
|
+
required = false;
|
|
2233
|
+
model = false;
|
|
2234
|
+
indeterminate = false;
|
|
2235
|
+
skeletonLoading = false;
|
|
2236
|
+
skeletonTheme = 'light';
|
|
2237
|
+
modelChange = new EventEmitter();
|
|
2238
|
+
changeEv = new EventEmitter();
|
|
2239
|
+
touched = false;
|
|
2240
|
+
get isInvalid() {
|
|
2241
|
+
return this.touched && this.required && !this.model;
|
|
2242
|
+
}
|
|
2243
|
+
ngOnChanges(changes) {
|
|
2244
|
+
if (changes['indeterminate'] && this.checkboxEl) {
|
|
2245
|
+
this.checkboxEl.nativeElement.indeterminate = this.indeterminate;
|
|
2642
2246
|
}
|
|
2643
2247
|
}
|
|
2644
2248
|
ngAfterViewInit() {
|
|
@@ -2653,7 +2257,7 @@ class OslCheckbox {
|
|
|
2653
2257
|
this.changeEv.emit(this.model);
|
|
2654
2258
|
}
|
|
2655
2259
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslCheckbox, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2656
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslCheckbox, isStandalone: false, selector: "osl-checkbox", inputs: { label: "label", disabled: "disabled", required: "required", model: "model", indeterminate: "indeterminate", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, viewQueries: [{ propertyName: "checkboxEl", first: true, predicate: ["checkboxEl"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"d-flex flex-column\">\r\n\r\n <label class=\"checkbox-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\" [class.checkbox-disabled]=\"disabled\" [class.checkbox-invalid]=\"isInvalid\">\r\n <input\r\n #checkboxEl\r\n type=\"checkbox\"\r\n [ngModel]=\"model\"\r\n (ngModelChange)=\"onModelChange($event)\"\r\n [disabled]=\"disabled\"\r\n >\r\n <span class=\"checkbox-label\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <span class=\"label-text\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required?'*':''}}</span>\r\n </span>\r\n </label>\r\n @if(isInvalid) {\r\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\r\n }\r\n</div>\r\n", styles: [".checkbox-wrapper{display:flex;align-items:center;gap:8px;cursor:pointer;font-size:var(--osl-label-font-size);width:fit-content;max-width:100%}.checkbox-wrapper input[type=checkbox]{width:16px;height:16px;cursor:pointer;accent-color:#333;flex-shrink:0}.checkbox-wrapper .checkbox-label{-webkit-user-select:none;user-select:none;display:flex;align-items:center;overflow:hidden;min-width:0}.checkbox-wrapper.checkbox-disabled{opacity:.6;cursor:not-allowed}.checkbox-wrapper.checkbox-disabled input[type=checkbox]{cursor:not-allowed}.checkbox-wrapper.checkbox-invalid{color:var(--osl-error-color)}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.txt-clr-red{color:var(--osl-error-color)}\n"], dependencies: [{ kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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: i2$
|
|
2260
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslCheckbox, isStandalone: false, selector: "osl-checkbox", inputs: { label: "label", disabled: "disabled", required: "required", model: "model", indeterminate: "indeterminate", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, viewQueries: [{ propertyName: "checkboxEl", first: true, predicate: ["checkboxEl"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"d-flex flex-column\">\r\n\r\n <label class=\"checkbox-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\" [class.checkbox-disabled]=\"disabled\" [class.checkbox-invalid]=\"isInvalid\">\r\n <input\r\n #checkboxEl\r\n type=\"checkbox\"\r\n [ngModel]=\"model\"\r\n (ngModelChange)=\"onModelChange($event)\"\r\n [disabled]=\"disabled\"\r\n >\r\n <span class=\"checkbox-label\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <span class=\"label-text\" [title]=\"label\">{{label}}</span><span class=\"txt-clr-red label-required\">{{required?'*':''}}</span>\r\n </span>\r\n </label>\r\n @if(isInvalid) {\r\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\r\n }\r\n</div>\r\n", styles: [".checkbox-wrapper{display:flex;align-items:center;gap:8px;cursor:pointer;font-size:var(--osl-label-font-size);width:fit-content;max-width:100%}.checkbox-wrapper input[type=checkbox]{width:16px;height:16px;cursor:pointer;accent-color:#333;flex-shrink:0}.checkbox-wrapper .checkbox-label{-webkit-user-select:none;user-select:none;display:flex;align-items:center;overflow:hidden;min-width:0}.checkbox-wrapper.checkbox-disabled{opacity:.6;cursor:not-allowed}.checkbox-wrapper.checkbox-disabled input[type=checkbox]{cursor:not-allowed}.checkbox-wrapper.checkbox-invalid{color:var(--osl-error-color)}.label-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.label-required{flex-shrink:0}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.txt-clr-red{color:var(--osl-error-color)}\n"], dependencies: [{ kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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: i2$2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: OslSkeletonDirective, selector: "[oslSkeleton]", inputs: ["oslSkeleton", "oslSkeletonType", "oslSkeletonAnimation", "oslSkeletonTheme", "oslSkeletonColor", "oslSkeletonHighlight", "oslSkeletonRadius", "oslSkeletonRows", "oslSkeletonRowGap", "oslSkeletonZIndex", "oslSkeletonDelay", "oslSkeletonDuration", "oslSkeletonMinHeight", "oslSkeletonForceReread", "oslSkeletonCircleSize", "oslSkeletonListItems", "oslSkeletonTableRows", "oslSkeletonTableCols", "oslSkeletonCardLines", "oslSkeletonBgColor"] }] });
|
|
2657
2261
|
}
|
|
2658
2262
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslCheckbox, decorators: [{
|
|
2659
2263
|
type: Component,
|
|
@@ -2747,147 +2351,584 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
2747
2351
|
type: Output
|
|
2748
2352
|
}] } });
|
|
2749
2353
|
|
|
2750
|
-
class DynamicForm {
|
|
2751
|
-
cdr;
|
|
2752
|
-
elements = [];
|
|
2753
|
-
_model;
|
|
2354
|
+
class DynamicForm {
|
|
2355
|
+
cdr;
|
|
2356
|
+
elements = [];
|
|
2357
|
+
_model;
|
|
2358
|
+
set model(val) {
|
|
2359
|
+
this._model = val;
|
|
2360
|
+
}
|
|
2361
|
+
get model() {
|
|
2362
|
+
return this._model;
|
|
2363
|
+
}
|
|
2364
|
+
skeletonLoading = false;
|
|
2365
|
+
skeletonTheme = 'light';
|
|
2366
|
+
modelChange = new EventEmitter();
|
|
2367
|
+
datasourceCache = inject(DatasourceCacheService);
|
|
2368
|
+
constructor(cdr) {
|
|
2369
|
+
this.cdr = cdr;
|
|
2370
|
+
}
|
|
2371
|
+
ngOnInit() {
|
|
2372
|
+
this.loadApiDatasources();
|
|
2373
|
+
}
|
|
2374
|
+
ngOnChanges(changes) {
|
|
2375
|
+
if (changes['elements'] || changes['model']) {
|
|
2376
|
+
this.loadApiDatasources();
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
loadApiDatasources() {
|
|
2380
|
+
this._loadForList(this.elements);
|
|
2381
|
+
}
|
|
2382
|
+
async _loadForList(list) {
|
|
2383
|
+
for (const elem of list) {
|
|
2384
|
+
if (elem.elementType === 'fieldset' && elem.rows?.length) {
|
|
2385
|
+
this._loadForList(elem.rows);
|
|
2386
|
+
}
|
|
2387
|
+
else if (elem.apiService && elem.apiMethod && (!elem.searchType || elem.searchType == 'Local')) {
|
|
2388
|
+
if (elem.dependsOn?.length) {
|
|
2389
|
+
const allReady = elem.dependsOn.every(k => this.model?.[k] != null);
|
|
2390
|
+
if (!allReady)
|
|
2391
|
+
continue;
|
|
2392
|
+
if (elem.datasource?.length)
|
|
2393
|
+
continue;
|
|
2394
|
+
}
|
|
2395
|
+
elem.loadingIf = () => true;
|
|
2396
|
+
const data = await this.datasourceCache.load(elem.apiService, elem.apiMethod, elem.apiBody ? elem.apiBody(this.model) : null);
|
|
2397
|
+
elem.loadingIf = () => false;
|
|
2398
|
+
this.cdr.markForCheck();
|
|
2399
|
+
if (data && data.length > 0) {
|
|
2400
|
+
elem.datasource = data;
|
|
2401
|
+
}
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
onModelChange(event, key) {
|
|
2406
|
+
this.model[key] = event;
|
|
2407
|
+
this.modelChange.emit(this.model);
|
|
2408
|
+
}
|
|
2409
|
+
onSelectChange(elem, value) {
|
|
2410
|
+
if (!elem.change)
|
|
2411
|
+
return;
|
|
2412
|
+
let selectedObj = undefined;
|
|
2413
|
+
if (elem.datasource) {
|
|
2414
|
+
if (Array.isArray(value)) {
|
|
2415
|
+
selectedObj = value.map(v => elem.datasource.find(item => (elem.valueField ? item[elem.valueField] : item) === v) ?? null);
|
|
2416
|
+
}
|
|
2417
|
+
else if (value !== null && value !== undefined) {
|
|
2418
|
+
selectedObj = elem.datasource.find(item => (elem.valueField ? item[elem.valueField] : item) === (isNaN(Number(value)) ? value : Number(value))) ?? null;
|
|
2419
|
+
}
|
|
2420
|
+
else {
|
|
2421
|
+
selectedObj = null;
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
elem.change(this.model, undefined, selectedObj);
|
|
2425
|
+
}
|
|
2426
|
+
onFieldChange(elem, value) {
|
|
2427
|
+
this.model[elem.key] = value;
|
|
2428
|
+
this.modelChange.emit(this.model);
|
|
2429
|
+
if (elem.change)
|
|
2430
|
+
this.onSelectChange(elem, value);
|
|
2431
|
+
this._refreshDependents(elem.key);
|
|
2432
|
+
}
|
|
2433
|
+
async _refreshDependents(changedKey) {
|
|
2434
|
+
const dependents = this._flatElements().filter(e => e.dependsOn?.includes(changedKey));
|
|
2435
|
+
for (const dep of dependents) {
|
|
2436
|
+
let valueCleared = false;
|
|
2437
|
+
// If any parent dependency is now null, clear field + datasource without API call
|
|
2438
|
+
const anyDepNull = dep.dependsOn.some(k => this.model[k] == null);
|
|
2439
|
+
if (anyDepNull) {
|
|
2440
|
+
if (this.model[dep.key] != null) {
|
|
2441
|
+
this.model[dep.key] = null;
|
|
2442
|
+
valueCleared = true;
|
|
2443
|
+
}
|
|
2444
|
+
dep.datasource = [];
|
|
2445
|
+
this.cdr.markForCheck();
|
|
2446
|
+
if (valueCleared)
|
|
2447
|
+
await this._refreshDependents(dep.key);
|
|
2448
|
+
continue;
|
|
2449
|
+
}
|
|
2450
|
+
const body = dep.apiBody ? dep.apiBody(this.model) : undefined;
|
|
2451
|
+
if (dep.searchType !== 'Api' && dep.apiService && dep.apiMethod) {
|
|
2452
|
+
dep.loadingIf = () => true;
|
|
2453
|
+
this.cdr.markForCheck();
|
|
2454
|
+
try {
|
|
2455
|
+
const res = await dep.apiService[dep.apiMethod](body);
|
|
2456
|
+
dep.datasource = Array.isArray(res) ? res : (res?.result ?? []);
|
|
2457
|
+
}
|
|
2458
|
+
finally {
|
|
2459
|
+
dep.loadingIf = () => false;
|
|
2460
|
+
}
|
|
2461
|
+
const cur = this.model[dep.key];
|
|
2462
|
+
if (cur != null) {
|
|
2463
|
+
const exists = dep.datasource.some(item => (dep.valueField ? item[dep.valueField] : item) === cur);
|
|
2464
|
+
if (!exists) {
|
|
2465
|
+
this.model[dep.key] = null;
|
|
2466
|
+
valueCleared = true;
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
else if (dep.searchType === 'Api') {
|
|
2471
|
+
if (this.model[dep.key] != null) {
|
|
2472
|
+
this.model[dep.key] = null;
|
|
2473
|
+
valueCleared = true;
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
this.cdr.markForCheck();
|
|
2477
|
+
if (valueCleared)
|
|
2478
|
+
await this._refreshDependents(dep.key);
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
_flatElements(list = this.elements) {
|
|
2482
|
+
return list.flatMap(e => e.rows?.length ? [e, ...this._flatElements(e.rows)] : [e]);
|
|
2483
|
+
}
|
|
2484
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DynamicForm, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
2485
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DynamicForm, isStandalone: false, selector: "osl-dynamic-form", inputs: { elements: "elements", model: "model", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange" }, usesOnChanges: true, ngImport: i0, template: "@if(elements && elements.length > 0) {\n <div class=\"row align-items-center w-100\">\n @for(elem of elements; track elem) {\n @if(!(elem.hideIf ? elem.hideIf(model) : elem.hide)) {\n <ng-container *ngTemplateOutlet=\"formField; context: { $implicit: elem }\"></ng-container>\n }\n }\n </div>\n}\n\n<ng-template #formField let-elem>\n @if(elem.hideIf ? !elem.hideIf(model) : true) {\n @switch (elem.elementType) {\n\n @case (\"textbox\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-input\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [label]=\"elem.label\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [type]=\"elem.inputType || 'text'\"\n [placeholder]=\"elem.placeholder || ''\"\n [mask]=\"elem.mask || ''\"\n [min]=\"elem.min ?? ''\"\n [max]=\"elem.max ?? ''\"\n [minLength]=\"elem.minLength ?? null\"\n [maxLength]=\"elem.maxLength ?? null\"\n [prefixIcon]=\"elem.prefixIcon || ''\"\n [suffixIcon]=\"elem.suffixIcon || ''\"\n [onlyChars]=\"!!elem.onlyChars\"\n [decimalPortion]=\"elem.decimalPortion ?? null\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n [isCapitalize]=\"elem.isCapitalize\"\n ></osl-input>\n </div>\n }\n\n @case (\"textarea\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-textarea\n [label]=\"elem.label\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [rows]=\"elem.textareaRows || 3\"\n [placeholder]=\"elem.placeholder || ''\"\n [maxLength]=\"elem.maxLength ?? null\"\n [minLength]=\"elem.minLength ?? null\"\n [characterCounter]=\"!!elem.characterCounter\"\n [resize]=\"elem.resize || 'none'\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-textarea>\n </div>\n }\n\n @case (\"select\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-select\n [label]=\"elem.label\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [datasource]=\"elem.datasource || []\"\n [displayField]=\"elem.displayField || ''\"\n [valueField]=\"elem.valueField || ''\"\n [placeholder]=\"elem.selectPlaceholder || 'Select...'\"\n [loading]=\"elem.loadingIf ? elem.loadingIf(model) : false\"\n [clearable]=\"!!elem.clearable\"\n (changeEv)=\"elem.change ? onSelectChange(elem, $event) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-select>\n </div>\n }\n\n @case (\"radio\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-radio\n [label]=\"elem.label\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [datasource]=\"elem.datasource || []\"\n [displayField]=\"elem.displayField || ''\"\n [valueField]=\"elem.valueField || ''\"\n [inline]=\"!!elem.inline\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-radio>\n </div>\n }\n\n @case (\"slide-toggle\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-slide-toggle\n [label]=\"elem.label\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [labelPosition]=\"elem.labelPosition || 'after'\"\n [trueLabel]=\"elem.trueLabel || ''\"\n [falseLabel]=\"elem.falseLabel || ''\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-slide-toggle>\n </div>\n }\n\n @case (\"autocomplete\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-autocomplete\n [label]=\"elem.label\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [datasource]=\"elem.datasource || []\"\n [displayField]=\"elem.displayField || ''\"\n [valueField]=\"elem.valueField || ''\"\n [placeholder]=\"elem.autocompletePlaceholder || 'Type to search...'\"\n [loading]=\"elem.loadingIf ? elem.loadingIf(model) : false\"\n (changeEv)=\"onFieldChange(elem, $event)\"\n [(model)]=\"model[elem.key]\"\n [methodName]=\"elem.apiMethod\"\n [service]=\"elem.apiService\"\n [object]=\"model[elem.objectName]\"\n [searchType]=\"elem.searchType\"\n [configMethodName]=\"elem.apiConfigMethod\"\n [isLister]=\"elem.isListerAutocomplete\"\n [apiBody]=\"elem.apiBody\"\n ></osl-autocomplete>\n </div>\n }\n\n @case (\"file-uploader\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-file-upload\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [label]=\"elem.label\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [accept]=\"elem.accept || ''\"\n [multiple]=\"!!elem.multiple\"\n [maxSize]=\"elem.maxFileSize || 0\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-file-upload>\n </div>\n }\n\n @case (\"datepicker\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-datepicker\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [label]=\"elem.label\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [dateType]=\"elem.dateType || 'date'\"\n [placeholder]=\"elem.placeholder || ''\"\n [minDate]=\"elem.minDateIf ? elem.minDateIf(model) : elem.minDate || ''\"\n [maxDate]=\"elem.maxDateIf ? elem.maxDateIf(model) : elem.maxDate || ''\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-datepicker>\n </div>\n }\n\n @case (\"checkbox\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-checkbox\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [label]=\"elem.label\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [indeterminate]=\"!!elem.indeterminate\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-checkbox>\n </div>\n }\n\n @case (\"button\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-button\n [label]=\"elem.label\"\n [loading]=\"elem.loadingIf ? elem.loadingIf(model) : false\"\n variant=\"secondary\"\n (clickEv)=\"elem.change ? elem.change(model) : null\"\n ></osl-button>\n </div>\n }\n\n @case (\"fieldset\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-3'\">\n <fieldset class=\"osl-fieldset\">\n @if(elem.label) {\n <legend class=\"osl-fieldset-legend\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n <span class=\"osl-fieldset-legend__text\">{{ elem.label }}</span>\n </legend>\n }\n <div class=\"row w-100 osl-fieldset-body\">\n @for(innerElem of elem.rows; track innerElem) {\n @if(!(innerElem.hideIf ? innerElem.hideIf(model) : innerElem.hide)) {\n <ng-container *ngTemplateOutlet=\"formField; context: { $implicit: innerElem }\"></ng-container>\n }\n }\n </div>\n </fieldset>\n </div>\n }\n\n @case (\"templateRef\"){\n <div [class]=\"'col-md-'+elem.columns+' mt-3'\">\n <ng-container *ngTemplateOutlet=\"elem.templateRef; context: { $implicit: elem}\"></ng-container>\n </div>\n }\n\n @case (\"spacer\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n </div>\n }\n\n }\n }\n</ng-template>\n", styles: [".osl-fieldset{border:1.5px solid var(--osl-border-color, #e5e7eb);border-radius:10px;padding:0 16px 16px;margin:0;background:linear-gradient(135deg,#f9fafb,#fff);box-shadow:0 1px 4px #0000000d;position:relative;transition:box-shadow .2s}.osl-fieldset:focus-within{box-shadow:0 0 0 3px #6366f114,0 2px 8px #0000000f;border-color:var(--osl-primary, #6366f1)}.osl-fieldset-legend{padding:0 6px;margin-left:8px;line-height:1;float:none;width:auto}.osl-fieldset-legend__text{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;background:#fff;padding:2px 10px;border:1.5px solid var(--osl-border-color, #e5e7eb);box-shadow:0 1px 3px #6366f11a}.osl-fieldset-body{margin-top:4px}\n"], dependencies: [{ kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: OslSkeletonDirective, selector: "[oslSkeleton]", inputs: ["oslSkeleton", "oslSkeletonType", "oslSkeletonAnimation", "oslSkeletonTheme", "oslSkeletonColor", "oslSkeletonHighlight", "oslSkeletonRadius", "oslSkeletonRows", "oslSkeletonRowGap", "oslSkeletonZIndex", "oslSkeletonDelay", "oslSkeletonDuration", "oslSkeletonMinHeight", "oslSkeletonForceReread", "oslSkeletonCircleSize", "oslSkeletonListItems", "oslSkeletonTableRows", "oslSkeletonTableCols", "oslSkeletonCardLines", "oslSkeletonBgColor"] }, { kind: "component", type: Oslinput, selector: "osl-input", inputs: ["label", "required", "disabled", "model", "type", "placeholder", "mask", "min", "max", "minLength", "maxLength", "prefixIcon", "suffixIcon", "skeletonLoading", "skeletonTheme", "onlyChars", "isCapitalize", "decimalPortion"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: Osltextarea, selector: "osl-textarea", inputs: ["label", "rows", "required", "disabled", "model", "placeholder", "maxLength", "minLength", "characterCounter", "resize", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslSelect, selector: "osl-select", inputs: ["label", "required", "disabled", "model", "datasource", "displayField", "valueField", "placeholder", "loading", "clearable", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslRadio, selector: "osl-radio", inputs: ["label", "required", "disabled", "model", "datasource", "displayField", "valueField", "inline", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslSlideToggle, selector: "osl-slide-toggle", inputs: ["label", "disabled", "model", "labelPosition", "trueLabel", "falseLabel", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslAutocomplete, selector: "osl-autocomplete", inputs: ["label", "required", "disabled", "model", "datasource", "displayField", "valueField", "placeholder", "loading", "searchType", "methodName", "configMethodName", "service", "object", "skeletonLoading", "skeletonTheme", "isLister", "apiBody"], outputs: ["datasourceChange", "modelChange", "changeEv"] }, { kind: "component", type: OslFileUpload, selector: "osl-file-upload", inputs: ["label", "required", "disabled", "model", "accept", "multiple", "maxSize", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslDatepicker, selector: "osl-datepicker", inputs: ["label", "required", "disabled", "model", "dateType", "placeholder", "minDate", "maxDate", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslCheckbox, selector: "osl-checkbox", inputs: ["label", "disabled", "required", "model", "indeterminate", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslButton, selector: "osl-button", inputs: ["label", "icon", "variant", "size", "disabled", "loading", "type", "fullWidth"], outputs: ["clickEv"] }] });
|
|
2486
|
+
}
|
|
2487
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DynamicForm, decorators: [{
|
|
2488
|
+
type: Component,
|
|
2489
|
+
args: [{ selector: 'osl-dynamic-form', standalone: false, template: "@if(elements && elements.length > 0) {\n <div class=\"row align-items-center w-100\">\n @for(elem of elements; track elem) {\n @if(!(elem.hideIf ? elem.hideIf(model) : elem.hide)) {\n <ng-container *ngTemplateOutlet=\"formField; context: { $implicit: elem }\"></ng-container>\n }\n }\n </div>\n}\n\n<ng-template #formField let-elem>\n @if(elem.hideIf ? !elem.hideIf(model) : true) {\n @switch (elem.elementType) {\n\n @case (\"textbox\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-input\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [label]=\"elem.label\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [type]=\"elem.inputType || 'text'\"\n [placeholder]=\"elem.placeholder || ''\"\n [mask]=\"elem.mask || ''\"\n [min]=\"elem.min ?? ''\"\n [max]=\"elem.max ?? ''\"\n [minLength]=\"elem.minLength ?? null\"\n [maxLength]=\"elem.maxLength ?? null\"\n [prefixIcon]=\"elem.prefixIcon || ''\"\n [suffixIcon]=\"elem.suffixIcon || ''\"\n [onlyChars]=\"!!elem.onlyChars\"\n [decimalPortion]=\"elem.decimalPortion ?? null\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n [isCapitalize]=\"elem.isCapitalize\"\n ></osl-input>\n </div>\n }\n\n @case (\"textarea\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-textarea\n [label]=\"elem.label\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [rows]=\"elem.textareaRows || 3\"\n [placeholder]=\"elem.placeholder || ''\"\n [maxLength]=\"elem.maxLength ?? null\"\n [minLength]=\"elem.minLength ?? null\"\n [characterCounter]=\"!!elem.characterCounter\"\n [resize]=\"elem.resize || 'none'\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-textarea>\n </div>\n }\n\n @case (\"select\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-select\n [label]=\"elem.label\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [datasource]=\"elem.datasource || []\"\n [displayField]=\"elem.displayField || ''\"\n [valueField]=\"elem.valueField || ''\"\n [placeholder]=\"elem.selectPlaceholder || 'Select...'\"\n [loading]=\"elem.loadingIf ? elem.loadingIf(model) : false\"\n [clearable]=\"!!elem.clearable\"\n (changeEv)=\"elem.change ? onSelectChange(elem, $event) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-select>\n </div>\n }\n\n @case (\"radio\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-radio\n [label]=\"elem.label\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [datasource]=\"elem.datasource || []\"\n [displayField]=\"elem.displayField || ''\"\n [valueField]=\"elem.valueField || ''\"\n [inline]=\"!!elem.inline\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-radio>\n </div>\n }\n\n @case (\"slide-toggle\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-slide-toggle\n [label]=\"elem.label\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [labelPosition]=\"elem.labelPosition || 'after'\"\n [trueLabel]=\"elem.trueLabel || ''\"\n [falseLabel]=\"elem.falseLabel || ''\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-slide-toggle>\n </div>\n }\n\n @case (\"autocomplete\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-autocomplete\n [label]=\"elem.label\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [datasource]=\"elem.datasource || []\"\n [displayField]=\"elem.displayField || ''\"\n [valueField]=\"elem.valueField || ''\"\n [placeholder]=\"elem.autocompletePlaceholder || 'Type to search...'\"\n [loading]=\"elem.loadingIf ? elem.loadingIf(model) : false\"\n (changeEv)=\"onFieldChange(elem, $event)\"\n [(model)]=\"model[elem.key]\"\n [methodName]=\"elem.apiMethod\"\n [service]=\"elem.apiService\"\n [object]=\"model[elem.objectName]\"\n [searchType]=\"elem.searchType\"\n [configMethodName]=\"elem.apiConfigMethod\"\n [isLister]=\"elem.isListerAutocomplete\"\n [apiBody]=\"elem.apiBody\"\n ></osl-autocomplete>\n </div>\n }\n\n @case (\"file-uploader\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-file-upload\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [label]=\"elem.label\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [accept]=\"elem.accept || ''\"\n [multiple]=\"!!elem.multiple\"\n [maxSize]=\"elem.maxFileSize || 0\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-file-upload>\n </div>\n }\n\n @case (\"datepicker\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-datepicker\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [label]=\"elem.label\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [dateType]=\"elem.dateType || 'date'\"\n [placeholder]=\"elem.placeholder || ''\"\n [minDate]=\"elem.minDateIf ? elem.minDateIf(model) : elem.minDate || ''\"\n [maxDate]=\"elem.maxDateIf ? elem.maxDateIf(model) : elem.maxDate || ''\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-datepicker>\n </div>\n }\n\n @case (\"checkbox\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-checkbox\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n [label]=\"elem.label\"\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\n [indeterminate]=\"!!elem.indeterminate\"\n (changeEv)=\"elem.change ? elem.change(model) : null\"\n [model]=\"model[elem.key]\"\n (modelChange)=\"onModelChange($event, elem.key)\"\n ></osl-checkbox>\n </div>\n }\n\n @case (\"button\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n <osl-button\n [label]=\"elem.label\"\n [loading]=\"elem.loadingIf ? elem.loadingIf(model) : false\"\n variant=\"secondary\"\n (clickEv)=\"elem.change ? elem.change(model) : null\"\n ></osl-button>\n </div>\n }\n\n @case (\"fieldset\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-3'\">\n <fieldset class=\"osl-fieldset\">\n @if(elem.label) {\n <legend class=\"osl-fieldset-legend\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n <span class=\"osl-fieldset-legend__text\">{{ elem.label }}</span>\n </legend>\n }\n <div class=\"row w-100 osl-fieldset-body\">\n @for(innerElem of elem.rows; track innerElem) {\n @if(!(innerElem.hideIf ? innerElem.hideIf(model) : innerElem.hide)) {\n <ng-container *ngTemplateOutlet=\"formField; context: { $implicit: innerElem }\"></ng-container>\n }\n }\n </div>\n </fieldset>\n </div>\n }\n\n @case (\"templateRef\"){\n <div [class]=\"'col-md-'+elem.columns+' mt-3'\">\n <ng-container *ngTemplateOutlet=\"elem.templateRef; context: { $implicit: elem}\"></ng-container>\n </div>\n }\n\n @case (\"spacer\") {\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\n </div>\n }\n\n }\n }\n</ng-template>\n", styles: [".osl-fieldset{border:1.5px solid var(--osl-border-color, #e5e7eb);border-radius:10px;padding:0 16px 16px;margin:0;background:linear-gradient(135deg,#f9fafb,#fff);box-shadow:0 1px 4px #0000000d;position:relative;transition:box-shadow .2s}.osl-fieldset:focus-within{box-shadow:0 0 0 3px #6366f114,0 2px 8px #0000000f;border-color:var(--osl-primary, #6366f1)}.osl-fieldset-legend{padding:0 6px;margin-left:8px;line-height:1;float:none;width:auto}.osl-fieldset-legend__text{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;background:#fff;padding:2px 10px;border:1.5px solid var(--osl-border-color, #e5e7eb);box-shadow:0 1px 3px #6366f11a}.osl-fieldset-body{margin-top:4px}\n"] }]
|
|
2490
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { elements: [{
|
|
2491
|
+
type: Input,
|
|
2492
|
+
args: ['elements']
|
|
2493
|
+
}], model: [{
|
|
2494
|
+
type: Input,
|
|
2495
|
+
args: ['model']
|
|
2496
|
+
}], skeletonLoading: [{
|
|
2497
|
+
type: Input,
|
|
2498
|
+
args: ['skeletonLoading']
|
|
2499
|
+
}], skeletonTheme: [{
|
|
2500
|
+
type: Input,
|
|
2501
|
+
args: ['skeletonTheme']
|
|
2502
|
+
}], modelChange: [{
|
|
2503
|
+
type: Output
|
|
2504
|
+
}] } });
|
|
2505
|
+
|
|
2506
|
+
// ─── Date Utilities ───────────────────────────────────────────────────────────
|
|
2507
|
+
const MONTH_NAMES_SHORT = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
2508
|
+
const MONTH_NAMES_FULL = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
|
2509
|
+
const DAY_NAMES_SHORT = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
2510
|
+
const DAY_NAMES_FULL = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
|
2511
|
+
function pad(n) {
|
|
2512
|
+
return String(n).padStart(2, '0');
|
|
2513
|
+
}
|
|
2514
|
+
/** Returns the current date and time. */
|
|
2515
|
+
function now() {
|
|
2516
|
+
return new Date();
|
|
2517
|
+
}
|
|
2518
|
+
/** Returns today's date with time set to midnight. */
|
|
2519
|
+
function today() {
|
|
2520
|
+
return startOfDay(new Date());
|
|
2521
|
+
}
|
|
2522
|
+
/** Returns true if the value is a valid Date object. */
|
|
2523
|
+
function isValidDate$1(value) {
|
|
2524
|
+
return value instanceof Date && !isNaN(value.getTime());
|
|
2525
|
+
}
|
|
2526
|
+
/**
|
|
2527
|
+
* Formats a Date using a pattern string.
|
|
2528
|
+
* Tokens: YYYY, YY, MM, M, DD, D, HH, H, mm, m, ss, s,
|
|
2529
|
+
* MMM (short month), MMMM (full month),
|
|
2530
|
+
* ddd (short day), dddd (full day).
|
|
2531
|
+
*/
|
|
2532
|
+
function formatDate(date, pattern) {
|
|
2533
|
+
const d = new Date(date);
|
|
2534
|
+
if (!isValidDate$1(d))
|
|
2535
|
+
return '';
|
|
2536
|
+
const year = d.getFullYear();
|
|
2537
|
+
const month = d.getMonth();
|
|
2538
|
+
const day = d.getDate();
|
|
2539
|
+
const hours = d.getHours();
|
|
2540
|
+
const mins = d.getMinutes();
|
|
2541
|
+
const secs = d.getSeconds();
|
|
2542
|
+
const dayOfWeek = d.getDay();
|
|
2543
|
+
return pattern
|
|
2544
|
+
.replace('YYYY', String(year))
|
|
2545
|
+
.replace('YY', String(year).slice(-2))
|
|
2546
|
+
.replace('MMMM', MONTH_NAMES_FULL[month])
|
|
2547
|
+
.replace('MMM', MONTH_NAMES_SHORT[month])
|
|
2548
|
+
.replace('MM', pad(month + 1))
|
|
2549
|
+
.replace('M', String(month + 1))
|
|
2550
|
+
.replace('dddd', DAY_NAMES_FULL[dayOfWeek])
|
|
2551
|
+
.replace('ddd', DAY_NAMES_SHORT[dayOfWeek])
|
|
2552
|
+
.replace('DD', pad(day))
|
|
2553
|
+
.replace('D', String(day))
|
|
2554
|
+
.replace('HH', pad(hours))
|
|
2555
|
+
.replace('H', String(hours))
|
|
2556
|
+
.replace('mm', pad(mins))
|
|
2557
|
+
.replace('m', String(mins))
|
|
2558
|
+
.replace('ss', pad(secs))
|
|
2559
|
+
.replace('s', String(secs));
|
|
2560
|
+
}
|
|
2561
|
+
/** Returns date as `YYYY-MM-DD`. */
|
|
2562
|
+
function toDateOnly(date) {
|
|
2563
|
+
return formatDate(date, 'YYYY-MM-DD');
|
|
2564
|
+
}
|
|
2565
|
+
/** Returns the ISO 8601 string for a date. */
|
|
2566
|
+
function toISOString(date) {
|
|
2567
|
+
return date.toISOString();
|
|
2568
|
+
}
|
|
2569
|
+
/** Returns a new date with `days` added. */
|
|
2570
|
+
function addDays(date, days) {
|
|
2571
|
+
const result = new Date(date);
|
|
2572
|
+
result.setDate(result.getDate() + days);
|
|
2573
|
+
return result;
|
|
2574
|
+
}
|
|
2575
|
+
/** Returns a new date with `months` added. */
|
|
2576
|
+
function addMonths(date, months) {
|
|
2577
|
+
const result = new Date(date);
|
|
2578
|
+
result.setMonth(result.getMonth() + months);
|
|
2579
|
+
return result;
|
|
2580
|
+
}
|
|
2581
|
+
/** Returns a new date with `years` added. */
|
|
2582
|
+
function addYears(date, years) {
|
|
2583
|
+
const result = new Date(date);
|
|
2584
|
+
result.setFullYear(result.getFullYear() + years);
|
|
2585
|
+
return result;
|
|
2586
|
+
}
|
|
2587
|
+
/** Returns a new date with `hours` added. */
|
|
2588
|
+
function addHours(date, hours) {
|
|
2589
|
+
return new Date(date.getTime() + hours * 3_600_000);
|
|
2590
|
+
}
|
|
2591
|
+
/** Returns a new date with `minutes` added. */
|
|
2592
|
+
function addMinutes(date, minutes) {
|
|
2593
|
+
return new Date(date.getTime() + minutes * 60_000);
|
|
2594
|
+
}
|
|
2595
|
+
/** Returns a new date with `days` subtracted. */
|
|
2596
|
+
function subtractDays(date, days) {
|
|
2597
|
+
return addDays(date, -days);
|
|
2598
|
+
}
|
|
2599
|
+
/** Returns a new date with `months` subtracted. */
|
|
2600
|
+
function subtractMonths(date, months) {
|
|
2601
|
+
return addMonths(date, -months);
|
|
2602
|
+
}
|
|
2603
|
+
/** Returns a new date with `years` subtracted. */
|
|
2604
|
+
function subtractYears(date, years) {
|
|
2605
|
+
return addYears(date, -years);
|
|
2606
|
+
}
|
|
2607
|
+
/** Returns the absolute difference in whole days between two dates. */
|
|
2608
|
+
function diffInDays(date1, date2) {
|
|
2609
|
+
return Math.abs(Math.floor((date1.getTime() - date2.getTime()) / 86_400_000));
|
|
2610
|
+
}
|
|
2611
|
+
/** Returns the absolute difference in whole months between two dates. */
|
|
2612
|
+
function diffInMonths(date1, date2) {
|
|
2613
|
+
return Math.abs((date1.getFullYear() - date2.getFullYear()) * 12 +
|
|
2614
|
+
(date1.getMonth() - date2.getMonth()));
|
|
2615
|
+
}
|
|
2616
|
+
/** Returns the absolute difference in whole years between two dates. */
|
|
2617
|
+
function diffInYears(date1, date2) {
|
|
2618
|
+
return Math.abs(date1.getFullYear() - date2.getFullYear());
|
|
2619
|
+
}
|
|
2620
|
+
/** Returns the difference in minutes between two dates (date1 - date2). */
|
|
2621
|
+
function diffInMinutes(date1, date2) {
|
|
2622
|
+
return Math.floor((date1.getTime() - date2.getTime()) / 60_000);
|
|
2623
|
+
}
|
|
2624
|
+
/** Returns true if date1 is strictly before date2. */
|
|
2625
|
+
function isBefore(date1, date2) {
|
|
2626
|
+
return date1.getTime() < date2.getTime();
|
|
2627
|
+
}
|
|
2628
|
+
/** Returns true if date1 is strictly after date2. */
|
|
2629
|
+
function isAfter(date1, date2) {
|
|
2630
|
+
return date1.getTime() > date2.getTime();
|
|
2631
|
+
}
|
|
2632
|
+
/** Returns true if both dates fall on the same calendar day. */
|
|
2633
|
+
function isSameDay(date1, date2) {
|
|
2634
|
+
return (date1.getFullYear() === date2.getFullYear() &&
|
|
2635
|
+
date1.getMonth() === date2.getMonth() &&
|
|
2636
|
+
date1.getDate() === date2.getDate());
|
|
2637
|
+
}
|
|
2638
|
+
/** Returns true if the date falls on a Saturday or Sunday. */
|
|
2639
|
+
function isWeekend(date) {
|
|
2640
|
+
return date.getDay() === 0 || date.getDay() === 6;
|
|
2641
|
+
}
|
|
2642
|
+
/** Returns true if the date is today. */
|
|
2643
|
+
function isToday(date) {
|
|
2644
|
+
return isSameDay(date, new Date());
|
|
2645
|
+
}
|
|
2646
|
+
/** Returns true if the date falls in the past (before now). */
|
|
2647
|
+
function isPast(date) {
|
|
2648
|
+
return date.getTime() < Date.now();
|
|
2649
|
+
}
|
|
2650
|
+
/** Returns true if the date falls in the future (after now). */
|
|
2651
|
+
function isFuture(date) {
|
|
2652
|
+
return date.getTime() > Date.now();
|
|
2653
|
+
}
|
|
2654
|
+
/** Returns the date with time set to 00:00:00.000. */
|
|
2655
|
+
function startOfDay(date) {
|
|
2656
|
+
const result = new Date(date);
|
|
2657
|
+
result.setHours(0, 0, 0, 0);
|
|
2658
|
+
return result;
|
|
2659
|
+
}
|
|
2660
|
+
/** Returns the date with time set to 23:59:59.999. */
|
|
2661
|
+
function endOfDay(date) {
|
|
2662
|
+
const result = new Date(date);
|
|
2663
|
+
result.setHours(23, 59, 59, 999);
|
|
2664
|
+
return result;
|
|
2665
|
+
}
|
|
2666
|
+
/** Returns the first day of the month (time 00:00:00.000). */
|
|
2667
|
+
function startOfMonth(date) {
|
|
2668
|
+
return new Date(date.getFullYear(), date.getMonth(), 1);
|
|
2669
|
+
}
|
|
2670
|
+
/** Returns the last day of the month (time 23:59:59.999). */
|
|
2671
|
+
function endOfMonth(date) {
|
|
2672
|
+
const result = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
2673
|
+
result.setHours(23, 59, 59, 999);
|
|
2674
|
+
return result;
|
|
2675
|
+
}
|
|
2676
|
+
/** Returns the first day of the year (Jan 1, 00:00:00.000). */
|
|
2677
|
+
function startOfYear(date) {
|
|
2678
|
+
return new Date(date.getFullYear(), 0, 1);
|
|
2679
|
+
}
|
|
2680
|
+
/** Returns the last day of the year (Dec 31, 23:59:59.999). */
|
|
2681
|
+
function endOfYear(date) {
|
|
2682
|
+
const result = new Date(date.getFullYear(), 11, 31);
|
|
2683
|
+
result.setHours(23, 59, 59, 999);
|
|
2684
|
+
return result;
|
|
2685
|
+
}
|
|
2686
|
+
/** Calculates the age in years from a birth date. */
|
|
2687
|
+
function getAge(birthDate) {
|
|
2688
|
+
const n = new Date();
|
|
2689
|
+
let age = n.getFullYear() - birthDate.getFullYear();
|
|
2690
|
+
const monthDiff = n.getMonth() - birthDate.getMonth();
|
|
2691
|
+
if (monthDiff < 0 || (monthDiff === 0 && n.getDate() < birthDate.getDate()))
|
|
2692
|
+
age--;
|
|
2693
|
+
return age;
|
|
2694
|
+
}
|
|
2695
|
+
/** Returns the number of days in a given month (0-based month). */
|
|
2696
|
+
function daysInMonth(year, month) {
|
|
2697
|
+
return new Date(year, month + 1, 0).getDate();
|
|
2698
|
+
}
|
|
2699
|
+
/** Returns true if the given year is a leap year. */
|
|
2700
|
+
function isLeapYear(year) {
|
|
2701
|
+
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
|
|
2702
|
+
}
|
|
2703
|
+
/**
|
|
2704
|
+
* Returns a human-friendly relative time string.
|
|
2705
|
+
* @example timeAgo(new Date(Date.now() - 3600_000)) → '1 hour ago'
|
|
2706
|
+
*/
|
|
2707
|
+
function timeAgo(date) {
|
|
2708
|
+
const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
2709
|
+
const abs = Math.abs(seconds);
|
|
2710
|
+
const future = seconds < 0;
|
|
2711
|
+
const intervals = [
|
|
2712
|
+
[31_536_000, 'year'],
|
|
2713
|
+
[2_592_000, 'month'],
|
|
2714
|
+
[604_800, 'week'],
|
|
2715
|
+
[86_400, 'day'],
|
|
2716
|
+
[3_600, 'hour'],
|
|
2717
|
+
[60, 'minute'],
|
|
2718
|
+
[1, 'second'],
|
|
2719
|
+
];
|
|
2720
|
+
for (const [secs, label] of intervals) {
|
|
2721
|
+
const count = Math.floor(abs / secs);
|
|
2722
|
+
if (count >= 1) {
|
|
2723
|
+
const unit = count === 1 ? label : `${label}s`;
|
|
2724
|
+
return future ? `in ${count} ${unit}` : `${count} ${unit} ago`;
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
return 'just now';
|
|
2728
|
+
}
|
|
2729
|
+
/** Returns the ISO week number (1–53) for the given date. */
|
|
2730
|
+
function getWeekNumber(date) {
|
|
2731
|
+
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
|
2732
|
+
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));
|
|
2733
|
+
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
2734
|
+
return Math.ceil((((d.getTime() - yearStart.getTime()) / 86_400_000) + 1) / 7);
|
|
2735
|
+
}
|
|
2736
|
+
/** Returns the next weekday (Mon–Fri) after the given date. */
|
|
2737
|
+
function nextWorkday(date) {
|
|
2738
|
+
const result = addDays(date, 1);
|
|
2739
|
+
while (isWeekend(result)) {
|
|
2740
|
+
result.setDate(result.getDate() + 1);
|
|
2741
|
+
}
|
|
2742
|
+
return result;
|
|
2743
|
+
}
|
|
2744
|
+
/** Returns true if a date falls within the range [start, end] (inclusive). */
|
|
2745
|
+
function inRange$2(date, start, end) {
|
|
2746
|
+
return date >= start && date <= end;
|
|
2747
|
+
}
|
|
2748
|
+
/** Parses a `YYYY-MM-DD` string and returns a local-midnight Date or null. */
|
|
2749
|
+
function parseDate(dateStr) {
|
|
2750
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(dateStr);
|
|
2751
|
+
if (!match)
|
|
2752
|
+
return null;
|
|
2753
|
+
const [, y, mo, d] = match.map(Number);
|
|
2754
|
+
const result = new Date(y, mo - 1, d);
|
|
2755
|
+
return isValidDate$1(result) ? result : null;
|
|
2756
|
+
}
|
|
2757
|
+
function localeDate(dateStr) {
|
|
2758
|
+
const date = new Date(dateStr);
|
|
2759
|
+
date.toLocaleString('en-US');
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
var date_util = /*#__PURE__*/Object.freeze({
|
|
2763
|
+
__proto__: null,
|
|
2764
|
+
addDays: addDays,
|
|
2765
|
+
addHours: addHours,
|
|
2766
|
+
addMinutes: addMinutes,
|
|
2767
|
+
addMonths: addMonths,
|
|
2768
|
+
addYears: addYears,
|
|
2769
|
+
daysInMonth: daysInMonth,
|
|
2770
|
+
diffInDays: diffInDays,
|
|
2771
|
+
diffInMinutes: diffInMinutes,
|
|
2772
|
+
diffInMonths: diffInMonths,
|
|
2773
|
+
diffInYears: diffInYears,
|
|
2774
|
+
endOfDay: endOfDay,
|
|
2775
|
+
endOfMonth: endOfMonth,
|
|
2776
|
+
endOfYear: endOfYear,
|
|
2777
|
+
formatDate: formatDate,
|
|
2778
|
+
getAge: getAge,
|
|
2779
|
+
getWeekNumber: getWeekNumber,
|
|
2780
|
+
inRange: inRange$2,
|
|
2781
|
+
isAfter: isAfter,
|
|
2782
|
+
isBefore: isBefore,
|
|
2783
|
+
isFuture: isFuture,
|
|
2784
|
+
isLeapYear: isLeapYear,
|
|
2785
|
+
isPast: isPast,
|
|
2786
|
+
isSameDay: isSameDay,
|
|
2787
|
+
isToday: isToday,
|
|
2788
|
+
isValidDate: isValidDate$1,
|
|
2789
|
+
isWeekend: isWeekend,
|
|
2790
|
+
localeDate: localeDate,
|
|
2791
|
+
nextWorkday: nextWorkday,
|
|
2792
|
+
now: now,
|
|
2793
|
+
parseDate: parseDate,
|
|
2794
|
+
startOfDay: startOfDay,
|
|
2795
|
+
startOfMonth: startOfMonth,
|
|
2796
|
+
startOfYear: startOfYear,
|
|
2797
|
+
subtractDays: subtractDays,
|
|
2798
|
+
subtractMonths: subtractMonths,
|
|
2799
|
+
subtractYears: subtractYears,
|
|
2800
|
+
timeAgo: timeAgo,
|
|
2801
|
+
toDateOnly: toDateOnly,
|
|
2802
|
+
toISOString: toISOString,
|
|
2803
|
+
today: today
|
|
2804
|
+
});
|
|
2805
|
+
|
|
2806
|
+
class OslDatetimepicker {
|
|
2807
|
+
dtNativeInput;
|
|
2808
|
+
label = '';
|
|
2809
|
+
required = false;
|
|
2810
|
+
disabled = false;
|
|
2811
|
+
dateModel = null;
|
|
2754
2812
|
set model(val) {
|
|
2755
|
-
this.
|
|
2813
|
+
this.dateModel = this._parseToDate(val);
|
|
2814
|
+
this._scheduleDisplayUpdate();
|
|
2756
2815
|
}
|
|
2757
|
-
|
|
2758
|
-
|
|
2816
|
+
placeholder = '';
|
|
2817
|
+
minDate = null;
|
|
2818
|
+
maxDate = null;
|
|
2819
|
+
set minDatetime(val) {
|
|
2820
|
+
this.minDate = this._parseToDate(val);
|
|
2821
|
+
}
|
|
2822
|
+
set maxDatetime(val) {
|
|
2823
|
+
this.maxDate = this._parseToDate(val);
|
|
2824
|
+
}
|
|
2825
|
+
/** Emitted string format. Tokens: YYYY MM DD HH mm ss ddd dddd etc. Default: 'YYYY-MM-DDTHH:mm' */
|
|
2826
|
+
format = 'YYYY-MM-DDTHH:mm';
|
|
2827
|
+
/** Display format shown inside the input field. Default: 'ddd DD/MM/YYYY HH:mm' → Sun 18/01/2026 19:18 */
|
|
2828
|
+
set displayFormatInput(val) {
|
|
2829
|
+
this._displayFormat = val || 'ddd DD/MM/YYYY HH:mm';
|
|
2830
|
+
this._scheduleDisplayUpdate();
|
|
2759
2831
|
}
|
|
2832
|
+
_displayFormat = 'ddd DD/MM/YYYY HH:mm';
|
|
2833
|
+
showSeconds = false;
|
|
2834
|
+
enableMeridian = false;
|
|
2760
2835
|
skeletonLoading = false;
|
|
2761
2836
|
skeletonTheme = 'light';
|
|
2762
2837
|
modelChange = new EventEmitter();
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
this.
|
|
2766
|
-
}
|
|
2767
|
-
ngOnInit() {
|
|
2768
|
-
this.loadApiDatasources();
|
|
2769
|
-
}
|
|
2770
|
-
ngOnChanges(changes) {
|
|
2771
|
-
if (changes['elements'] || changes['model']) {
|
|
2772
|
-
this.loadApiDatasources();
|
|
2773
|
-
}
|
|
2774
|
-
}
|
|
2775
|
-
loadApiDatasources() {
|
|
2776
|
-
this._loadForList(this.elements);
|
|
2777
|
-
}
|
|
2778
|
-
async _loadForList(list) {
|
|
2779
|
-
for (const elem of list) {
|
|
2780
|
-
if (elem.elementType === 'fieldset' && elem.rows?.length) {
|
|
2781
|
-
this._loadForList(elem.rows);
|
|
2782
|
-
}
|
|
2783
|
-
else if (elem.apiService && elem.apiMethod && (!elem.searchType || elem.searchType == 'Local')) {
|
|
2784
|
-
if (elem.dependsOn?.length) {
|
|
2785
|
-
const allReady = elem.dependsOn.every(k => this.model?.[k] != null);
|
|
2786
|
-
if (!allReady)
|
|
2787
|
-
continue;
|
|
2788
|
-
if (elem.datasource?.length)
|
|
2789
|
-
continue;
|
|
2790
|
-
}
|
|
2791
|
-
elem.loadingIf = () => true;
|
|
2792
|
-
const data = await this.datasourceCache.load(elem.apiService, elem.apiMethod, elem.apiBody ? elem.apiBody(this.model) : null);
|
|
2793
|
-
elem.loadingIf = () => false;
|
|
2794
|
-
this.cdr.markForCheck();
|
|
2795
|
-
if (data && data.length > 0) {
|
|
2796
|
-
elem.datasource = data;
|
|
2797
|
-
}
|
|
2798
|
-
}
|
|
2799
|
-
}
|
|
2838
|
+
changeEv = new EventEmitter();
|
|
2839
|
+
ngAfterViewInit() {
|
|
2840
|
+
this._updateDisplay();
|
|
2800
2841
|
}
|
|
2801
|
-
|
|
2802
|
-
this.
|
|
2803
|
-
this.
|
|
2842
|
+
onDateChange(date) {
|
|
2843
|
+
this.dateModel = date;
|
|
2844
|
+
this._updateDisplay();
|
|
2845
|
+
const str = date ? formatDate(date, this.format) : '';
|
|
2846
|
+
this.modelChange.emit(str);
|
|
2847
|
+
this.changeEv.emit(str);
|
|
2804
2848
|
}
|
|
2805
|
-
|
|
2806
|
-
if (!
|
|
2849
|
+
_updateDisplay() {
|
|
2850
|
+
if (!this.dtNativeInput)
|
|
2807
2851
|
return;
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
if (Array.isArray(value)) {
|
|
2811
|
-
selectedObj = value.map(v => elem.datasource.find(item => (elem.valueField ? item[elem.valueField] : item) === v) ?? null);
|
|
2812
|
-
}
|
|
2813
|
-
else if (value !== null && value !== undefined) {
|
|
2814
|
-
selectedObj = elem.datasource.find(item => (elem.valueField ? item[elem.valueField] : item) === (isNaN(Number(value)) ? value : Number(value))) ?? null;
|
|
2815
|
-
}
|
|
2816
|
-
else {
|
|
2817
|
-
selectedObj = null;
|
|
2818
|
-
}
|
|
2819
|
-
}
|
|
2820
|
-
elem.change(this.model, undefined, selectedObj);
|
|
2852
|
+
this.dtNativeInput.nativeElement.value =
|
|
2853
|
+
this.dateModel ? formatDate(this.dateModel, this._displayFormat) : '';
|
|
2821
2854
|
}
|
|
2822
|
-
|
|
2823
|
-
this.
|
|
2824
|
-
if (elem.change)
|
|
2825
|
-
this.onSelectChange(elem, value);
|
|
2826
|
-
this._refreshDependents(elem.key);
|
|
2855
|
+
_scheduleDisplayUpdate() {
|
|
2856
|
+
setTimeout(() => this._updateDisplay());
|
|
2827
2857
|
}
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
const body = dep.apiBody ? dep.apiBody(this.model) : undefined;
|
|
2846
|
-
if (dep.searchType !== 'Api' && dep.apiService && dep.apiMethod) {
|
|
2847
|
-
dep.loadingIf = () => true;
|
|
2848
|
-
this.cdr.markForCheck();
|
|
2849
|
-
try {
|
|
2850
|
-
const res = await dep.apiService[dep.apiMethod](body);
|
|
2851
|
-
dep.datasource = Array.isArray(res) ? res : (res?.result ?? []);
|
|
2852
|
-
}
|
|
2853
|
-
finally {
|
|
2854
|
-
dep.loadingIf = () => false;
|
|
2855
|
-
}
|
|
2856
|
-
const cur = this.model[dep.key];
|
|
2857
|
-
if (cur != null) {
|
|
2858
|
-
const exists = dep.datasource.some(item => (dep.valueField ? item[dep.valueField] : item) === cur);
|
|
2859
|
-
if (!exists) {
|
|
2860
|
-
this.model[dep.key] = null;
|
|
2861
|
-
valueCleared = true;
|
|
2858
|
+
_parseToDate(val) {
|
|
2859
|
+
if (!val)
|
|
2860
|
+
return null;
|
|
2861
|
+
const d = new Date(val);
|
|
2862
|
+
return isNaN(d.getTime()) ? null : d;
|
|
2863
|
+
}
|
|
2864
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslDatetimepicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2865
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslDatetimepicker, isStandalone: false, selector: "osl-datetimepicker", inputs: { label: "label", required: "required", disabled: "disabled", model: "model", placeholder: "placeholder", minDatetime: "minDatetime", maxDatetime: "maxDatetime", format: "format", displayFormatInput: ["displayFormat", "displayFormatInput"], showSeconds: "showSeconds", enableMeridian: "enableMeridian", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange", changeEv: "changeEv" }, providers: [
|
|
2866
|
+
{
|
|
2867
|
+
provide: MAT_DATE_FORMATS,
|
|
2868
|
+
useValue: {
|
|
2869
|
+
parse: {
|
|
2870
|
+
dateInput: 'DD-MMM-YYYY hh:mm a',
|
|
2871
|
+
},
|
|
2872
|
+
display: {
|
|
2873
|
+
dateInput: 'DD-MMM-YYYY hh:mm a',
|
|
2874
|
+
monthYearLabel: 'MMM YYYY', dateA11yLabel: 'LL', monthYearA11yLabel: 'MMMM YYYY',
|
|
2862
2875
|
}
|
|
2863
2876
|
}
|
|
2864
2877
|
}
|
|
2865
|
-
|
|
2866
|
-
if (this.model[dep.key] != null) {
|
|
2867
|
-
this.model[dep.key] = null;
|
|
2868
|
-
valueCleared = true;
|
|
2869
|
-
}
|
|
2870
|
-
}
|
|
2871
|
-
this.cdr.markForCheck();
|
|
2872
|
-
if (valueCleared)
|
|
2873
|
-
await this._refreshDependents(dep.key);
|
|
2874
|
-
}
|
|
2875
|
-
}
|
|
2876
|
-
_flatElements(list = this.elements) {
|
|
2877
|
-
return list.flatMap(e => e.rows?.length ? [e, ...this._flatElements(e.rows)] : [e]);
|
|
2878
|
-
}
|
|
2879
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: DynamicForm, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
2880
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: DynamicForm, isStandalone: false, selector: "osl-dynamic-form", inputs: { elements: "elements", model: "model", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme" }, outputs: { modelChange: "modelChange" }, usesOnChanges: true, ngImport: i0, template: "@if(elements && elements.length > 0) {\r\n <div class=\"row align-items-center w-100\">\r\n @for(elem of elements; track elem) {\r\n @if(!(elem.hideIf ? elem.hideIf(model) : elem.hide)) {\r\n <ng-container *ngTemplateOutlet=\"formField; context: { $implicit: elem }\"></ng-container>\r\n }\r\n }\r\n </div>\r\n}\r\n\r\n<ng-template #formField let-elem>\r\n @if(elem.hideIf ? !elem.hideIf(model) : true) {\r\n @switch (elem.elementType) {\r\n\r\n @case (\"textbox\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-input\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n [label]=\"elem.label\"\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [type]=\"elem.inputType || 'text'\"\r\n [placeholder]=\"elem.placeholder || ''\"\r\n [mask]=\"elem.mask || ''\"\r\n [min]=\"elem.min ?? ''\"\r\n [max]=\"elem.max ?? ''\"\r\n [minLength]=\"elem.minLength ?? null\"\r\n [maxLength]=\"elem.maxLength ?? null\"\r\n [prefixIcon]=\"elem.prefixIcon || ''\"\r\n [suffixIcon]=\"elem.suffixIcon || ''\"\r\n [onlyChars]=\"!!elem.onlyChars\"\r\n [decimalPortion]=\"elem.decimalPortion ?? null\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n [isCapitalize]=\"elem.isCapitalize\"\r\n ></osl-input>\r\n </div>\r\n }\r\n\r\n @case (\"textarea\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-textarea\r\n [label]=\"elem.label\"\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [rows]=\"elem.textareaRows || 3\"\r\n [placeholder]=\"elem.placeholder || ''\"\r\n [maxLength]=\"elem.maxLength ?? null\"\r\n [minLength]=\"elem.minLength ?? null\"\r\n [characterCounter]=\"!!elem.characterCounter\"\r\n [resize]=\"elem.resize || 'none'\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-textarea>\r\n </div>\r\n }\r\n\r\n @case (\"select\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-select\r\n [label]=\"elem.label\"\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [datasource]=\"elem.datasource || []\"\r\n [displayField]=\"elem.displayField || ''\"\r\n [valueField]=\"elem.valueField || ''\"\r\n [placeholder]=\"elem.selectPlaceholder || 'Select...'\"\r\n [loading]=\"elem.loadingIf ? elem.loadingIf(model) : false\"\r\n [clearable]=\"!!elem.clearable\"\r\n (changeEv)=\"elem.change ? onSelectChange(elem, $event) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-select>\r\n </div>\r\n }\r\n\r\n @case (\"radio\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-radio\r\n [label]=\"elem.label\"\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [datasource]=\"elem.datasource || []\"\r\n [displayField]=\"elem.displayField || ''\"\r\n [valueField]=\"elem.valueField || ''\"\r\n [inline]=\"!!elem.inline\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-radio>\r\n </div>\r\n }\r\n\r\n @case (\"slide-toggle\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-slide-toggle\r\n [label]=\"elem.label\"\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [labelPosition]=\"elem.labelPosition || 'after'\"\r\n [trueLabel]=\"elem.trueLabel || ''\"\r\n [falseLabel]=\"elem.falseLabel || ''\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-slide-toggle>\r\n </div>\r\n }\r\n\r\n @case (\"autocomplete\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-autocomplete\r\n [label]=\"elem.label\"\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [(datasource)]=\"elem.datasource\"\r\n [displayField]=\"elem.displayField || ''\"\r\n [valueField]=\"elem.valueField || ''\"\r\n [placeholder]=\"elem.autocompletePlaceholder || 'Type to search...'\"\r\n [loading]=\"elem.loadingIf ? elem.loadingIf(model) : false\"\r\n (changeEv)=\"onFieldChange(elem, $event)\"\r\n [(model)]=\"model[elem.key]\"\r\n [methodName]=\"elem.apiMethod\"\r\n [service]=\"elem.apiService\"\r\n [object]=\"model[elem.objectName]\"\r\n [searchType]=\"elem.searchType\"\r\n [configMethodName]=\"elem.apiConfigMethod\"\r\n [isLister]=\"elem.isListerAutocomplete\"\r\n [apiBody]=\"elem.apiBody ? elem.apiBody(model) : null\"\r\n ></osl-autocomplete>\r\n </div>\r\n }\r\n\r\n @case (\"file-uploader\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-file-upload\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [label]=\"elem.label\"\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [accept]=\"elem.accept || ''\"\r\n [multiple]=\"!!elem.multiple\"\r\n [maxSize]=\"elem.maxFileSize || 0\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-file-upload>\r\n </div>\r\n }\r\n\r\n @case (\"datepicker\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-datepicker\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [label]=\"elem.label\"\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [dateType]=\"elem.dateType || 'date'\"\r\n [placeholder]=\"elem.placeholder || ''\"\r\n [minDate]=\"elem.minDateIf ? elem.minDateIf(model): elem.minDate ? elem.minDate : ''\"\r\n [maxDate]=\"elem.maxDateIf ? elem.maxDateIf(model): elem.maxDate ? elem.maxDate : ''\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-datepicker>\r\n </div>\r\n }\r\n\r\n @case (\"datetimepicker\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-datetimepicker\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n [label]=\"elem.label\"\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [placeholder]=\"elem.placeholder || ''\"\r\n [minDatetime]=\"elem.minDatetimeIf ? elem.minDatetimeIf(model) : elem.minDatetime || ''\"\r\n [maxDatetime]=\"elem.maxDatetimeIf ? elem.maxDatetimeIf(model) : elem.maxDatetime || ''\"\r\n [format]=\"elem.datetimepickerFormat || 'YYYY-MM-DDTHH:mm'\"\r\n [showSeconds]=\"!!elem.datetimepickerShowSeconds\"\r\n [enableMeridian]=\"!!elem.datetimepickerEnableMeridian\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-datetimepicker>\r\n </div>\r\n }\r\n\r\n @case (\"checkbox\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-checkbox\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [label]=\"elem.label\"\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [indeterminate]=\"!!elem.indeterminate\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-checkbox>\r\n </div>\r\n }\r\n\r\n @case (\"button\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-button\r\n \r\n [label]=\"elem.label\"\r\n [loading]=\"elem.loadingIf ? elem.loadingIf(model) : false\"\r\n variant=\"secondary\"\r\n (clickEv)=\"elem.change ? elem.change(model) : null\"\r\n ></osl-button>\r\n </div>\r\n }\r\n\r\n @case (\"fieldset\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-3'\">\r\n <fieldset class=\"osl-fieldset\">\r\n @if(elem.label) {\r\n <legend class=\"osl-fieldset-legend\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <span class=\"osl-fieldset-legend__text\">{{ elem.label }}</span>\r\n </legend>\r\n }\r\n <div class=\"row w-100 osl-fieldset-body\">\r\n @for(innerElem of elem.rows; track innerElem) {\r\n @if(!(innerElem.hideIf ? innerElem.hideIf(model) : innerElem.hide)) {\r\n <ng-container *ngTemplateOutlet=\"formField; context: { $implicit: innerElem }\"></ng-container>\r\n }\r\n }\r\n </div>\r\n </fieldset>\r\n </div>\r\n }\r\n\r\n @case (\"templateRef\"){\r\n <div [class]=\"'col-md-'+elem.columns+' mt-3'\">\r\n <ng-container *ngTemplateOutlet=\"elem.templateRef; context: { $implicit: elem}\"></ng-container>\r\n\r\n \r\n </div>\r\n }\r\n @case (\"spacer\"){\r\n <div [class]=\"'col-md-'+elem.columns+' mt-3'\">\r\n <!-- <ng-container *ngTemplateOutlet=\"elem.templateRef; context: { $implicit: elem}\"></ng-container> -->\r\n\r\n \r\n </div>\r\n }\r\n\r\n }\r\n }\r\n</ng-template>\r\n", styles: [".osl-fieldset{border:1.5px solid var(--osl-border-color, #e5e7eb);border-radius:10px;padding:0 16px 16px;margin:0;background:linear-gradient(135deg,#f9fafb,#fff);box-shadow:0 1px 4px #0000000d;position:relative;transition:box-shadow .2s}.osl-fieldset:focus-within{box-shadow:0 0 0 3px #6366f114,0 2px 8px #0000000f;border-color:var(--osl-primary, #6366f1)}.osl-fieldset-legend{padding:0 6px;margin-left:8px;line-height:1;float:none;width:auto}.osl-fieldset-legend__text{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;background:#fff;padding:2px 10px;border:1.5px solid var(--osl-border-color, #e5e7eb);box-shadow:0 1px 3px #6366f11a}.osl-fieldset-body{margin-top:4px}\n"], dependencies: [{ kind: "directive", type: i1$2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: OslSkeletonDirective, selector: "[oslSkeleton]", inputs: ["oslSkeleton", "oslSkeletonType", "oslSkeletonAnimation", "oslSkeletonTheme", "oslSkeletonColor", "oslSkeletonHighlight", "oslSkeletonRadius", "oslSkeletonRows", "oslSkeletonRowGap", "oslSkeletonZIndex", "oslSkeletonDelay", "oslSkeletonDuration", "oslSkeletonMinHeight", "oslSkeletonForceReread", "oslSkeletonCircleSize", "oslSkeletonListItems", "oslSkeletonTableRows", "oslSkeletonTableCols", "oslSkeletonCardLines", "oslSkeletonBgColor"] }, { kind: "component", type: Oslinput, selector: "osl-input", inputs: ["label", "required", "disabled", "model", "type", "placeholder", "mask", "min", "max", "minLength", "maxLength", "prefixIcon", "suffixIcon", "skeletonLoading", "skeletonTheme", "onlyChars", "isCapitalize", "decimalPortion"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: Osltextarea, selector: "osl-textarea", inputs: ["label", "rows", "required", "disabled", "model", "placeholder", "maxLength", "minLength", "characterCounter", "resize", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslSelect, selector: "osl-select", inputs: ["label", "required", "disabled", "model", "datasource", "displayField", "valueField", "placeholder", "loading", "clearable", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslRadio, selector: "osl-radio", inputs: ["label", "required", "disabled", "model", "datasource", "displayField", "valueField", "inline", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslSlideToggle, selector: "osl-slide-toggle", inputs: ["label", "disabled", "model", "labelPosition", "trueLabel", "falseLabel", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslAutocomplete, selector: "osl-autocomplete", inputs: ["label", "required", "disabled", "model", "datasource", "displayField", "valueField", "placeholder", "loading", "searchType", "methodName", "configMethodName", "service", "object", "skeletonLoading", "skeletonTheme", "isLister", "apiBody"], outputs: ["datasourceChange", "modelChange", "changeEv"] }, { kind: "component", type: OslFileUpload, selector: "osl-file-upload", inputs: ["label", "required", "disabled", "model", "accept", "multiple", "maxSize", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslDatepicker, selector: "osl-datepicker", inputs: ["label", "required", "disabled", "model", "dateType", "placeholder", "minDate", "maxDate", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslDatetimepicker, selector: "osl-datetimepicker", inputs: ["label", "required", "disabled", "model", "placeholder", "minDatetime", "maxDatetime", "format", "displayFormat", "showSeconds", "enableMeridian", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslCheckbox, selector: "osl-checkbox", inputs: ["label", "disabled", "required", "model", "indeterminate", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange", "changeEv"] }, { kind: "component", type: OslButton, selector: "osl-button", inputs: ["label", "icon", "variant", "size", "disabled", "loading", "type", "fullWidth"], outputs: ["clickEv"] }] });
|
|
2878
|
+
], viewQueries: [{ propertyName: "dtNativeInput", first: true, predicate: ["dtNativeInput"], descendants: true }], ngImport: i0, template: "<div class=\"d-flex flex-column\">\n @if (label) {\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n <label class=\"label\" [class.txt-clr-red]=\"dtField.touched && dtField.invalid\">\n {{label}} <span class=\"txt-clr-red\">{{required ? '*' : ''}}</span>\n </label>\n </div>\n }\n <div class=\"input-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n <input\n class=\"dt-input\"\n [ngxMatDatetimePicker]=\"picker\"\n [ngModel]=\"dateModel\"\n (ngModelChange)=\"onDateChange($event)\"\n [required]=\"required\"\n [disabled]=\"disabled\"\n [min]=\"minDate\"\n [max]=\"maxDate\"\n [placeholder]=\"placeholder\"\n [class.error]=\"dtField.touched && dtField.invalid\"\n #dtField=\"ngModel\"\n readonly\n />\n <button\n type=\"button\"\n class=\"dt-icon-btn\"\n (click)=\"picker.open()\"\n [disabled]=\"disabled\"\n tabindex=\"-1\">\n <svg viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"4\" width=\"14\" height=\"14\" rx=\"2\"/>\n <line x1=\"3\" y1=\"8\" x2=\"17\" y2=\"8\"/>\n <line x1=\"7\" y1=\"2\" x2=\"7\" y2=\"6\"/>\n <line x1=\"13\" y1=\"2\" x2=\"13\" y2=\"6\"/>\n </svg>\n </button>\n <ngx-mat-datetime-picker\n #picker\n [showSpinners]=\"true\"\n [showSeconds]=\"showSeconds\"\n [enableMeridian]=\"enableMeridian\">\n </ngx-mat-datetime-picker>\n </div>\n @if (dtField.touched && dtField.invalid && required) {\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\n }\n</div>\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px}.txt-clr-red{color:var(--osl-error-color)}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.input-wrapper{position:relative;display:flex;align-items:center}.dt-icon-btn{position:absolute;right:7px;top:50%;transform:translateY(-50%);width:22px;height:22px;padding:0;border:none;background:transparent;cursor:pointer;color:#9ca3af;display:flex;align-items:center;justify-content:center;transition:color .15s;border-radius:4px}.dt-icon-btn svg{width:16px;height:16px;display:block}.dt-icon-btn:hover:not(:disabled){color:var(--osl-focus-border-color)}.dt-icon-btn:disabled{opacity:.35;cursor:not-allowed}.dt-input{height:var(--osl-control-height);width:100%;border-radius:var(--osl-border-radius);outline:none;border:1px solid var(--osl-border-color);padding:0 38px 0 10px;font-size:var(--osl-text-font-size);font-family:inherit;color:#333;background-color:#fff;transition:border-color .2s,box-shadow .2s;letter-spacing:.01em;box-sizing:border-box}.dt-input::placeholder{color:#bbb}.dt-input:hover:not(:disabled){border-color:var(--osl-focus-border-color)}.dt-input:focus{border-color:var(--osl-focus-border-color);box-shadow:0 0 0 3px var(--osl-focus-shadow, rgba(99, 102, 241, .14))}.dt-input:disabled{background-color:#f5f5f5;cursor:not-allowed;opacity:.7}.dt-input.error{border-color:var(--osl-error-color)}.dt-input.error:focus{box-shadow:0 0 0 3px var(--osl-error-shadow, rgba(220, 38, 38, .14))}\n"], dependencies: [{ kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i2$2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: OslSkeletonDirective, selector: "[oslSkeleton]", inputs: ["oslSkeleton", "oslSkeletonType", "oslSkeletonAnimation", "oslSkeletonTheme", "oslSkeletonColor", "oslSkeletonHighlight", "oslSkeletonRadius", "oslSkeletonRows", "oslSkeletonRowGap", "oslSkeletonZIndex", "oslSkeletonDelay", "oslSkeletonDuration", "oslSkeletonMinHeight", "oslSkeletonForceReread", "oslSkeletonCircleSize", "oslSkeletonListItems", "oslSkeletonTableRows", "oslSkeletonTableCols", "oslSkeletonCardLines", "oslSkeletonBgColor"] }, { kind: "component", type: i4.NgxMatDatetimepicker, selector: "ngx-mat-datetime-picker", exportAs: ["ngxMatDatetimePicker"] }, { kind: "directive", type: i4.NgxMatDatepickerInput, selector: "input[ngxMatDatetimePicker]", inputs: ["ngxMatDatetimePicker", "min", "max", "matDatepickerFilter"], exportAs: ["ngxMatDatepickerInput"] }] });
|
|
2881
2879
|
}
|
|
2882
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type:
|
|
2880
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslDatetimepicker, decorators: [{
|
|
2883
2881
|
type: Component,
|
|
2884
|
-
args: [{ selector: 'osl-dynamic-form', standalone: false, template: "@if(elements && elements.length > 0) {\r\n <div class=\"row align-items-center w-100\">\r\n @for(elem of elements; track elem) {\r\n @if(!(elem.hideIf ? elem.hideIf(model) : elem.hide)) {\r\n <ng-container *ngTemplateOutlet=\"formField; context: { $implicit: elem }\"></ng-container>\r\n }\r\n }\r\n </div>\r\n}\r\n\r\n<ng-template #formField let-elem>\r\n @if(elem.hideIf ? !elem.hideIf(model) : true) {\r\n @switch (elem.elementType) {\r\n\r\n @case (\"textbox\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-input\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n [label]=\"elem.label\"\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [type]=\"elem.inputType || 'text'\"\r\n [placeholder]=\"elem.placeholder || ''\"\r\n [mask]=\"elem.mask || ''\"\r\n [min]=\"elem.min ?? ''\"\r\n [max]=\"elem.max ?? ''\"\r\n [minLength]=\"elem.minLength ?? null\"\r\n [maxLength]=\"elem.maxLength ?? null\"\r\n [prefixIcon]=\"elem.prefixIcon || ''\"\r\n [suffixIcon]=\"elem.suffixIcon || ''\"\r\n [onlyChars]=\"!!elem.onlyChars\"\r\n [decimalPortion]=\"elem.decimalPortion ?? null\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n [isCapitalize]=\"elem.isCapitalize\"\r\n ></osl-input>\r\n </div>\r\n }\r\n\r\n @case (\"textarea\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-textarea\r\n [label]=\"elem.label\"\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [rows]=\"elem.textareaRows || 3\"\r\n [placeholder]=\"elem.placeholder || ''\"\r\n [maxLength]=\"elem.maxLength ?? null\"\r\n [minLength]=\"elem.minLength ?? null\"\r\n [characterCounter]=\"!!elem.characterCounter\"\r\n [resize]=\"elem.resize || 'none'\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-textarea>\r\n </div>\r\n }\r\n\r\n @case (\"select\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-select\r\n [label]=\"elem.label\"\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [datasource]=\"elem.datasource || []\"\r\n [displayField]=\"elem.displayField || ''\"\r\n [valueField]=\"elem.valueField || ''\"\r\n [placeholder]=\"elem.selectPlaceholder || 'Select...'\"\r\n [loading]=\"elem.loadingIf ? elem.loadingIf(model) : false\"\r\n [clearable]=\"!!elem.clearable\"\r\n (changeEv)=\"elem.change ? onSelectChange(elem, $event) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-select>\r\n </div>\r\n }\r\n\r\n @case (\"radio\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-radio\r\n [label]=\"elem.label\"\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [datasource]=\"elem.datasource || []\"\r\n [displayField]=\"elem.displayField || ''\"\r\n [valueField]=\"elem.valueField || ''\"\r\n [inline]=\"!!elem.inline\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-radio>\r\n </div>\r\n }\r\n\r\n @case (\"slide-toggle\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-slide-toggle\r\n [label]=\"elem.label\"\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [labelPosition]=\"elem.labelPosition || 'after'\"\r\n [trueLabel]=\"elem.trueLabel || ''\"\r\n [falseLabel]=\"elem.falseLabel || ''\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-slide-toggle>\r\n </div>\r\n }\r\n\r\n @case (\"autocomplete\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-autocomplete\r\n [label]=\"elem.label\"\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [(datasource)]=\"elem.datasource\"\r\n [displayField]=\"elem.displayField || ''\"\r\n [valueField]=\"elem.valueField || ''\"\r\n [placeholder]=\"elem.autocompletePlaceholder || 'Type to search...'\"\r\n [loading]=\"elem.loadingIf ? elem.loadingIf(model) : false\"\r\n (changeEv)=\"onFieldChange(elem, $event)\"\r\n [(model)]=\"model[elem.key]\"\r\n [methodName]=\"elem.apiMethod\"\r\n [service]=\"elem.apiService\"\r\n [object]=\"model[elem.objectName]\"\r\n [searchType]=\"elem.searchType\"\r\n [configMethodName]=\"elem.apiConfigMethod\"\r\n [isLister]=\"elem.isListerAutocomplete\"\r\n [apiBody]=\"elem.apiBody ? elem.apiBody(model) : null\"\r\n ></osl-autocomplete>\r\n </div>\r\n }\r\n\r\n @case (\"file-uploader\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-file-upload\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [label]=\"elem.label\"\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [accept]=\"elem.accept || ''\"\r\n [multiple]=\"!!elem.multiple\"\r\n [maxSize]=\"elem.maxFileSize || 0\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-file-upload>\r\n </div>\r\n }\r\n\r\n @case (\"datepicker\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-datepicker\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [label]=\"elem.label\"\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [dateType]=\"elem.dateType || 'date'\"\r\n [placeholder]=\"elem.placeholder || ''\"\r\n [minDate]=\"elem.minDateIf ? elem.minDateIf(model): elem.minDate ? elem.minDate : ''\"\r\n [maxDate]=\"elem.maxDateIf ? elem.maxDateIf(model): elem.maxDate ? elem.maxDate : ''\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-datepicker>\r\n </div>\r\n }\r\n\r\n @case (\"datetimepicker\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-datetimepicker\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n [label]=\"elem.label\"\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [placeholder]=\"elem.placeholder || ''\"\r\n [minDatetime]=\"elem.minDatetimeIf ? elem.minDatetimeIf(model) : elem.minDatetime || ''\"\r\n [maxDatetime]=\"elem.maxDatetimeIf ? elem.maxDatetimeIf(model) : elem.maxDatetime || ''\"\r\n [format]=\"elem.datetimepickerFormat || 'YYYY-MM-DDTHH:mm'\"\r\n [showSeconds]=\"!!elem.datetimepickerShowSeconds\"\r\n [enableMeridian]=\"!!elem.datetimepickerEnableMeridian\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-datetimepicker>\r\n </div>\r\n }\r\n\r\n @case (\"checkbox\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-checkbox\r\n [skeletonLoading]=\"skeletonLoading\"\r\n [skeletonTheme]=\"skeletonTheme\"\r\n\r\n [label]=\"elem.label\"\r\n [disabled]=\"elem.disabledIf ? elem.disabledIf(model) : !!elem.disabled\"\r\n [required]=\"elem.requiredIf ? elem.requiredIf(model) : !!elem.required\"\r\n [indeterminate]=\"!!elem.indeterminate\"\r\n (changeEv)=\"elem.change ? elem.change(model) : null\"\r\n [(model)]=\"model[elem.key]\"\r\n ></osl-checkbox>\r\n </div>\r\n }\r\n\r\n @case (\"button\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-2'\" [attr.data-field-key]=\"elem.key\">\r\n <osl-button\r\n \r\n [label]=\"elem.label\"\r\n [loading]=\"elem.loadingIf ? elem.loadingIf(model) : false\"\r\n variant=\"secondary\"\r\n (clickEv)=\"elem.change ? elem.change(model) : null\"\r\n ></osl-button>\r\n </div>\r\n }\r\n\r\n @case (\"fieldset\") {\r\n <div [class]=\"'col-md-'+elem.columns+' mt-3'\">\r\n <fieldset class=\"osl-fieldset\">\r\n @if(elem.label) {\r\n <legend class=\"osl-fieldset-legend\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\r\n <span class=\"osl-fieldset-legend__text\">{{ elem.label }}</span>\r\n </legend>\r\n }\r\n <div class=\"row w-100 osl-fieldset-body\">\r\n @for(innerElem of elem.rows; track innerElem) {\r\n @if(!(innerElem.hideIf ? innerElem.hideIf(model) : innerElem.hide)) {\r\n <ng-container *ngTemplateOutlet=\"formField; context: { $implicit: innerElem }\"></ng-container>\r\n }\r\n }\r\n </div>\r\n </fieldset>\r\n </div>\r\n }\r\n\r\n @case (\"templateRef\"){\r\n <div [class]=\"'col-md-'+elem.columns+' mt-3'\">\r\n <ng-container *ngTemplateOutlet=\"elem.templateRef; context: { $implicit: elem}\"></ng-container>\r\n\r\n \r\n </div>\r\n }\r\n @case (\"spacer\"){\r\n <div [class]=\"'col-md-'+elem.columns+' mt-3'\">\r\n <!-- <ng-container *ngTemplateOutlet=\"elem.templateRef; context: { $implicit: elem}\"></ng-container> -->\r\n\r\n \r\n </div>\r\n }\r\n\r\n }\r\n }\r\n</ng-template>\r\n", styles: [".osl-fieldset{border:1.5px solid var(--osl-border-color, #e5e7eb);border-radius:10px;padding:0 16px 16px;margin:0;background:linear-gradient(135deg,#f9fafb,#fff);box-shadow:0 1px 4px #0000000d;position:relative;transition:box-shadow .2s}.osl-fieldset:focus-within{box-shadow:0 0 0 3px #6366f114,0 2px 8px #0000000f;border-color:var(--osl-primary, #6366f1)}.osl-fieldset-legend{padding:0 6px;margin-left:8px;line-height:1;float:none;width:auto}.osl-fieldset-legend__text{display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;background:#fff;padding:2px 10px;border:1.5px solid var(--osl-border-color, #e5e7eb);box-shadow:0 1px 3px #6366f11a}.osl-fieldset-body{margin-top:4px}\n"] }]
|
|
2885
|
-
|
|
2882
|
+
args: [{ selector: 'osl-datetimepicker', standalone: false, providers: [
|
|
2883
|
+
{
|
|
2884
|
+
provide: MAT_DATE_FORMATS,
|
|
2885
|
+
useValue: {
|
|
2886
|
+
parse: {
|
|
2887
|
+
dateInput: 'DD-MMM-YYYY hh:mm a',
|
|
2888
|
+
},
|
|
2889
|
+
display: {
|
|
2890
|
+
dateInput: 'DD-MMM-YYYY hh:mm a',
|
|
2891
|
+
monthYearLabel: 'MMM YYYY', dateA11yLabel: 'LL', monthYearA11yLabel: 'MMMM YYYY',
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
], template: "<div class=\"d-flex flex-column\">\n @if (label) {\n <div [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n <label class=\"label\" [class.txt-clr-red]=\"dtField.touched && dtField.invalid\">\n {{label}} <span class=\"txt-clr-red\">{{required ? '*' : ''}}</span>\n </label>\n </div>\n }\n <div class=\"input-wrapper\" [oslSkeleton]=\"skeletonLoading\" [oslSkeletonTheme]=\"skeletonTheme\">\n <input\n class=\"dt-input\"\n [ngxMatDatetimePicker]=\"picker\"\n [ngModel]=\"dateModel\"\n (ngModelChange)=\"onDateChange($event)\"\n [required]=\"required\"\n [disabled]=\"disabled\"\n [min]=\"minDate\"\n [max]=\"maxDate\"\n [placeholder]=\"placeholder\"\n [class.error]=\"dtField.touched && dtField.invalid\"\n #dtField=\"ngModel\"\n readonly\n />\n <button\n type=\"button\"\n class=\"dt-icon-btn\"\n (click)=\"picker.open()\"\n [disabled]=\"disabled\"\n tabindex=\"-1\">\n <svg viewBox=\"0 0 20 20\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"3\" y=\"4\" width=\"14\" height=\"14\" rx=\"2\"/>\n <line x1=\"3\" y1=\"8\" x2=\"17\" y2=\"8\"/>\n <line x1=\"7\" y1=\"2\" x2=\"7\" y2=\"6\"/>\n <line x1=\"13\" y1=\"2\" x2=\"13\" y2=\"6\"/>\n </svg>\n </button>\n <ngx-mat-datetime-picker\n #picker\n [showSpinners]=\"true\"\n [showSeconds]=\"showSeconds\"\n [enableMeridian]=\"enableMeridian\">\n </ngx-mat-datetime-picker>\n </div>\n @if (dtField.touched && dtField.invalid && required) {\n <mat-hint class=\"hint\">{{label}} is Required!</mat-hint>\n }\n</div>\n", styles: [".label{font-size:var(--osl-label-font-size);margin-bottom:5px}.txt-clr-red{color:var(--osl-error-color)}.hint{color:var(--osl-error-color);margin-top:2px;font-size:var(--osl-hint-font-size)}.input-wrapper{position:relative;display:flex;align-items:center}.dt-icon-btn{position:absolute;right:7px;top:50%;transform:translateY(-50%);width:22px;height:22px;padding:0;border:none;background:transparent;cursor:pointer;color:#9ca3af;display:flex;align-items:center;justify-content:center;transition:color .15s;border-radius:4px}.dt-icon-btn svg{width:16px;height:16px;display:block}.dt-icon-btn:hover:not(:disabled){color:var(--osl-focus-border-color)}.dt-icon-btn:disabled{opacity:.35;cursor:not-allowed}.dt-input{height:var(--osl-control-height);width:100%;border-radius:var(--osl-border-radius);outline:none;border:1px solid var(--osl-border-color);padding:0 38px 0 10px;font-size:var(--osl-text-font-size);font-family:inherit;color:#333;background-color:#fff;transition:border-color .2s,box-shadow .2s;letter-spacing:.01em;box-sizing:border-box}.dt-input::placeholder{color:#bbb}.dt-input:hover:not(:disabled){border-color:var(--osl-focus-border-color)}.dt-input:focus{border-color:var(--osl-focus-border-color);box-shadow:0 0 0 3px var(--osl-focus-shadow, rgba(99, 102, 241, .14))}.dt-input:disabled{background-color:#f5f5f5;cursor:not-allowed;opacity:.7}.dt-input.error{border-color:var(--osl-error-color)}.dt-input.error:focus{box-shadow:0 0 0 3px var(--osl-error-shadow, rgba(220, 38, 38, .14))}\n"] }]
|
|
2896
|
+
}], propDecorators: { dtNativeInput: [{
|
|
2897
|
+
type: ViewChild,
|
|
2898
|
+
args: ['dtNativeInput']
|
|
2899
|
+
}], label: [{
|
|
2886
2900
|
type: Input,
|
|
2887
|
-
args: ['
|
|
2901
|
+
args: ['label']
|
|
2902
|
+
}], required: [{
|
|
2903
|
+
type: Input,
|
|
2904
|
+
args: ['required']
|
|
2905
|
+
}], disabled: [{
|
|
2906
|
+
type: Input,
|
|
2907
|
+
args: ['disabled']
|
|
2888
2908
|
}], model: [{
|
|
2889
2909
|
type: Input,
|
|
2890
2910
|
args: ['model']
|
|
2911
|
+
}], placeholder: [{
|
|
2912
|
+
type: Input,
|
|
2913
|
+
args: ['placeholder']
|
|
2914
|
+
}], minDatetime: [{
|
|
2915
|
+
type: Input,
|
|
2916
|
+
args: ['minDatetime']
|
|
2917
|
+
}], maxDatetime: [{
|
|
2918
|
+
type: Input,
|
|
2919
|
+
args: ['maxDatetime']
|
|
2920
|
+
}], format: [{
|
|
2921
|
+
type: Input,
|
|
2922
|
+
args: ['format']
|
|
2923
|
+
}], displayFormatInput: [{
|
|
2924
|
+
type: Input,
|
|
2925
|
+
args: ['displayFormat']
|
|
2926
|
+
}], showSeconds: [{
|
|
2927
|
+
type: Input,
|
|
2928
|
+
args: ['showSeconds']
|
|
2929
|
+
}], enableMeridian: [{
|
|
2930
|
+
type: Input,
|
|
2931
|
+
args: ['enableMeridian']
|
|
2891
2932
|
}], skeletonLoading: [{
|
|
2892
2933
|
type: Input,
|
|
2893
2934
|
args: ['skeletonLoading']
|
|
@@ -2896,6 +2937,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
2896
2937
|
args: ['skeletonTheme']
|
|
2897
2938
|
}], modelChange: [{
|
|
2898
2939
|
type: Output
|
|
2940
|
+
}], changeEv: [{
|
|
2941
|
+
type: Output
|
|
2899
2942
|
}] } });
|
|
2900
2943
|
|
|
2901
2944
|
class OslSetupStateService {
|
|
@@ -5301,7 +5344,7 @@ class OslReportGrid {
|
|
|
5301
5344
|
asGroupRow = (row) => row;
|
|
5302
5345
|
asDataRow = (row) => row;
|
|
5303
5346
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslReportGrid, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5304
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslReportGrid, isStandalone: false, selector: "osl-report-grid", inputs: { columns: "columns", datasource: "datasource", loading: "loading", totalRecords: "totalRecords", autoMode: "autoMode", isPaginated: "isPaginated", pageSize: "pageSize", tableHeight: "tableHeight", striped: "striped", exportable: "exportable", rowHeight: "rowHeight", rowSelection: "rowSelection", showAggregates: "showAggregates", title: "title", pdfExportFromGrid: "pdfExportFromGrid", pdfConfig: "pdfConfig" }, outputs: { pageChange: "pageChange", pageSizeChange: "pageSizeChange", sortChange: "sortChange", rowClick: "rowClick", selectionChange: "selectionChange" }, host: { listeners: { "document:mousemove": "onMouseMove($event)", "document:mouseup": "onMouseUp()", "document:click": "onDocumentClick()" } }, providers: [DatePipe, DecimalPipe], viewQueries: [{ propertyName: "headerScrollRef", first: true, predicate: ["headerScrollRef"], descendants: true }, { propertyName: "bodyScrollRef", first: true, predicate: ["bodyScrollRef"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"osl-rg\" (click)=\"closeColumnMenu(); openFilterKey = null; $event.stopPropagation()\">\n\n <!-- \u2550\u2550 TOOLBAR \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"osl-rg-toolbar\">\n <div class=\"osl-rg-toolbar__left\">\n @if (title) { <span class=\"osl-rg-title\">{{ title }}</span> }\n @if (!loading) {\n @if (_filteredTotal !== datasource.length) {\n <span class=\"osl-rg-filter-badge\">\n <span class=\"osl-rg-filter-badge__dot\"></span>\n {{ _filteredTotal | number }} of {{ datasource.length | number }} rows\n </span>\n } @else {\n <span class=\"osl-rg-record-count\">{{ datasource.length | number }} rows</span>\n }\n }\n </div>\n <div class=\"osl-rg-toolbar__right\">\n\n <!-- Global search -->\n <div class=\"osl-rg-global-search\">\n <svg class=\"osl-rg-global-search__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"m21 21-4.35-4.35\"/>\n </svg>\n <input class=\"osl-rg-global-search__input\" placeholder=\"Search all columns\u2026\"\n [(ngModel)]=\"globalSearch\" (ngModelChange)=\"currentPage=1; processData()\" />\n @if (globalSearch) {\n <button class=\"osl-rg-global-search__clear\" (click)=\"globalSearch=''; processData()\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </button>\n }\n </div>\n\n <!-- Sort active badge -->\n @if (sortStates.size > 0) {\n <button class=\"osl-rg-toolbar-btn osl-rg-toolbar-btn--active\" (click)=\"clearSort()\" title=\"Clear sort\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M11 5H4M11 9H4M11 13H4M18 4v16M15 7l3-3 3 3M15 17l3 3 3-3\"/>\n </svg>\n <span>{{ sortStates.size }} sort{{ sortStates.size > 1 ? 's' : '' }}</span>\n <svg class=\"rg-icon rg-icon--xs\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </button>\n }\n\n <!-- Filter active badge -->\n @if (hasAnyFilter) {\n <button class=\"osl-rg-toolbar-btn osl-rg-toolbar-btn--danger\" (click)=\"clearAllFilters()\" title=\"Clear all filters\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M22 3H2l8 9.46V19l4 2V12.46z\"/></svg>\n <span>Filtered</span>\n <svg class=\"rg-icon rg-icon--xs\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </button>\n }\n\n <!-- Group toggle -->\n <button class=\"osl-rg-toolbar-btn\" [class.osl-rg-toolbar-btn--on]=\"showGroupPanel\"\n (click)=\"showGroupPanel=!showGroupPanel\" title=\"Group panel\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"2\" y=\"7\" width=\"20\" height=\"14\" rx=\"2\"/><path d=\"M16 3H8a2 2 0 0 0-2 2v2h12V5a2 2 0 0 0-2-2z\"/>\n </svg>\n <span>Group</span>\n </button>\n\n <!-- Expand / collapse when grouped -->\n @if (activeGroups.length > 0) {\n <button class=\"osl-rg-toolbar-btn\" (click)=\"expandAll()\" title=\"Expand all\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m7 20 5-5 5 5M7 4l5 5 5-5\"/></svg>\n </button>\n <button class=\"osl-rg-toolbar-btn\" (click)=\"collapseAll()\" title=\"Collapse all\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m7 15 5-5 5 5M7 9l5 5 5 5\"/></svg>\n </button>\n }\n\n <!-- Auto-size all -->\n <button class=\"osl-rg-toolbar-btn\" (click)=\"autoSizeAll()\" title=\"Auto-size all columns\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 6H3M21 18H3\"/><path d=\"M8 3 3 6l5 3M16 3l5 3-5 3M8 15l-5 3 5 3M16 15l5 3-5 3\"/>\n </svg>\n <span>Auto-size</span>\n </button>\n\n <!-- Column config -->\n <button class=\"osl-rg-toolbar-btn\" [class.osl-rg-toolbar-btn--on]=\"showColumnConfig\"\n (click)=\"showColumnConfig=!showColumnConfig; $event.stopPropagation()\" title=\"Column settings\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M4 6h16M4 12h16M4 18h16\"/>\n <circle cx=\"8\" cy=\"6\" r=\"2\" fill=\"white\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <circle cx=\"16\" cy=\"12\" r=\"2\" fill=\"white\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <circle cx=\"8\" cy=\"18\" r=\"2\" fill=\"white\" stroke=\"currentColor\" stroke-width=\"2\"/>\n </svg>\n <span>Columns</span>\n </button>\n\n <!-- Export Excel -->\n @if (exportable) {\n <button class=\"osl-rg-toolbar-btn osl-rg-toolbar-btn--excel\" (click)=\"exportExcel()\" title=\"Export to Excel\">\n <svg class=\"rg-icon rg-icon--excel\" viewBox=\"0 0 24 24\">\n <rect width=\"24\" height=\"24\" rx=\"3\" fill=\"#1D6F42\"/>\n <path d=\"M13.5 3H7a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7.5L13.5 3z\" fill=\"#fff\" opacity=\".15\"/>\n <path d=\"M13.5 3v4.5H18L13.5 3z\" fill=\"#fff\" opacity=\".3\"/>\n <path d=\"M8 9.5l2.3 3.5L8 16.5h1.6l1.4-2.3 1.4 2.3H14l-2.3-3.5 2.3-3.5h-1.6l-1.4 2.3-1.4-2.3H8z\" fill=\"#fff\"/>\n </svg>\n <span>Export Excel</span>\n </button>\n }\n\n <!-- Export PDF -->\n @if (exportable && pdfExportFromGrid) {\n <button class=\"osl-rg-toolbar-btn osl-rg-toolbar-btn--pdf\" (click)=\"exportPdf()\" title=\"Export to PDF\">\n <svg class=\"rg-icon rg-icon--pdf\" viewBox=\"0 0 24 24\">\n <rect width=\"24\" height=\"24\" rx=\"3\" fill=\"#D32F2F\"/>\n <path d=\"M13.5 3H7a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7.5L13.5 3z\" fill=\"#fff\" opacity=\".15\"/>\n <path d=\"M13.5 3v4.5H18L13.5 3z\" fill=\"#fff\" opacity=\".3\"/>\n <text x=\"4.5\" y=\"17.5\" fill=\"#fff\" font-size=\"6.5\" font-family=\"sans-serif\" font-weight=\"700\">PDF</text>\n </svg>\n <span>Export PDF</span>\n </button>\n }\n </div>\n </div>\n\n <!-- \u2550\u2550 GROUP PANEL \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (showGroupPanel) {\n <div class=\"osl-rg-group-panel\">\n <span class=\"osl-rg-group-panel__label\">Group by:</span>\n @if (activeGroups.length === 0) {\n <span class=\"osl-rg-group-panel__hint\">Use the \u22EE column menu \u2192 \"Group by this\"</span>\n }\n <div class=\"osl-rg-group-chips\" cdkDropList cdkDropListOrientation=\"horizontal\"\n [cdkDropListData]=\"activeGroups\" (cdkDropListDropped)=\"onGroupPanelDrop($event)\">\n @for (key of activeGroups; track key) {\n <div class=\"osl-rg-group-chip\" cdkDrag>\n <svg cdkDragHandle class=\"rg-drag-handle\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"9\" cy=\"5\" r=\"1.5\"/><circle cx=\"15\" cy=\"5\" r=\"1.5\"/>\n <circle cx=\"9\" cy=\"12\" r=\"1.5\"/><circle cx=\"15\" cy=\"12\" r=\"1.5\"/>\n <circle cx=\"9\" cy=\"19\" r=\"1.5\"/><circle cx=\"15\" cy=\"19\" r=\"1.5\"/>\n </svg>\n <span>{{ getGroupColLabel(key) }}</span>\n <button class=\"osl-rg-group-chip__remove\" (click)=\"removeGroup(key)\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </button>\n </div>\n }\n </div>\n @if (activeGroups.length > 0) {\n <button class=\"osl-rg-group-panel__clear\" (click)=\"clearGroups()\">Clear groups</button>\n }\n </div>\n }\n\n <!-- \u2550\u2550 STICKY HEADER \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"osl-rg-header-wrap\" #headerScrollRef>\n\n <!-- Double header \u2013 group row (only rendered when headerGroup is used on any column) -->\n @if (hasHeaderGroups) {\n <div class=\"osl-rg-header-row osl-rg-header-row--groups\" [style.minWidth.px]=\"totalWidth\">\n @if (rowSelection === 'multiple') {\n <div class=\"osl-rg-th osl-rg-th--checkbox osl-rg-th--group-span\"></div>\n }\n <div style=\"display:flex;flex:1;min-width:0;\">\n @for (group of computedHeaderGroups; track $index) {\n <div class=\"osl-rg-th osl-rg-th--group-span\"\n [class.osl-rg-th--group-span--labeled]=\"!!group.label\"\n [class.osl-rg-th--pinned]=\"group.isPinned\"\n [style.width.px]=\"group.width\"\n [style.minWidth.px]=\"group.width\"\n [style.left.px]=\"group.isPinned ? group.stickyLeft : null\"\n [style.position]=\"group.isPinned ? 'sticky' : 'relative'\"\n [style.zIndex]=\"group.isPinned ? 3 : 1\">\n @if (group.label) {\n <span class=\"osl-rg-th-group-label\">{{ group.label }}</span>\n }\n </div>\n }\n </div>\n </div>\n }\n\n <div class=\"osl-rg-header-row\" [style.minWidth.px]=\"totalWidth\">\n\n <!-- Select-all checkbox -->\n @if (rowSelection === 'multiple') {\n <div class=\"osl-rg-th osl-rg-th--checkbox\">\n <input type=\"checkbox\" class=\"osl-rg-checkbox\"\n [checked]=\"allSelected\" [indeterminate]=\"someSelected\"\n (change)=\"toggleSelectAll($any($event.target).checked)\" />\n </div>\n }\n\n <!-- Column headers (drag-to-reorder) -->\n <div class=\"osl-rg-header-cols\" cdkDropList cdkDropListOrientation=\"horizontal\"\n [cdkDropListData]=\"_cols\" (cdkDropListDropped)=\"onColumnReorder($event)\"\n style=\"display:flex;flex:1;min-width:0;\">\n\n @for (col of visibleCols; track col.key) {\n <div class=\"osl-rg-th\"\n [class.osl-rg-th--pinned]=\"col._pinned\"\n [class.osl-rg-th--sorted]=\"getSortState(col.key)\"\n [class.osl-rg-th--filtered]=\"hasActiveFilter(col.key)\"\n [style.width.px]=\"col._width\"\n [style.minWidth.px]=\"col._width\"\n [style.left.px]=\"col._pinned ? col._stickyLeft : null\"\n [style.position]=\"col._pinned ? 'sticky' : 'relative'\"\n [style.zIndex]=\"col._pinned ? 3 : 1\"\n cdkDrag [cdkDragDisabled]=\"col._pinned\"\n (cdkDragStarted)=\"onColumnDragStarted()\"\n (cdkDragEnded)=\"onColumnDragEnded()\"\n (click)=\"onHeaderClick(col, $event)\">\n\n <div *cdkDragPlaceholder class=\"osl-rg-th-drag-placeholder\"></div>\n\n <!-- Pin badge -->\n @if (col._pinned) {\n <span class=\"osl-rg-th-pin-badge\" title=\"Pinned\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"9\" height=\"9\"><path d=\"M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z\"/></svg>\n </span>\n }\n\n <!-- Label row -->\n <div class=\"osl-rg-th-content\">\n <span class=\"osl-rg-th-label\" [title]=\"col.label\">{{ col.label }}</span>\n <div class=\"osl-rg-th-actions\">\n\n <!-- Sort indicator -->\n @if (col.sortable) {\n <span class=\"osl-rg-sort-icon\"\n [class.osl-rg-sort-icon--asc]=\"getSortState(col.key)?.asc === true\"\n [class.osl-rg-sort-icon--desc]=\"getSortState(col.key)?.asc === false\">\n @if (getSortState(col.key); as s) {\n @if (s.asc) {\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 19V5m-7 7 7-7 7 7\"/></svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 5v14m7-7-7 7-7-7\"/></svg>\n }\n @if (sortStates.size > 1) { <sup class=\"osl-rg-sort-badge\">{{ s.index + 1 }}</sup> }\n } @else {\n <svg class=\"osl-rg-sort-idle\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 9l4-4 4 4M16 15l-4 4-4-4\"/></svg>\n }\n </span>\n }\n\n <!-- Excel filter button -->\n @if (col.filterable) {\n <button class=\"osl-rg-filter-btn\" [class.osl-rg-filter-btn--active]=\"hasActiveFilter(col.key)\"\n (click)=\"openExcelFilter(col.key, $event)\" title=\"Filter\">\n @if (hasActiveFilter(col.key)) {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M22 3H2l8 9.46V19l4 2V12.46z\"/></svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\"/></svg>\n }\n </button>\n }\n\n <!-- Column menu -->\n <button class=\"osl-rg-col-menu-btn\" (click)=\"openColumnMenu(col.key, $event)\" title=\"Column options\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><circle cx=\"12\" cy=\"5\" r=\"2\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/><circle cx=\"12\" cy=\"19\" r=\"2\"/></svg>\n </button>\n\n </div>\n </div>\n\n <!-- Per-column search -->\n @if (col.searchable) {\n <div class=\"osl-rg-col-search\" (click)=\"$event.stopPropagation()\">\n <svg class=\"osl-rg-col-search__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m18 18-3.5-3.5\"/>\n </svg>\n <input class=\"osl-rg-col-search__input\" [placeholder]=\"col.label + '\u2026'\"\n [ngModel]=\"columnSearch[col.key]\" (ngModelChange)=\"onColumnSearch(col.key, $event)\" />\n @if (columnSearch[col.key]) {\n <button class=\"osl-rg-col-search__clear\" (click)=\"columnSearch[col.key]=''; processData()\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </button>\n }\n </div>\n }\n\n <!-- Resize handle -->\n @if (col.resizable) {\n <div class=\"osl-rg-resize-handle\" (mousedown)=\"startResize(col, $event)\" (dblclick)=\"autoSizeColumn(col, $event)\" (click)=\"$event.stopPropagation()\"></div>\n }\n\n\n </div><!-- /osl-rg-th -->\n }\n\n </div><!-- /header-cols -->\n </div><!-- /header-row -->\n </div><!-- /header-wrap -->\n\n <!-- \u2550\u2550 SCROLLABLE BODY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"osl-rg-body\" [style.height]=\"tableHeight\" #bodyScrollRef (scroll)=\"onBodyScroll($event)\">\n <div class=\"osl-rg-inner\" [style.minWidth.px]=\"totalWidth\">\n\n <!-- Skeleton loading -->\n @if (loading) {\n @for (sk of skeletonRows; track $index) {\n <div class=\"osl-rg-row osl-rg-row--skeleton\" [style.height.px]=\"rowHeight\">\n @if (rowSelection === 'multiple') {\n <div class=\"osl-rg-td osl-rg-td--checkbox\"><div class=\"osl-rg-skel\" style=\"width:14px;height:14px;border-radius:3px;\"></div></div>\n }\n @for (col of visibleCols; track col.key) {\n <div class=\"osl-rg-td\" [style.width.px]=\"col._width\" [style.minWidth.px]=\"col._width\">\n <div class=\"osl-rg-skel\" [style.width]=\"$index % 3 === 0 ? '55%' : $index % 3 === 1 ? '80%' : '65%'\"></div>\n </div>\n }\n </div>\n }\n\n } @else if (isEmpty) {\n <!-- Empty state -->\n <div class=\"osl-rg-empty\">\n <div class=\"osl-rg-empty__icon\">\n <svg viewBox=\"0 0 48 48\" fill=\"none\" stroke=\"#d1d5db\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"6\" y=\"6\" width=\"36\" height=\"36\" rx=\"4\"/>\n <path d=\"M6 18h36M6 30h36M18 6v36\"/>\n </svg>\n </div>\n <p class=\"osl-rg-empty__title\">No records found</p>\n <p class=\"osl-rg-empty__sub\">\n @if (hasAnyFilter) { Try adjusting your filters or search terms }\n @else { No data to display }\n </p>\n @if (hasAnyFilter) {\n <button class=\"osl-rg-empty__action\" (click)=\"clearAllFilters()\">Clear all filters</button>\n }\n </div>\n\n } @else {\n <!-- Data rows -->\n @for (row of flatRows; track trackByRow($index, row)) {\n\n @if (row._type === 'group') {\n <div class=\"osl-rg-group-row\"\n [class.osl-rg-group-row--l0]=\"asGroupRow(row)._level === 0\"\n [class.osl-rg-group-row--l1]=\"asGroupRow(row)._level === 1\"\n [class.osl-rg-group-row--l2]=\"asGroupRow(row)._level >= 2\"\n [style.height.px]=\"rowHeight\"\n [style.paddingLeft.px]=\"asGroupRow(row)._level * 20 + 12\"\n (click)=\"toggleGroup(asGroupRow(row)._path)\">\n @if (asGroupRow(row)._expanded) {\n <svg class=\"osl-rg-group-chevron\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>\n } @else {\n <svg class=\"osl-rg-group-chevron\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m9 18 6-6-6-6\"/></svg>\n }\n <span class=\"osl-rg-group-row__key\">{{ asGroupRow(row)._colLabel }}:</span>\n <span class=\"osl-rg-group-row__value\">{{ asGroupRow(row)._label }}</span>\n <span class=\"osl-rg-group-row__count\">{{ asGroupRow(row)._count | number }} rows</span>\n </div>\n\n } @else {\n <div class=\"osl-rg-row\"\n [class.osl-rg-row--striped]=\"striped && asDataRow(row)._rowIndex % 2 === 1\"\n [class.osl-rg-row--selected]=\"selectedRows.has(asDataRow(row)._data)\"\n [style.height.px]=\"rowHeight\"\n (click)=\"onRowClick(asDataRow(row)._data, $event)\">\n\n @if (rowSelection === 'multiple') {\n <div class=\"osl-rg-td osl-rg-td--checkbox\">\n <input type=\"checkbox\" class=\"osl-rg-checkbox\"\n [checked]=\"selectedRows.has(asDataRow(row)._data)\"\n (change)=\"toggleRowSelect(asDataRow(row)._data, $event)\" />\n </div>\n }\n\n @for (col of visibleCols; track col.key) {\n <div class=\"osl-rg-td\"\n [class.osl-rg-td--pinned]=\"col._pinned\"\n [ngClass]=\"getCellClass(asDataRow(row)._data, col)\"\n [style.width.px]=\"col._width\"\n [style.minWidth.px]=\"col._width\"\n [style.left.px]=\"col._pinned ? col._stickyLeft : null\"\n [style.position]=\"col._pinned ? 'sticky' : 'relative'\"\n [style.zIndex]=\"col._pinned ? 2 : 0\"\n [style.textAlign]=\"col.align ?? 'left'\"\n (dblclick)=\"copyCell(getCellDisplay(asDataRow(row)._data, col), $event)\"\n [title]=\"getCellDisplay(asDataRow(row)._data, col)\">\n <span class=\"osl-rg-cell-text\">{{ getCellDisplay(asDataRow(row)._data, col) }}</span>\n </div>\n }\n\n </div>\n }\n\n }\n }\n\n </div><!-- /osl-rg-inner -->\n </div><!-- /osl-rg-body -->\n\n <!-- \u2550\u2550 AGGREGATE FOOTER \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (showAggregates && hasAnyAggregate() && !loading) {\n <div class=\"osl-rg-aggregate-row\" [style.minWidth.px]=\"totalWidth\">\n @if (rowSelection === 'multiple') { <div class=\"osl-rg-agg-cell osl-rg-agg-cell--checkbox\"></div> }\n @for (col of visibleCols; track col.key) {\n <div class=\"osl-rg-agg-cell\" [style.width.px]=\"col._width\" [style.minWidth.px]=\"col._width\" [style.textAlign]=\"col.align ?? 'left'\">\n @if (col.aggregate) {\n <span class=\"osl-rg-agg-label\">{{ col.aggregate | uppercase }}</span>\n <span class=\"osl-rg-agg-value\">{{ getAggregate(col) }}</span>\n }\n </div>\n }\n </div>\n }\n\n <!-- \u2550\u2550 PAGINATION \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (isPaginated) {\n <div class=\"osl-rg-pagination\">\n <span class=\"osl-rg-pagination__info\">\n @if (loading) { Loading\u2026 }\n @else if (_total > 0) { Showing <strong>{{ startRecord | number }}\u2013{{ endRecord | number }}</strong> of <strong>{{ _total | number }}</strong> records }\n @else { No records }\n </span>\n <div class=\"osl-rg-pagination__controls\">\n <button class=\"osl-rg-page-btn osl-rg-page-btn--nav\" (click)=\"goToPage(1)\" [disabled]=\"currentPage===1||loading\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><path d=\"m11 17-5-5 5-5M18 17l-5-5 5-5\"/></svg>\n </button>\n <button class=\"osl-rg-page-btn osl-rg-page-btn--nav\" (click)=\"goToPage(currentPage-1)\" [disabled]=\"currentPage===1||loading\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><path d=\"m15 18-6-6 6-6\"/></svg>\n Prev\n </button>\n @for (page of pageNumbers; track $index) {\n @if (page === -1) { <span class=\"osl-rg-page-ellipsis\">\u2026</span> }\n @else {\n <button class=\"osl-rg-page-btn osl-rg-page-btn--num\" [class.osl-rg-page-btn--active]=\"page===currentPage\"\n [disabled]=\"loading\" (click)=\"goToPage(page)\">{{ page }}</button>\n }\n }\n <button class=\"osl-rg-page-btn osl-rg-page-btn--nav\" (click)=\"goToPage(currentPage+1)\" [disabled]=\"currentPage===totalPages||loading\">\n Next\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><path d=\"m9 18 6-6-6-6\"/></svg>\n </button>\n <button class=\"osl-rg-page-btn osl-rg-page-btn--nav\" (click)=\"goToPage(totalPages)\" [disabled]=\"currentPage===totalPages||loading\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><path d=\"m13 17 5-5-5-5M6 17l5-5-5-5\"/></svg>\n </button>\n </div>\n <div class=\"osl-rg-pagination__size\">\n <span class=\"osl-rg-pagination__size-label\">Rows per page</span>\n <select class=\"osl-rg-page-size\" [ngModel]=\"pageSize\" (ngModelChange)=\"onPageSizeChange($event)\" [disabled]=\"loading\">\n @for (opt of pageSizeOptions; track opt) { <option [value]=\"opt\">{{ opt }}</option> }\n </select>\n </div>\n </div>\n }\n\n</div><!-- /osl-rg -->\n\n\n<!-- \u2550\u2550 EXCEL FILTER DROPDOWN (fixed, outside overflow containers) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (openFilterKey && excelFilterState[openFilterKey]) {\n <div class=\"osl-rg-excel-filter\"\n [style.top.px]=\"filterDropdownPos.top\"\n [style.left.px]=\"filterDropdownPos.left\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"osl-rg-excel-filter__header\">\n <div class=\"osl-rg-excel-filter__title\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"12\" height=\"12\" style=\"flex-shrink:0;\"><path d=\"M22 3H2l8 9.46V19l4 2V12.46z\"/></svg>\n Filter: {{ getColumnByKey(openFilterKey)?.label }}\n </div>\n @if (hasActiveFilter(openFilterKey)) {\n <button class=\"osl-rg-excel-filter__clear-btn\" (click)=\"clearExcelFilter(openFilterKey!)\">Clear</button>\n }\n </div>\n <div class=\"osl-rg-excel-filter__search\">\n <input class=\"osl-rg-excel-filter__search-input\" placeholder=\"Search values\u2026\"\n [(ngModel)]=\"excelFilterState[openFilterKey].search\" />\n </div>\n <div class=\"osl-rg-excel-filter__select-all\">\n <label class=\"osl-rg-excel-filter__item osl-rg-excel-filter__item--all\">\n <input type=\"checkbox\" class=\"osl-rg-checkbox\" [checked]=\"isAllExcelChecked(openFilterKey)\"\n (change)=\"toggleAllExcelFilter(openFilterKey!, $any($event.target).checked)\" />\n <span class=\"osl-rg-excel-filter__item-label\">(Select All)</span>\n </label>\n </div>\n <div class=\"osl-rg-excel-filter__list\">\n @for (item of getFilteredExcelItems(openFilterKey); track item.value) {\n <label class=\"osl-rg-excel-filter__item\">\n <input type=\"checkbox\" class=\"osl-rg-checkbox\" [(ngModel)]=\"item.checked\" />\n <span class=\"osl-rg-excel-filter__item-label\">{{ item.label }}</span>\n </label>\n }\n </div>\n <div class=\"osl-rg-excel-filter__footer\">\n <button class=\"osl-rg-excel-filter__apply\" (click)=\"applyExcelFilter(openFilterKey!)\">Apply Filter</button>\n <button class=\"osl-rg-excel-filter__cancel\" (click)=\"openFilterKey=null\">Cancel</button>\n </div>\n </div>\n}\n\n\n<!-- \u2550\u2550 COLUMN CONTEXT MENU (fixed) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (columnMenuKey && getColumnByKey(columnMenuKey); as menuCol) {\n <div class=\"osl-rg-col-menu\"\n [style.top.px]=\"columnMenuPos.top\" [style.left.px]=\"columnMenuPos.left\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"osl-rg-col-menu__header\">{{ menuCol.label }}</div>\n @if (menuCol.sortable) {\n <button class=\"osl-rg-col-menu__item\"\n (click)=\"sortStates.clear(); sortStates.set(menuCol.key,{key:menuCol.key,asc:true,index:0}); sortCounter=1; processData(); closeColumnMenu()\">\n <svg class=\"rg-icon rg-icon--sm\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 19V5m-7 7 7-7 7 7\"/></svg>\n Sort A \u2192 Z\n </button>\n <button class=\"osl-rg-col-menu__item\"\n (click)=\"sortStates.clear(); sortStates.set(menuCol.key,{key:menuCol.key,asc:false,index:0}); sortCounter=1; processData(); closeColumnMenu()\">\n <svg class=\"rg-icon rg-icon--sm\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 5v14m7-7-7 7-7-7\"/></svg>\n Sort Z \u2192 A\n </button>\n }\n @if (menuCol.groupable) {\n <div class=\"osl-rg-col-menu__divider\"></div>\n <button class=\"osl-rg-col-menu__item\" (click)=\"addGroup(menuCol.key); closeColumnMenu()\">\n <svg class=\"rg-icon rg-icon--sm\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2\" y=\"7\" width=\"20\" height=\"14\" rx=\"2\"/><path d=\"M16 3H8a2 2 0 0 0-2 2v2h12V5a2 2 0 0 0-2-2z\"/></svg>\n Group by this\n </button>\n }\n <div class=\"osl-rg-col-menu__divider\"></div>\n <button class=\"osl-rg-col-menu__item\" (click)=\"toggleColumnPin(menuCol)\">\n <svg class=\"rg-icon rg-icon--sm\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z\"/></svg>\n {{ menuCol._pinned ? 'Unpin column' : 'Pin to left' }}\n </button>\n <button class=\"osl-rg-col-menu__item\" (click)=\"autoSizeColumn(menuCol); closeColumnMenu()\">\n <svg class=\"rg-icon rg-icon--sm\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 6H3M21 18H3\"/><path d=\"M8 3 3 6l5 3M16 3l5 3-5 3M8 15l-5 3 5 3M16 15l5 3-5 3\"/></svg>\n Auto-size column\n </button>\n <div class=\"osl-rg-col-menu__divider\"></div>\n <button class=\"osl-rg-col-menu__item osl-rg-col-menu__item--danger\" (click)=\"toggleColumnVisibility(menuCol); closeColumnMenu()\">\n <svg class=\"rg-icon rg-icon--sm\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24M1 1l22 22\"/>\n </svg>\n Hide column\n </button>\n </div>\n}\n\n\n<!-- \u2550\u2550 COLUMN CONFIG SIDE DRAWER \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (showColumnConfig) {\n <div class=\"osl-rg-config-backdrop\" (click)=\"showColumnConfig=false\"></div>\n <div class=\"osl-rg-config-panel\" (click)=\"$event.stopPropagation()\">\n <div class=\"osl-rg-config-panel__header\">\n <div class=\"osl-rg-config-panel__title\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"16\" height=\"16\">\n <path d=\"M4 6h16M4 12h16M4 18h16\"/>\n <circle cx=\"8\" cy=\"6\" r=\"2\" fill=\"white\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <circle cx=\"16\" cy=\"12\" r=\"2\" fill=\"white\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <circle cx=\"8\" cy=\"18\" r=\"2\" fill=\"white\" stroke=\"currentColor\" stroke-width=\"2\"/>\n </svg>\n Column Settings\n </div>\n <button class=\"osl-rg-config-panel__close\" (click)=\"showColumnConfig=false\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </button>\n </div>\n <div class=\"osl-rg-config-panel__actions\">\n <button class=\"osl-rg-config-action\" (click)=\"autoSizeAll()\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><path d=\"M21 6H3M21 18H3\"/><path d=\"M8 3 3 6l5 3M16 3l5 3-5 3\"/></svg>\n Auto-size all\n </button>\n <button class=\"osl-rg-config-action\" (click)=\"showAllColumns()\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>\n Show all\n </button>\n </div>\n <div class=\"osl-rg-config-hint\">Drag to reorder \u00B7 Toggle to show/hide \u00B7 Pin to freeze</div>\n <div class=\"osl-rg-config-list\" cdkDropList (cdkDropListDropped)=\"onConfigColumnReorder($event)\">\n @for (col of _cols; track col.key) {\n <div class=\"osl-rg-config-item\" cdkDrag [class.osl-rg-config-item--hidden]=\"col._hidden\">\n <svg cdkDragHandle class=\"rg-drag-handle\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"9\" cy=\"5\" r=\"1.5\"/><circle cx=\"15\" cy=\"5\" r=\"1.5\"/>\n <circle cx=\"9\" cy=\"12\" r=\"1.5\"/><circle cx=\"15\" cy=\"12\" r=\"1.5\"/>\n <circle cx=\"9\" cy=\"19\" r=\"1.5\"/><circle cx=\"15\" cy=\"19\" r=\"1.5\"/>\n </svg>\n <input type=\"checkbox\" class=\"osl-rg-checkbox\" [checked]=\"!col._hidden\" (change)=\"toggleColumnVisibility(col)\" />\n <span class=\"osl-rg-config-item__label\" [class.osl-rg-config-item__label--hidden]=\"col._hidden\">{{ col.label }}</span>\n <button class=\"osl-rg-config-item__pin-btn\" (click)=\"toggleColumnPin(col)\"\n [class.osl-rg-config-item__pin-btn--active]=\"col._pinned\"\n [title]=\"col._pinned ? 'Unpin' : 'Pin to left'\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"12\" height=\"12\"><path d=\"M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z\"/></svg>\n </button>\n </div>\n }\n </div>\n </div>\n}\n\n\n<!-- \u2550\u2550 COPY TOAST \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (copiedCell !== null) {\n <div class=\"osl-rg-copy-toast\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><polyline points=\"20 6 9 17 4 12\"/></svg>\n Copied to clipboard\n </div>\n}\n", styles: ["@keyframes rg-pulse{0%,to{opacity:1}50%{opacity:.4}}@keyframes rg-fade-up{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes rg-slide-in{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}.rg-icon{width:15px;height:15px;flex-shrink:0;display:block}.rg-icon--sm{width:14px;height:14px}.rg-icon--xs{width:12px;height:12px}.rg-icon--excel{width:18px;height:18px}.rg-drag-handle{width:14px;height:14px;flex-shrink:0;color:#d1d5db;cursor:grab;display:block}.rg-drag-handle:active{cursor:grabbing}.osl-rg{position:relative;display:flex;flex-direction:column;width:100%;font-family:inherit;font-size:13px;color:#111827;background:#fff;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:10px;overflow:hidden;box-shadow:0 2px 8px #00000012,0 1px 2px #0000000a;-webkit-user-select:none;user-select:none}.osl-rg-toolbar{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 14px;background:linear-gradient(135deg,#f8faff,#f3f4f6);border-bottom:1px solid var(--osl-border-color, #e5e7eb);flex-shrink:0;flex-wrap:wrap}.osl-rg-toolbar__left{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.osl-rg-toolbar__right{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.osl-rg-title{font-size:14px;font-weight:700;color:#111827;letter-spacing:-.01em}.osl-rg-record-count{display:inline-flex;align-items:center;gap:5px;font-size:12px;color:#9ca3af;font-weight:500}.osl-rg-record-count svg{color:#d1d5db}.osl-rg-filter-badge{display:inline-flex;align-items:center;gap:5px;padding:2px 9px 2px 6px;background:#eff6ff;border:1px solid #bfdbfe;border-radius:12px;font-size:11px;color:#1d4ed8;font-weight:600}.osl-rg-filter-badge__dot{width:6px;height:6px;border-radius:50%;background:#2563eb;animation:rg-pulse 1.5s ease-in-out infinite;flex-shrink:0}.osl-rg-toolbar-btn{display:inline-flex;align-items:center;gap:5px;height:32px;padding:0 11px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:7px;background:#fff;color:#5a6478;font-size:12px;font-family:inherit;font-weight:500;cursor:pointer;white-space:nowrap;transition:all .15s ease}.osl-rg-toolbar-btn svg{flex-shrink:0}.osl-rg-toolbar-btn:hover{background:#f3f4f6;color:#1f2937;border-color:#c4c9d4;box-shadow:0 1px 3px #0000000f}.osl-rg-toolbar-btn--on{background:#eff6ff;border-color:#93c5fd;color:#1d4ed8}.osl-rg-toolbar-btn--active{background:#eff6ff;border-color:#93c5fd;color:#1d4ed8;font-weight:600}.osl-rg-toolbar-btn--danger{background:#fff5f5;border-color:#fca5a5;color:#dc2626}.osl-rg-toolbar-btn--danger:hover{background:#fee2e2;border-color:#f87171}.osl-rg-toolbar-btn--excel{background:linear-gradient(135deg,#1a7a3e,#1d6f42);border-color:#166534;color:#fff;font-weight:600;box-shadow:0 1px 3px #1665344d}.osl-rg-toolbar-btn--excel:hover{background:linear-gradient(135deg,#166534,#145a2c);border-color:#14532d;color:#fff;box-shadow:0 2px 6px #16653466}.osl-rg-toolbar-btn--pdf{background:linear-gradient(135deg,#e53935,#c62828);border-color:#b71c1c;color:#fff;font-weight:600;box-shadow:0 1px 3px #b71c1c4d}.osl-rg-toolbar-btn--pdf:hover{background:linear-gradient(135deg,#c62828,#ad1515);border-color:#ad1515;color:#fff;box-shadow:0 2px 6px #b71c1c66}.rg-icon--pdf{width:18px;height:18px}.osl-rg-global-search{position:relative;display:inline-flex;align-items:center}.osl-rg-global-search__icon{position:absolute;left:9px;width:14px;height:14px;color:#9ca3af;pointer-events:none;display:block}.osl-rg-global-search__input{height:32px;padding:0 30px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:7px;background:#fff;font-size:12.5px;font-family:inherit;color:#111827;outline:none;width:210px;transition:border-color .15s,width .2s,box-shadow .15s}.osl-rg-global-search__input::placeholder{color:#b0b7c3}.osl-rg-global-search__input:focus{border-color:var(--osl-primary, #2563eb);width:260px;box-shadow:0 0 0 3px #2563eb1a}.osl-rg-global-search__clear{position:absolute;right:6px;display:flex;align-items:center;justify-content:center;width:18px;height:18px;border:none;background:transparent;color:#9ca3af;cursor:pointer;padding:0;border-radius:4px;transition:all .12s}.osl-rg-global-search__clear svg{width:12px;height:12px}.osl-rg-global-search__clear:hover{color:#374151;background:#0000000d}.osl-rg-group-panel{display:flex;align-items:center;gap:8px;padding:6px 14px;background:linear-gradient(135deg,#fafbff,#f5f7ff);border-bottom:1px dashed #dde5ff;min-height:40px;flex-shrink:0;flex-wrap:wrap}.osl-rg-group-panel__label{display:flex;align-items:center;gap:5px;font-size:11px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;white-space:nowrap;flex-shrink:0}.osl-rg-group-panel__label svg{color:#9ca3af}.osl-rg-group-panel__hint{font-size:11px;color:#b0b7c3;font-style:italic}.osl-rg-group-panel__clear{margin-left:auto;font-size:11px;color:#6b7280;background:none;border:none;cursor:pointer;text-decoration:underline;flex-shrink:0;padding:0;font-family:inherit}.osl-rg-group-panel__clear:hover{color:#dc2626}.osl-rg-group-chips{display:flex;gap:6px;flex-wrap:wrap;align-items:center;flex:1;min-height:26px}.osl-rg-group-chip{display:inline-flex;align-items:center;gap:5px;padding:3px 6px 3px 5px;background:linear-gradient(135deg,#dbeafe,#eff6ff);border:1px solid #bfdbfe;border-radius:20px;font-size:12px;font-weight:600;color:#1e40af;cursor:grab;-webkit-user-select:none;user-select:none;box-shadow:0 1px 2px #2563eb1a;transition:box-shadow .1s}.osl-rg-group-chip:active{cursor:grabbing;box-shadow:0 2px 6px #2563eb33}.osl-rg-group-chip .rg-drag-handle{color:#93c5fd}.osl-rg-group-chip__remove{display:flex;align-items:center;justify-content:center;width:16px;height:16px;border:none;background:transparent;cursor:pointer;padding:0;border-radius:50%;transition:all .1s}.osl-rg-group-chip__remove svg{width:10px;height:10px;color:#93c5fd}.osl-rg-group-chip__remove:hover{background:#dbeafe}.osl-rg-group-chip__remove:hover svg{color:#1e40af}.osl-rg-table-area{display:flex;flex-direction:column;flex:1 1 0;overflow:hidden;position:relative;min-height:0}.osl-rg-header-wrap{overflow:hidden;flex-shrink:0;border-bottom:2px solid #dde5ff;background:linear-gradient(180deg,#f0f4ff,#e8efff);z-index:10;position:relative}.osl-rg-header-row{display:flex;align-items:stretch}.osl-rg-header-row--groups{border-bottom:1px solid #c7d7f0}.osl-rg-th--group-span{display:flex;align-items:center;justify-content:center;height:28px;min-height:28px;cursor:default;padding:0;border-right:1px solid #dde5ff}.osl-rg-th--group-span:hover{background:transparent!important}.osl-rg-th--group-span:last-child{border-right:none}.osl-rg-th--group-span--labeled{background:linear-gradient(135deg,#dbeafe,#eff6ff);border-bottom:2px solid #93c5fd}.osl-rg-th-group-label{font-size:11px;font-weight:700;color:#1e40af;text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:0 10px;text-align:center;width:100%}.osl-rg-header-cols{display:flex}.osl-rg-th{position:relative;display:flex;flex-direction:column;background:transparent;border-right:1px solid #dde5ff;cursor:pointer;flex-shrink:0;transition:background .12s;overflow:visible}.osl-rg-th:last-child{border-right:none}.osl-rg-th:hover{background:#2563eb0f}.osl-rg-th--pinned{background:linear-gradient(180deg,#e8efff,#dde8ff);border-right:1px solid #bfdbfe;z-index:3}.osl-rg-th--pinned:after{content:\"\";position:absolute;right:-5px;top:0;bottom:0;width:5px;background:linear-gradient(to right,rgba(37,99,235,.14),transparent);pointer-events:none;z-index:4}.osl-rg-th--sorted{background:#6366f114}.osl-rg-th--filtered{background:#f59e0b12}.osl-rg-th-drag-placeholder{background:#e0e7ff;border:2px dashed #6366f1;border-radius:4px;opacity:.6}.osl-rg-th-pin-badge{position:absolute;top:3px;right:24px;color:var(--osl-primary, #2563eb);opacity:.6;display:flex;align-items:center}.osl-rg-th-content{display:flex;align-items:center;justify-content:space-between;gap:4px;padding:0 8px 0 10px;height:36px;min-height:36px}.osl-rg-th-label{font-size:11.5px;font-weight:700;color:#1e3a6e;text-transform:uppercase;letter-spacing:.05em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.osl-rg-th-actions{display:flex;align-items:center;gap:2px;flex-shrink:0}.osl-rg-sort-icon{display:inline-flex;align-items:center;position:relative;color:#9ca3af;transition:color .12s;width:14px;height:14px}.osl-rg-sort-icon svg{width:13px;height:13px;display:block}.osl-rg-sort-icon--asc,.osl-rg-sort-icon--desc{color:var(--osl-primary, #2563eb)}.osl-rg-sort-idle{opacity:.3}.osl-rg-sort-badge{position:absolute;top:-4px;right:-6px;font-size:8px;font-weight:700;background:var(--osl-primary, #2563eb);color:#fff;border-radius:50%;width:11px;height:11px;display:flex;align-items:center;justify-content:center;line-height:1}.osl-rg-filter-btn,.osl-rg-col-menu-btn{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:transparent;color:#b0b7c3;cursor:pointer;padding:0;border-radius:5px;transition:all .12s}.osl-rg-filter-btn svg,.osl-rg-col-menu-btn svg{width:13px;height:13px;display:block}.osl-rg-filter-btn:hover,.osl-rg-col-menu-btn:hover{background:#00000012;color:#374151}.osl-rg-filter-btn--active{color:var(--osl-primary, #2563eb)!important;background:#2563eb1f!important}.osl-rg-col-search{position:relative;display:flex;align-items:center;padding:3px 6px;border-top:1px solid #e0e7ff;background:#ffffff80}.osl-rg-col-search__icon{position:absolute;left:10px;width:12px;height:12px;display:block;pointer-events:none;color:#c4cad6}.osl-rg-col-search__input{width:100%;height:23px;padding:0 18px 0 22px;border:1px solid #dde5ff;border-radius:5px;font-size:11px;font-family:inherit;background:#fff;color:#111827;outline:none;transition:border-color .12s,box-shadow .12s}.osl-rg-col-search__input::placeholder{color:#c4cad6}.osl-rg-col-search__input:focus{border-color:var(--osl-primary, #2563eb);box-shadow:0 0 0 2px #2563eb1a}.osl-rg-col-search__clear{position:absolute;right:8px;display:flex;align-items:center;width:14px;height:14px;border:none;background:transparent;color:#9ca3af;cursor:pointer;padding:0;border-radius:3px;transition:all .1s}.osl-rg-col-search__clear svg{width:10px;height:10px;display:block}.osl-rg-col-search__clear:hover{color:#374151;background:#0000000f}.osl-rg-resize-handle{position:absolute;right:0;top:0;bottom:0;width:6px;cursor:col-resize;z-index:5}.osl-rg-resize-handle:after{content:\"\";position:absolute;right:2px;top:15%;bottom:15%;width:2px;border-radius:2px;background:#a5b4fc;opacity:0;transition:opacity .15s}.osl-rg-resize-handle:hover:after{opacity:1}.osl-rg-th:hover .osl-rg-resize-handle:after{opacity:.4}.osl-rg-excel-filter{position:fixed;z-index:9999;width:250px;background:#fff;border:1px solid #e5e7eb;border-radius:10px;box-shadow:0 12px 32px #00000026,0 2px 8px #00000012;animation:rg-fade-up .14s ease;overflow:hidden}.osl-rg-excel-filter__header{display:flex;align-items:center;justify-content:space-between;padding:9px 12px;background:linear-gradient(135deg,#f0f4ff,#e8efff);border-bottom:1px solid #dde5ff}.osl-rg-excel-filter__title{display:flex;align-items:center;gap:6px;font-size:11px;font-weight:700;color:#1e3a6e;text-transform:uppercase;letter-spacing:.06em}.osl-rg-excel-filter__clear-btn{font-size:11px;color:#dc2626;background:none;border:none;cursor:pointer;font-weight:600;padding:2px 6px;border-radius:4px;font-family:inherit;transition:background .1s}.osl-rg-excel-filter__clear-btn:hover{background:#fee2e2}.osl-rg-excel-filter__search{padding:7px 8px;border-bottom:1px solid #f3f4f6;position:relative}.osl-rg-excel-filter__search-input{width:100%;height:28px;padding:0 8px 0 28px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit;outline:none;box-sizing:border-box;transition:border-color .12s;color:#111827}.osl-rg-excel-filter__search-input::placeholder{color:#b0b7c3}.osl-rg-excel-filter__search-input:focus{border-color:var(--osl-primary, #2563eb)}.osl-rg-excel-filter__select-all{padding:0 8px;border-bottom:2px solid #f3f4f6;background:#fafbff}.osl-rg-excel-filter__list{max-height:210px;overflow-y:auto;padding:3px 8px}.osl-rg-excel-filter__list::-webkit-scrollbar{width:5px}.osl-rg-excel-filter__list::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:4px}.osl-rg-excel-filter__list::-webkit-scrollbar-thumb:hover{background:#9ca3af}.osl-rg-excel-filter__item{display:flex;align-items:center;gap:8px;padding:5px 4px;font-size:12.5px;color:#374151;cursor:pointer;border-radius:5px;transition:background .1s}.osl-rg-excel-filter__item:hover{background:#f3f4f6}.osl-rg-excel-filter__item--all{font-weight:600;color:#1f2937;padding:6px 4px}.osl-rg-excel-filter__item-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.osl-rg-excel-filter__footer{display:flex;gap:6px;padding:8px 10px;border-top:1px solid #f3f4f6;background:#f9fafb}.osl-rg-excel-filter__apply{flex:1;height:30px;border:none;border-radius:6px;font-size:12px;font-family:inherit;font-weight:600;cursor:pointer;background:var(--osl-primary, #2563eb);color:#fff;transition:background .12s}.osl-rg-excel-filter__apply:hover{background:#1d4ed8}.osl-rg-excel-filter__cancel{height:30px;padding:0 14px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit;font-weight:500;cursor:pointer;background:#fff;color:#6b7280;transition:all .12s}.osl-rg-excel-filter__cancel:hover{background:#f3f4f6;border-color:#9ca3af}.osl-rg-th--checkbox,.osl-rg-td--checkbox{display:flex;align-items:center;justify-content:center;width:36px;min-width:36px;flex-shrink:0;border-right:1px solid #dde5ff}.osl-rg-checkbox{width:14px;height:14px;cursor:pointer;accent-color:var(--osl-primary, #2563eb);flex-shrink:0}.osl-rg-body{overflow-x:auto;overflow-y:auto;flex-shrink:0}.osl-rg-body::-webkit-scrollbar{width:8px;height:8px}.osl-rg-body::-webkit-scrollbar-track{background:#f9fafb}.osl-rg-body::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:4px}.osl-rg-body::-webkit-scrollbar-thumb:hover{background:#9ca3af}.osl-rg-body::-webkit-scrollbar-corner{background:#f9fafb}.osl-rg-inner{display:block}.osl-rg-row{display:flex;align-items:stretch;border-bottom:1px solid #f3f4f6;transition:background .08s;cursor:default}.osl-rg-row:hover{background:#f0f5ff!important}.osl-rg-row--striped{background:#fafbff}.osl-rg-row--selected{background:#eff6ff!important;border-bottom-color:#dbeafe}.osl-rg-row--skeleton{pointer-events:none}.osl-rg-td{display:flex;align-items:center;padding:0 12px;border-right:1px solid #f0f2f7;overflow:hidden;background:inherit;flex-shrink:0}.osl-rg-td:last-child{border-right:none}.osl-rg-td--pinned{background:#fff;border-right:1px solid #e0e7ff;z-index:2}.osl-rg-td--pinned:after{content:\"\";position:absolute;right:-5px;top:0;bottom:0;width:5px;background:linear-gradient(to right,rgba(37,99,235,.07),transparent);pointer-events:none}.osl-rg-cell-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;width:100%;font-size:13px;color:#1f2937;line-height:1.4}.rg-profit-pos .osl-rg-cell-text{color:#15803d;font-weight:600}.rg-profit-neg .osl-rg-cell-text{color:#dc2626;font-weight:600}.rg-status-delivered .osl-rg-cell-text{color:#15803d;font-weight:600}.rg-status-shipped .osl-rg-cell-text{color:#2563eb;font-weight:600}.rg-status-processing .osl-rg-cell-text{color:#d97706;font-weight:600}.rg-status-pending .osl-rg-cell-text{color:#6b7280;font-weight:500}.rg-status-cancelled .osl-rg-cell-text{color:#dc2626;font-weight:500;text-decoration:line-through;opacity:.75}.osl-rg-group-row{display:flex;align-items:center;gap:7px;border-bottom:1px solid #e5e7eb;font-weight:600;cursor:pointer;flex-shrink:0;transition:filter .1s}.osl-rg-group-row--l0{background:linear-gradient(135deg,#eff6ff,#dbeafe);color:#1e40af;border-left:4px solid #3b82f6}.osl-rg-group-row--l1{background:linear-gradient(135deg,#f5f3ff,#ede9fe);color:#5b21b6;border-left:4px solid #8b5cf6}.osl-rg-group-row--l2{background:linear-gradient(135deg,#fff7ed,#fef3c7);color:#92400e;border-left:4px solid #f59e0b}.osl-rg-group-row:hover{filter:brightness(.97)}.osl-rg-group-row__key{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;opacity:.65;flex-shrink:0}.osl-rg-group-row__value{font-size:13px;font-weight:700;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.osl-rg-group-row__count{font-size:11px;font-weight:600;padding:2px 9px;border-radius:12px;background:#00000017;margin-right:12px;flex-shrink:0}.osl-rg-group-chevron{width:16px;height:16px;flex-shrink:0;opacity:.75;display:block}.osl-rg-aggregate-row{display:flex;align-items:center;border-top:2px solid #e5e7eb;background:linear-gradient(135deg,#f0fdf4,#f9fafb);flex-shrink:0;min-height:38px}.osl-rg-agg-cell{display:flex;align-items:center;gap:5px;padding:0 12px;border-right:1px solid #e5e7eb;height:38px;flex-shrink:0;overflow:hidden}.osl-rg-agg-cell--checkbox{width:36px;min-width:36px}.osl-rg-agg-label{font-size:9.5px;font-weight:800;color:#9ca3af;text-transform:uppercase;letter-spacing:.06em;flex-shrink:0;background:#e5e7eb;padding:1px 4px;border-radius:3px}.osl-rg-agg-value{font-size:12px;font-weight:700;color:#15803d;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.osl-rg-skel{height:12px;border-radius:4px;background:linear-gradient(90deg,#e5e7eb 25%,#f3f4f6,#e5e7eb 75%);background-size:200% 100%;animation:rg-pulse 1.5s ease-in-out infinite}.osl-rg-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:64px 20px;color:#9ca3af;text-align:center}.osl-rg-empty__icon{margin-bottom:18px;opacity:.7}.osl-rg-empty__title{font-size:15px;font-weight:600;color:#374151;margin:0 0 6px}.osl-rg-empty__sub{font-size:13px;color:#9ca3af;margin:0 0 18px;max-width:290px;line-height:1.5}.osl-rg-empty__action{height:34px;padding:0 18px;background:var(--osl-primary, #2563eb);color:#fff;border:none;border-radius:7px;font-size:13px;font-family:inherit;font-weight:600;cursor:pointer;transition:background .15s;box-shadow:0 2px 6px #2563eb4d}.osl-rg-empty__action:hover{background:#1d4ed8}.osl-rg-pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--osl-border-color, #e5e7eb);background:linear-gradient(135deg,#f8faff,#f3f4f6);flex-shrink:0;flex-wrap:wrap}.osl-rg-pagination__info{font-size:12px;color:#6b7280;min-width:160px;white-space:nowrap}.osl-rg-pagination__info strong{color:#1f2937;font-weight:600}.osl-rg-pagination__controls{display:flex;align-items:center;gap:3px}.osl-rg-pagination__size{display:flex;align-items:center;gap:7px;min-width:120px;justify-content:flex-end}.osl-rg-pagination__size-label{font-size:11.5px;color:#9ca3af;white-space:nowrap}.osl-rg-page-btn{display:inline-flex;align-items:center;justify-content:center;gap:3px;height:30px;padding:0 8px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:6px;background:#fff;color:#374151;font-size:12px;font-family:inherit;font-weight:500;cursor:pointer;transition:all .12s;white-space:nowrap}.osl-rg-page-btn svg{display:block}.osl-rg-page-btn:hover:not(:disabled){background:#f3f4f6;border-color:#9ca3af;box-shadow:0 1px 2px #0000000f}.osl-rg-page-btn:disabled{opacity:.35;cursor:not-allowed}.osl-rg-page-btn--num{min-width:30px;padding:0;font-size:12.5px}.osl-rg-page-btn--active{background:var(--osl-primary, #2563eb);border-color:var(--osl-primary, #2563eb);color:#fff;font-weight:700;box-shadow:0 2px 5px #2563eb59}.osl-rg-page-btn--active:hover:not(:disabled){background:#1d4ed8;border-color:#1d4ed8}.osl-rg-page-ellipsis{display:inline-flex;align-items:center;justify-content:center;width:26px;color:#9ca3af;font-size:13px;pointer-events:none}.osl-rg-page-size{height:30px;padding:0 8px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:6px;background:#fff;font-size:12px;font-family:inherit;color:#374151;cursor:pointer;outline:none;transition:border-color .15s}.osl-rg-page-size:focus{border-color:var(--osl-primary, #2563eb)}.osl-rg-col-menu{position:fixed;z-index:9999;min-width:210px;background:#fff;border:1px solid #e5e7eb;border-radius:11px;box-shadow:0 12px 36px #00000029,0 3px 10px #00000014;overflow:hidden;animation:rg-fade-up .13s cubic-bezier(.16,1,.3,1)}.osl-rg-col-menu__header{padding:9px 14px 7px;font-size:10.5px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:#9ca3af;border-bottom:1px solid #f3f4f6;background:#fafbff}.osl-rg-col-menu__divider{height:1px;background:#f3f4f6;margin:2px 0}.osl-rg-col-menu__item{display:flex;align-items:center;gap:9px;width:100%;padding:9px 14px;background:transparent;border:none;border-left:3px solid transparent;border-bottom:none;text-align:left;font-size:13px;font-weight:500;font-family:inherit;color:#374151;cursor:pointer;transition:all .1s}.osl-rg-col-menu__item svg{color:#9ca3af;display:block}.osl-rg-col-menu__item:hover{background:#f0f4ff;color:var(--osl-primary, #2563eb);border-left-color:var(--osl-primary, #2563eb)}.osl-rg-col-menu__item:hover svg{color:var(--osl-primary, #2563eb)}.osl-rg-col-menu__item--danger:hover{background:#fff5f5;color:#dc2626;border-left-color:#dc2626}.osl-rg-col-menu__item--danger:hover svg{color:#dc2626}.osl-rg-config-backdrop{position:fixed;inset:0;z-index:9000;background:#0003;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}.osl-rg-config-panel{position:fixed;top:0;right:0;bottom:0;z-index:9001;width:300px;background:#fff;border-left:1px solid #e5e7eb;box-shadow:-6px 0 24px #00000021;display:flex;flex-direction:column;animation:rg-slide-in .2s cubic-bezier(.16,1,.3,1)}.osl-rg-config-panel__header{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid #e5e7eb;background:linear-gradient(135deg,#f0f4ff,#e8efff);flex-shrink:0}.osl-rg-config-panel__title{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:700;color:#1e3a6e}.osl-rg-config-panel__title svg{color:#3b82f6}.osl-rg-config-panel__close{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border:none;background:transparent;color:#9ca3af;cursor:pointer;border-radius:7px;padding:0;transition:all .12s}.osl-rg-config-panel__close svg{width:16px;height:16px;display:block}.osl-rg-config-panel__close:hover{background:#00000012;color:#374151}.osl-rg-config-panel__actions{display:flex;gap:8px;padding:10px 14px;border-bottom:1px solid #f3f4f6;flex-shrink:0;background:#fafbff}.osl-rg-config-hint{padding:6px 16px;font-size:10.5px;color:#b0b7c3;font-style:italic;border-bottom:1px solid #f3f4f6;flex-shrink:0}.osl-rg-config-action{display:inline-flex;align-items:center;gap:5px;height:30px;padding:0 10px;border:1px solid #e5e7eb;border-radius:6px;background:#fff;color:#374151;font-size:12px;font-family:inherit;font-weight:500;cursor:pointer;transition:all .12s}.osl-rg-config-action svg{display:block}.osl-rg-config-action:hover{background:#f3f4f6;border-color:#9ca3af}.osl-rg-config-list{flex:1;overflow-y:auto;padding:6px 0}.osl-rg-config-list::-webkit-scrollbar{width:5px}.osl-rg-config-list::-webkit-scrollbar-thumb{background:#e5e7eb;border-radius:4px}.osl-rg-config-list::-webkit-scrollbar-thumb:hover{background:#d1d5db}.osl-rg-config-item{display:flex;align-items:center;gap:9px;padding:7px 14px;transition:background .1s;cursor:grab}.osl-rg-config-item:hover{background:#f9fafb}.osl-rg-config-item:active{cursor:grabbing}.osl-rg-config-item--hidden{opacity:.5}.osl-rg-config-item__label{flex:1;font-size:13px;color:#374151;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}.osl-rg-config-item__label--hidden{color:#9ca3af;font-style:italic}.osl-rg-config-item__pin-btn{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border:1px solid #e5e7eb;background:#fff;color:#d1d5db;cursor:pointer;padding:0;border-radius:5px;transition:all .12s;flex-shrink:0}.osl-rg-config-item__pin-btn svg{display:block}.osl-rg-config-item__pin-btn:hover,.osl-rg-config-item__pin-btn--active{background:#eff6ff;border-color:#93c5fd;color:var(--osl-primary, #2563eb)}.osl-rg-copy-toast{position:fixed;bottom:24px;left:50%;transform:translate(-50%);z-index:99999;display:flex;align-items:center;gap:7px;background:#1f2937;color:#fff;font-size:12.5px;font-weight:500;padding:8px 18px;border-radius:22px;animation:rg-fade-up .2s ease;box-shadow:0 6px 16px #0000004d;pointer-events:none;white-space:nowrap;font-family:inherit}.osl-rg-copy-toast svg{flex-shrink:0;color:#4ade80}\n"], dependencies: [{ kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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: i3$1.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i3$1.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i3$1.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: i3$1.CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "pipe", type: i1$2.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$2.UpperCasePipe, name: "uppercase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5347
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslReportGrid, isStandalone: false, selector: "osl-report-grid", inputs: { columns: "columns", datasource: "datasource", loading: "loading", totalRecords: "totalRecords", autoMode: "autoMode", isPaginated: "isPaginated", pageSize: "pageSize", tableHeight: "tableHeight", striped: "striped", exportable: "exportable", rowHeight: "rowHeight", rowSelection: "rowSelection", showAggregates: "showAggregates", title: "title", pdfExportFromGrid: "pdfExportFromGrid", pdfConfig: "pdfConfig" }, outputs: { pageChange: "pageChange", pageSizeChange: "pageSizeChange", sortChange: "sortChange", rowClick: "rowClick", selectionChange: "selectionChange" }, host: { listeners: { "document:mousemove": "onMouseMove($event)", "document:mouseup": "onMouseUp()", "document:click": "onDocumentClick()" } }, providers: [DatePipe, DecimalPipe], viewQueries: [{ propertyName: "headerScrollRef", first: true, predicate: ["headerScrollRef"], descendants: true }, { propertyName: "bodyScrollRef", first: true, predicate: ["bodyScrollRef"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"osl-rg\" (click)=\"closeColumnMenu(); openFilterKey = null; $event.stopPropagation()\">\n\n <!-- \u2550\u2550 TOOLBAR \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"osl-rg-toolbar\">\n <div class=\"osl-rg-toolbar__left\">\n @if (title) { <span class=\"osl-rg-title\">{{ title }}</span> }\n @if (!loading) {\n @if (_filteredTotal !== datasource.length) {\n <span class=\"osl-rg-filter-badge\">\n <span class=\"osl-rg-filter-badge__dot\"></span>\n {{ _filteredTotal | number }} of {{ datasource.length | number }} rows\n </span>\n } @else {\n <span class=\"osl-rg-record-count\">{{ datasource.length | number }} rows</span>\n }\n }\n </div>\n <div class=\"osl-rg-toolbar__right\">\n\n <!-- Global search -->\n <div class=\"osl-rg-global-search\">\n <svg class=\"osl-rg-global-search__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"m21 21-4.35-4.35\"/>\n </svg>\n <input class=\"osl-rg-global-search__input\" placeholder=\"Search all columns\u2026\"\n [(ngModel)]=\"globalSearch\" (ngModelChange)=\"currentPage=1; processData()\" />\n @if (globalSearch) {\n <button class=\"osl-rg-global-search__clear\" (click)=\"globalSearch=''; processData()\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </button>\n }\n </div>\n\n <!-- Sort active badge -->\n @if (sortStates.size > 0) {\n <button class=\"osl-rg-toolbar-btn osl-rg-toolbar-btn--active\" (click)=\"clearSort()\" title=\"Clear sort\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M11 5H4M11 9H4M11 13H4M18 4v16M15 7l3-3 3 3M15 17l3 3 3-3\"/>\n </svg>\n <span>{{ sortStates.size }} sort{{ sortStates.size > 1 ? 's' : '' }}</span>\n <svg class=\"rg-icon rg-icon--xs\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </button>\n }\n\n <!-- Filter active badge -->\n @if (hasAnyFilter) {\n <button class=\"osl-rg-toolbar-btn osl-rg-toolbar-btn--danger\" (click)=\"clearAllFilters()\" title=\"Clear all filters\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M22 3H2l8 9.46V19l4 2V12.46z\"/></svg>\n <span>Filtered</span>\n <svg class=\"rg-icon rg-icon--xs\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </button>\n }\n\n <!-- Group toggle -->\n <button class=\"osl-rg-toolbar-btn\" [class.osl-rg-toolbar-btn--on]=\"showGroupPanel\"\n (click)=\"showGroupPanel=!showGroupPanel\" title=\"Group panel\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"2\" y=\"7\" width=\"20\" height=\"14\" rx=\"2\"/><path d=\"M16 3H8a2 2 0 0 0-2 2v2h12V5a2 2 0 0 0-2-2z\"/>\n </svg>\n <span>Group</span>\n </button>\n\n <!-- Expand / collapse when grouped -->\n @if (activeGroups.length > 0) {\n <button class=\"osl-rg-toolbar-btn\" (click)=\"expandAll()\" title=\"Expand all\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m7 20 5-5 5 5M7 4l5 5 5-5\"/></svg>\n </button>\n <button class=\"osl-rg-toolbar-btn\" (click)=\"collapseAll()\" title=\"Collapse all\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m7 15 5-5 5 5M7 9l5 5 5 5\"/></svg>\n </button>\n }\n\n <!-- Auto-size all -->\n <button class=\"osl-rg-toolbar-btn\" (click)=\"autoSizeAll()\" title=\"Auto-size all columns\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M21 6H3M21 18H3\"/><path d=\"M8 3 3 6l5 3M16 3l5 3-5 3M8 15l-5 3 5 3M16 15l5 3-5 3\"/>\n </svg>\n <span>Auto-size</span>\n </button>\n\n <!-- Column config -->\n <button class=\"osl-rg-toolbar-btn\" [class.osl-rg-toolbar-btn--on]=\"showColumnConfig\"\n (click)=\"showColumnConfig=!showColumnConfig; $event.stopPropagation()\" title=\"Column settings\">\n <svg class=\"rg-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M4 6h16M4 12h16M4 18h16\"/>\n <circle cx=\"8\" cy=\"6\" r=\"2\" fill=\"white\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <circle cx=\"16\" cy=\"12\" r=\"2\" fill=\"white\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <circle cx=\"8\" cy=\"18\" r=\"2\" fill=\"white\" stroke=\"currentColor\" stroke-width=\"2\"/>\n </svg>\n <span>Columns</span>\n </button>\n\n <!-- Export Excel -->\n @if (exportable) {\n <button class=\"osl-rg-toolbar-btn osl-rg-toolbar-btn--excel\" (click)=\"exportExcel()\" title=\"Export to Excel\">\n <svg class=\"rg-icon rg-icon--excel\" viewBox=\"0 0 24 24\">\n <rect width=\"24\" height=\"24\" rx=\"3\" fill=\"#1D6F42\"/>\n <path d=\"M13.5 3H7a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7.5L13.5 3z\" fill=\"#fff\" opacity=\".15\"/>\n <path d=\"M13.5 3v4.5H18L13.5 3z\" fill=\"#fff\" opacity=\".3\"/>\n <path d=\"M8 9.5l2.3 3.5L8 16.5h1.6l1.4-2.3 1.4 2.3H14l-2.3-3.5 2.3-3.5h-1.6l-1.4 2.3-1.4-2.3H8z\" fill=\"#fff\"/>\n </svg>\n <span>Export Excel</span>\n </button>\n }\n\n <!-- Export PDF -->\n @if (exportable && pdfExportFromGrid) {\n <button class=\"osl-rg-toolbar-btn osl-rg-toolbar-btn--pdf\" (click)=\"exportPdf()\" title=\"Export to PDF\">\n <svg class=\"rg-icon rg-icon--pdf\" viewBox=\"0 0 24 24\">\n <rect width=\"24\" height=\"24\" rx=\"3\" fill=\"#D32F2F\"/>\n <path d=\"M13.5 3H7a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V7.5L13.5 3z\" fill=\"#fff\" opacity=\".15\"/>\n <path d=\"M13.5 3v4.5H18L13.5 3z\" fill=\"#fff\" opacity=\".3\"/>\n <text x=\"4.5\" y=\"17.5\" fill=\"#fff\" font-size=\"6.5\" font-family=\"sans-serif\" font-weight=\"700\">PDF</text>\n </svg>\n <span>Export PDF</span>\n </button>\n }\n </div>\n </div>\n\n <!-- \u2550\u2550 GROUP PANEL \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (showGroupPanel) {\n <div class=\"osl-rg-group-panel\">\n <span class=\"osl-rg-group-panel__label\">Group by:</span>\n @if (activeGroups.length === 0) {\n <span class=\"osl-rg-group-panel__hint\">Use the \u22EE column menu \u2192 \"Group by this\"</span>\n }\n <div class=\"osl-rg-group-chips\" cdkDropList cdkDropListOrientation=\"horizontal\"\n [cdkDropListData]=\"activeGroups\" (cdkDropListDropped)=\"onGroupPanelDrop($event)\">\n @for (key of activeGroups; track key) {\n <div class=\"osl-rg-group-chip\" cdkDrag>\n <svg cdkDragHandle class=\"rg-drag-handle\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"9\" cy=\"5\" r=\"1.5\"/><circle cx=\"15\" cy=\"5\" r=\"1.5\"/>\n <circle cx=\"9\" cy=\"12\" r=\"1.5\"/><circle cx=\"15\" cy=\"12\" r=\"1.5\"/>\n <circle cx=\"9\" cy=\"19\" r=\"1.5\"/><circle cx=\"15\" cy=\"19\" r=\"1.5\"/>\n </svg>\n <span>{{ getGroupColLabel(key) }}</span>\n <button class=\"osl-rg-group-chip__remove\" (click)=\"removeGroup(key)\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </button>\n </div>\n }\n </div>\n @if (activeGroups.length > 0) {\n <button class=\"osl-rg-group-panel__clear\" (click)=\"clearGroups()\">Clear groups</button>\n }\n </div>\n }\n\n <!-- \u2550\u2550 STICKY HEADER \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"osl-rg-header-wrap\" #headerScrollRef>\n\n <!-- Double header \u2013 group row (only rendered when headerGroup is used on any column) -->\n @if (hasHeaderGroups) {\n <div class=\"osl-rg-header-row osl-rg-header-row--groups\" [style.minWidth.px]=\"totalWidth\">\n @if (rowSelection === 'multiple') {\n <div class=\"osl-rg-th osl-rg-th--checkbox osl-rg-th--group-span\"></div>\n }\n <div style=\"display:flex;flex:1;min-width:0;\">\n @for (group of computedHeaderGroups; track $index) {\n <div class=\"osl-rg-th osl-rg-th--group-span\"\n [class.osl-rg-th--group-span--labeled]=\"!!group.label\"\n [class.osl-rg-th--pinned]=\"group.isPinned\"\n [style.width.px]=\"group.width\"\n [style.minWidth.px]=\"group.width\"\n [style.left.px]=\"group.isPinned ? group.stickyLeft : null\"\n [style.position]=\"group.isPinned ? 'sticky' : 'relative'\"\n [style.zIndex]=\"group.isPinned ? 3 : 1\">\n @if (group.label) {\n <span class=\"osl-rg-th-group-label\">{{ group.label }}</span>\n }\n </div>\n }\n </div>\n </div>\n }\n\n <div class=\"osl-rg-header-row\" [style.minWidth.px]=\"totalWidth\">\n\n <!-- Select-all checkbox -->\n @if (rowSelection === 'multiple') {\n <div class=\"osl-rg-th osl-rg-th--checkbox\">\n <input type=\"checkbox\" class=\"osl-rg-checkbox\"\n [checked]=\"allSelected\" [indeterminate]=\"someSelected\"\n (change)=\"toggleSelectAll($any($event.target).checked)\" />\n </div>\n }\n\n <!-- Column headers (drag-to-reorder) -->\n <div class=\"osl-rg-header-cols\" cdkDropList cdkDropListOrientation=\"horizontal\"\n [cdkDropListData]=\"_cols\" (cdkDropListDropped)=\"onColumnReorder($event)\"\n style=\"display:flex;flex:1;min-width:0;\">\n\n @for (col of visibleCols; track col.key) {\n <div class=\"osl-rg-th\"\n [class.osl-rg-th--pinned]=\"col._pinned\"\n [class.osl-rg-th--sorted]=\"getSortState(col.key)\"\n [class.osl-rg-th--filtered]=\"hasActiveFilter(col.key)\"\n [style.width.px]=\"col._width\"\n [style.minWidth.px]=\"col._width\"\n [style.left.px]=\"col._pinned ? col._stickyLeft : null\"\n [style.position]=\"col._pinned ? 'sticky' : 'relative'\"\n [style.zIndex]=\"col._pinned ? 3 : 1\"\n cdkDrag [cdkDragDisabled]=\"col._pinned\"\n (cdkDragStarted)=\"onColumnDragStarted()\"\n (cdkDragEnded)=\"onColumnDragEnded()\"\n (click)=\"onHeaderClick(col, $event)\">\n\n <div *cdkDragPlaceholder class=\"osl-rg-th-drag-placeholder\"></div>\n\n <!-- Pin badge -->\n @if (col._pinned) {\n <span class=\"osl-rg-th-pin-badge\" title=\"Pinned\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"9\" height=\"9\"><path d=\"M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z\"/></svg>\n </span>\n }\n\n <!-- Label row -->\n <div class=\"osl-rg-th-content\">\n <span class=\"osl-rg-th-label\" [title]=\"col.label\">{{ col.label }}</span>\n <div class=\"osl-rg-th-actions\">\n\n <!-- Sort indicator -->\n @if (col.sortable) {\n <span class=\"osl-rg-sort-icon\"\n [class.osl-rg-sort-icon--asc]=\"getSortState(col.key)?.asc === true\"\n [class.osl-rg-sort-icon--desc]=\"getSortState(col.key)?.asc === false\">\n @if (getSortState(col.key); as s) {\n @if (s.asc) {\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 19V5m-7 7 7-7 7 7\"/></svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 5v14m7-7-7 7-7-7\"/></svg>\n }\n @if (sortStates.size > 1) { <sup class=\"osl-rg-sort-badge\">{{ s.index + 1 }}</sup> }\n } @else {\n <svg class=\"osl-rg-sort-idle\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M8 9l4-4 4 4M16 15l-4 4-4-4\"/></svg>\n }\n </span>\n }\n\n <!-- Excel filter button -->\n @if (col.filterable) {\n <button class=\"osl-rg-filter-btn\" [class.osl-rg-filter-btn--active]=\"hasActiveFilter(col.key)\"\n (click)=\"openExcelFilter(col.key, $event)\" title=\"Filter\">\n @if (hasActiveFilter(col.key)) {\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M22 3H2l8 9.46V19l4 2V12.46z\"/></svg>\n } @else {\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3\"/></svg>\n }\n </button>\n }\n\n <!-- Column menu -->\n <button class=\"osl-rg-col-menu-btn\" (click)=\"openColumnMenu(col.key, $event)\" title=\"Column options\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><circle cx=\"12\" cy=\"5\" r=\"2\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/><circle cx=\"12\" cy=\"19\" r=\"2\"/></svg>\n </button>\n\n </div>\n </div>\n\n <!-- Per-column search -->\n @if (col.searchable) {\n <div class=\"osl-rg-col-search\" (click)=\"$event.stopPropagation()\">\n <svg class=\"osl-rg-col-search__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"7\"/><path d=\"m18 18-3.5-3.5\"/>\n </svg>\n <input class=\"osl-rg-col-search__input\" [placeholder]=\"col.label + '\u2026'\"\n [ngModel]=\"columnSearch[col.key]\" (ngModelChange)=\"onColumnSearch(col.key, $event)\" />\n @if (columnSearch[col.key]) {\n <button class=\"osl-rg-col-search__clear\" (click)=\"columnSearch[col.key]=''; processData()\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </button>\n }\n </div>\n }\n\n <!-- Resize handle -->\n @if (col.resizable) {\n <div class=\"osl-rg-resize-handle\" (mousedown)=\"startResize(col, $event)\" (dblclick)=\"autoSizeColumn(col, $event)\" (click)=\"$event.stopPropagation()\"></div>\n }\n\n\n </div><!-- /osl-rg-th -->\n }\n\n </div><!-- /header-cols -->\n </div><!-- /header-row -->\n </div><!-- /header-wrap -->\n\n <!-- \u2550\u2550 SCROLLABLE BODY \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"osl-rg-body\" [style.height]=\"tableHeight\" #bodyScrollRef (scroll)=\"onBodyScroll($event)\">\n <div class=\"osl-rg-inner\" [style.minWidth.px]=\"totalWidth\">\n\n <!-- Skeleton loading -->\n @if (loading) {\n @for (sk of skeletonRows; track $index) {\n <div class=\"osl-rg-row osl-rg-row--skeleton\" [style.height.px]=\"rowHeight\">\n @if (rowSelection === 'multiple') {\n <div class=\"osl-rg-td osl-rg-td--checkbox\"><div class=\"osl-rg-skel\" style=\"width:14px;height:14px;border-radius:3px;\"></div></div>\n }\n @for (col of visibleCols; track col.key) {\n <div class=\"osl-rg-td\" [style.width.px]=\"col._width\" [style.minWidth.px]=\"col._width\">\n <div class=\"osl-rg-skel\" [style.width]=\"$index % 3 === 0 ? '55%' : $index % 3 === 1 ? '80%' : '65%'\"></div>\n </div>\n }\n </div>\n }\n\n } @else if (isEmpty) {\n <!-- Empty state -->\n <div class=\"osl-rg-empty\">\n <div class=\"osl-rg-empty__icon\">\n <svg viewBox=\"0 0 48 48\" fill=\"none\" stroke=\"#d1d5db\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <rect x=\"6\" y=\"6\" width=\"36\" height=\"36\" rx=\"4\"/>\n <path d=\"M6 18h36M6 30h36M18 6v36\"/>\n </svg>\n </div>\n <p class=\"osl-rg-empty__title\">No records found</p>\n <p class=\"osl-rg-empty__sub\">\n @if (hasAnyFilter) { Try adjusting your filters or search terms }\n @else { No data to display }\n </p>\n @if (hasAnyFilter) {\n <button class=\"osl-rg-empty__action\" (click)=\"clearAllFilters()\">Clear all filters</button>\n }\n </div>\n\n } @else {\n <!-- Data rows -->\n @for (row of flatRows; track trackByRow($index, row)) {\n\n @if (row._type === 'group') {\n <div class=\"osl-rg-group-row\"\n [class.osl-rg-group-row--l0]=\"asGroupRow(row)._level === 0\"\n [class.osl-rg-group-row--l1]=\"asGroupRow(row)._level === 1\"\n [class.osl-rg-group-row--l2]=\"asGroupRow(row)._level >= 2\"\n [style.height.px]=\"rowHeight\"\n [style.paddingLeft.px]=\"asGroupRow(row)._level * 20 + 12\"\n (click)=\"toggleGroup(asGroupRow(row)._path)\">\n @if (asGroupRow(row)._expanded) {\n <svg class=\"osl-rg-group-chevron\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m6 9 6 6 6-6\"/></svg>\n } @else {\n <svg class=\"osl-rg-group-chevron\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m9 18 6-6-6-6\"/></svg>\n }\n <span class=\"osl-rg-group-row__key\">{{ asGroupRow(row)._colLabel }}:</span>\n <span class=\"osl-rg-group-row__value\">{{ asGroupRow(row)._label }}</span>\n <span class=\"osl-rg-group-row__count\">{{ asGroupRow(row)._count | number }} rows</span>\n </div>\n\n } @else {\n <div class=\"osl-rg-row\"\n [class.osl-rg-row--striped]=\"striped && asDataRow(row)._rowIndex % 2 === 1\"\n [class.osl-rg-row--selected]=\"selectedRows.has(asDataRow(row)._data)\"\n [style.height.px]=\"rowHeight\"\n (click)=\"onRowClick(asDataRow(row)._data, $event)\">\n\n @if (rowSelection === 'multiple') {\n <div class=\"osl-rg-td osl-rg-td--checkbox\">\n <input type=\"checkbox\" class=\"osl-rg-checkbox\"\n [checked]=\"selectedRows.has(asDataRow(row)._data)\"\n (change)=\"toggleRowSelect(asDataRow(row)._data, $event)\" />\n </div>\n }\n\n @for (col of visibleCols; track col.key) {\n <div class=\"osl-rg-td\"\n [class.osl-rg-td--pinned]=\"col._pinned\"\n [ngClass]=\"getCellClass(asDataRow(row)._data, col)\"\n [style.width.px]=\"col._width\"\n [style.minWidth.px]=\"col._width\"\n [style.left.px]=\"col._pinned ? col._stickyLeft : null\"\n [style.position]=\"col._pinned ? 'sticky' : 'relative'\"\n [style.zIndex]=\"col._pinned ? 2 : 0\"\n [style.textAlign]=\"col.align ?? 'left'\"\n (dblclick)=\"copyCell(getCellDisplay(asDataRow(row)._data, col), $event)\"\n [title]=\"getCellDisplay(asDataRow(row)._data, col)\">\n <span class=\"osl-rg-cell-text\">{{ getCellDisplay(asDataRow(row)._data, col) }}</span>\n </div>\n }\n\n </div>\n }\n\n }\n }\n\n </div><!-- /osl-rg-inner -->\n </div><!-- /osl-rg-body -->\n\n <!-- \u2550\u2550 AGGREGATE FOOTER \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (showAggregates && hasAnyAggregate() && !loading) {\n <div class=\"osl-rg-aggregate-row\" [style.minWidth.px]=\"totalWidth\">\n @if (rowSelection === 'multiple') { <div class=\"osl-rg-agg-cell osl-rg-agg-cell--checkbox\"></div> }\n @for (col of visibleCols; track col.key) {\n <div class=\"osl-rg-agg-cell\" [style.width.px]=\"col._width\" [style.minWidth.px]=\"col._width\" [style.textAlign]=\"col.align ?? 'left'\">\n @if (col.aggregate) {\n <span class=\"osl-rg-agg-label\">{{ col.aggregate | uppercase }}</span>\n <span class=\"osl-rg-agg-value\">{{ getAggregate(col) }}</span>\n }\n </div>\n }\n </div>\n }\n\n <!-- \u2550\u2550 PAGINATION \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (isPaginated) {\n <div class=\"osl-rg-pagination\">\n <span class=\"osl-rg-pagination__info\">\n @if (loading) { Loading\u2026 }\n @else if (_total > 0) { Showing <strong>{{ startRecord | number }}\u2013{{ endRecord | number }}</strong> of <strong>{{ _total | number }}</strong> records }\n @else { No records }\n </span>\n <div class=\"osl-rg-pagination__controls\">\n <button class=\"osl-rg-page-btn osl-rg-page-btn--nav\" (click)=\"goToPage(1)\" [disabled]=\"currentPage===1||loading\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><path d=\"m11 17-5-5 5-5M18 17l-5-5 5-5\"/></svg>\n </button>\n <button class=\"osl-rg-page-btn osl-rg-page-btn--nav\" (click)=\"goToPage(currentPage-1)\" [disabled]=\"currentPage===1||loading\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><path d=\"m15 18-6-6 6-6\"/></svg>\n Prev\n </button>\n @for (page of pageNumbers; track $index) {\n @if (page === -1) { <span class=\"osl-rg-page-ellipsis\">\u2026</span> }\n @else {\n <button class=\"osl-rg-page-btn osl-rg-page-btn--num\" [class.osl-rg-page-btn--active]=\"page===currentPage\"\n [disabled]=\"loading\" (click)=\"goToPage(page)\">{{ page }}</button>\n }\n }\n <button class=\"osl-rg-page-btn osl-rg-page-btn--nav\" (click)=\"goToPage(currentPage+1)\" [disabled]=\"currentPage===totalPages||loading\">\n Next\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><path d=\"m9 18 6-6-6-6\"/></svg>\n </button>\n <button class=\"osl-rg-page-btn osl-rg-page-btn--nav\" (click)=\"goToPage(totalPages)\" [disabled]=\"currentPage===totalPages||loading\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><path d=\"m13 17 5-5-5-5M6 17l5-5-5-5\"/></svg>\n </button>\n </div>\n <div class=\"osl-rg-pagination__size\">\n <span class=\"osl-rg-pagination__size-label\">Rows per page</span>\n <select class=\"osl-rg-page-size\" [ngModel]=\"pageSize\" (ngModelChange)=\"onPageSizeChange($event)\" [disabled]=\"loading\">\n @for (opt of pageSizeOptions; track opt) { <option [value]=\"opt\">{{ opt }}</option> }\n </select>\n </div>\n </div>\n }\n\n</div><!-- /osl-rg -->\n\n\n<!-- \u2550\u2550 EXCEL FILTER DROPDOWN (fixed, outside overflow containers) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (openFilterKey && excelFilterState[openFilterKey]) {\n <div class=\"osl-rg-excel-filter\"\n [style.top.px]=\"filterDropdownPos.top\"\n [style.left.px]=\"filterDropdownPos.left\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"osl-rg-excel-filter__header\">\n <div class=\"osl-rg-excel-filter__title\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"12\" height=\"12\" style=\"flex-shrink:0;\"><path d=\"M22 3H2l8 9.46V19l4 2V12.46z\"/></svg>\n Filter: {{ getColumnByKey(openFilterKey)?.label }}\n </div>\n @if (hasActiveFilter(openFilterKey)) {\n <button class=\"osl-rg-excel-filter__clear-btn\" (click)=\"clearExcelFilter(openFilterKey!)\">Clear</button>\n }\n </div>\n <div class=\"osl-rg-excel-filter__search\">\n <input class=\"osl-rg-excel-filter__search-input\" placeholder=\"Search values\u2026\"\n [(ngModel)]=\"excelFilterState[openFilterKey].search\" />\n </div>\n <div class=\"osl-rg-excel-filter__select-all\">\n <label class=\"osl-rg-excel-filter__item osl-rg-excel-filter__item--all\">\n <input type=\"checkbox\" class=\"osl-rg-checkbox\" [checked]=\"isAllExcelChecked(openFilterKey)\"\n (change)=\"toggleAllExcelFilter(openFilterKey!, $any($event.target).checked)\" />\n <span class=\"osl-rg-excel-filter__item-label\">(Select All)</span>\n </label>\n </div>\n <div class=\"osl-rg-excel-filter__list\">\n @for (item of getFilteredExcelItems(openFilterKey); track item.value) {\n <label class=\"osl-rg-excel-filter__item\">\n <input type=\"checkbox\" class=\"osl-rg-checkbox\" [(ngModel)]=\"item.checked\" />\n <span class=\"osl-rg-excel-filter__item-label\">{{ item.label }}</span>\n </label>\n }\n </div>\n <div class=\"osl-rg-excel-filter__footer\">\n <button class=\"osl-rg-excel-filter__apply\" (click)=\"applyExcelFilter(openFilterKey!)\">Apply Filter</button>\n <button class=\"osl-rg-excel-filter__cancel\" (click)=\"openFilterKey=null\">Cancel</button>\n </div>\n </div>\n}\n\n\n<!-- \u2550\u2550 COLUMN CONTEXT MENU (fixed) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (columnMenuKey && getColumnByKey(columnMenuKey); as menuCol) {\n <div class=\"osl-rg-col-menu\"\n [style.top.px]=\"columnMenuPos.top\" [style.left.px]=\"columnMenuPos.left\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"osl-rg-col-menu__header\">{{ menuCol.label }}</div>\n @if (menuCol.sortable) {\n <button class=\"osl-rg-col-menu__item\"\n (click)=\"sortStates.clear(); sortStates.set(menuCol.key,{key:menuCol.key,asc:true,index:0}); sortCounter=1; processData(); closeColumnMenu()\">\n <svg class=\"rg-icon rg-icon--sm\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 19V5m-7 7 7-7 7 7\"/></svg>\n Sort A \u2192 Z\n </button>\n <button class=\"osl-rg-col-menu__item\"\n (click)=\"sortStates.clear(); sortStates.set(menuCol.key,{key:menuCol.key,asc:false,index:0}); sortCounter=1; processData(); closeColumnMenu()\">\n <svg class=\"rg-icon rg-icon--sm\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M12 5v14m7-7-7 7-7-7\"/></svg>\n Sort Z \u2192 A\n </button>\n }\n @if (menuCol.groupable) {\n <div class=\"osl-rg-col-menu__divider\"></div>\n <button class=\"osl-rg-col-menu__item\" (click)=\"addGroup(menuCol.key); closeColumnMenu()\">\n <svg class=\"rg-icon rg-icon--sm\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"2\" y=\"7\" width=\"20\" height=\"14\" rx=\"2\"/><path d=\"M16 3H8a2 2 0 0 0-2 2v2h12V5a2 2 0 0 0-2-2z\"/></svg>\n Group by this\n </button>\n }\n <div class=\"osl-rg-col-menu__divider\"></div>\n <button class=\"osl-rg-col-menu__item\" (click)=\"toggleColumnPin(menuCol)\">\n <svg class=\"rg-icon rg-icon--sm\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z\"/></svg>\n {{ menuCol._pinned ? 'Unpin column' : 'Pin to left' }}\n </button>\n <button class=\"osl-rg-col-menu__item\" (click)=\"autoSizeColumn(menuCol); closeColumnMenu()\">\n <svg class=\"rg-icon rg-icon--sm\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M21 6H3M21 18H3\"/><path d=\"M8 3 3 6l5 3M16 3l5 3-5 3M8 15l-5 3 5 3M16 15l5 3-5 3\"/></svg>\n Auto-size column\n </button>\n <div class=\"osl-rg-col-menu__divider\"></div>\n <button class=\"osl-rg-col-menu__item osl-rg-col-menu__item--danger\" (click)=\"toggleColumnVisibility(menuCol); closeColumnMenu()\">\n <svg class=\"rg-icon rg-icon--sm\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24M1 1l22 22\"/>\n </svg>\n Hide column\n </button>\n </div>\n}\n\n\n<!-- \u2550\u2550 COLUMN CONFIG SIDE DRAWER \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (showColumnConfig) {\n <div class=\"osl-rg-config-backdrop\" (click)=\"showColumnConfig=false\"></div>\n <div class=\"osl-rg-config-panel\" (click)=\"$event.stopPropagation()\">\n <div class=\"osl-rg-config-panel__header\">\n <div class=\"osl-rg-config-panel__title\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"16\" height=\"16\">\n <path d=\"M4 6h16M4 12h16M4 18h16\"/>\n <circle cx=\"8\" cy=\"6\" r=\"2\" fill=\"white\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <circle cx=\"16\" cy=\"12\" r=\"2\" fill=\"white\" stroke=\"currentColor\" stroke-width=\"2\"/>\n <circle cx=\"8\" cy=\"18\" r=\"2\" fill=\"white\" stroke=\"currentColor\" stroke-width=\"2\"/>\n </svg>\n Column Settings\n </div>\n <button class=\"osl-rg-config-panel__close\" (click)=\"showColumnConfig=false\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\"><path d=\"M18 6 6 18M6 6l12 12\"/></svg>\n </button>\n </div>\n <div class=\"osl-rg-config-panel__actions\">\n <button class=\"osl-rg-config-action\" (click)=\"autoSizeAll()\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><path d=\"M21 6H3M21 18H3\"/><path d=\"M8 3 3 6l5 3M16 3l5 3-5 3\"/></svg>\n Auto-size all\n </button>\n <button class=\"osl-rg-config-action\" (click)=\"showAllColumns()\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/></svg>\n Show all\n </button>\n </div>\n <div class=\"osl-rg-config-hint\">Drag to reorder \u00B7 Toggle to show/hide \u00B7 Pin to freeze</div>\n <div class=\"osl-rg-config-list\" cdkDropList (cdkDropListDropped)=\"onConfigColumnReorder($event)\">\n @for (col of _cols; track col.key) {\n <div class=\"osl-rg-config-item\" cdkDrag [class.osl-rg-config-item--hidden]=\"col._hidden\">\n <svg cdkDragHandle class=\"rg-drag-handle\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"9\" cy=\"5\" r=\"1.5\"/><circle cx=\"15\" cy=\"5\" r=\"1.5\"/>\n <circle cx=\"9\" cy=\"12\" r=\"1.5\"/><circle cx=\"15\" cy=\"12\" r=\"1.5\"/>\n <circle cx=\"9\" cy=\"19\" r=\"1.5\"/><circle cx=\"15\" cy=\"19\" r=\"1.5\"/>\n </svg>\n <input type=\"checkbox\" class=\"osl-rg-checkbox\" [checked]=\"!col._hidden\" (change)=\"toggleColumnVisibility(col)\" />\n <span class=\"osl-rg-config-item__label\" [class.osl-rg-config-item__label--hidden]=\"col._hidden\">{{ col.label }}</span>\n <button class=\"osl-rg-config-item__pin-btn\" (click)=\"toggleColumnPin(col)\"\n [class.osl-rg-config-item__pin-btn--active]=\"col._pinned\"\n [title]=\"col._pinned ? 'Unpin' : 'Pin to left'\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"12\" height=\"12\"><path d=\"M16 12V4h1V2H7v2h1v8l-2 2v2h5.2v6h1.6v-6H18v-2l-2-2z\"/></svg>\n </button>\n </div>\n }\n </div>\n </div>\n}\n\n\n<!-- \u2550\u2550 COPY TOAST \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (copiedCell !== null) {\n <div class=\"osl-rg-copy-toast\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\"><polyline points=\"20 6 9 17 4 12\"/></svg>\n Copied to clipboard\n </div>\n}\n", styles: ["@keyframes rg-pulse{0%,to{opacity:1}50%{opacity:.4}}@keyframes rg-fade-up{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes rg-slide-in{0%{opacity:0;transform:translate(20px)}to{opacity:1;transform:translate(0)}}.rg-icon{width:15px;height:15px;flex-shrink:0;display:block}.rg-icon--sm{width:14px;height:14px}.rg-icon--xs{width:12px;height:12px}.rg-icon--excel{width:18px;height:18px}.rg-drag-handle{width:14px;height:14px;flex-shrink:0;color:#d1d5db;cursor:grab;display:block}.rg-drag-handle:active{cursor:grabbing}.osl-rg{position:relative;display:flex;flex-direction:column;width:100%;font-family:inherit;font-size:13px;color:#111827;background:#fff;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:10px;overflow:hidden;box-shadow:0 2px 8px #00000012,0 1px 2px #0000000a;-webkit-user-select:none;user-select:none}.osl-rg-toolbar{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 14px;background:linear-gradient(135deg,#f8faff,#f3f4f6);border-bottom:1px solid var(--osl-border-color, #e5e7eb);flex-shrink:0;flex-wrap:wrap}.osl-rg-toolbar__left{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.osl-rg-toolbar__right{display:flex;align-items:center;gap:6px;flex-wrap:wrap}.osl-rg-title{font-size:14px;font-weight:700;color:#111827;letter-spacing:-.01em}.osl-rg-record-count{display:inline-flex;align-items:center;gap:5px;font-size:12px;color:#9ca3af;font-weight:500}.osl-rg-record-count svg{color:#d1d5db}.osl-rg-filter-badge{display:inline-flex;align-items:center;gap:5px;padding:2px 9px 2px 6px;background:#eff6ff;border:1px solid #bfdbfe;border-radius:12px;font-size:11px;color:#1d4ed8;font-weight:600}.osl-rg-filter-badge__dot{width:6px;height:6px;border-radius:50%;background:#2563eb;animation:rg-pulse 1.5s ease-in-out infinite;flex-shrink:0}.osl-rg-toolbar-btn{display:inline-flex;align-items:center;gap:5px;height:32px;padding:0 11px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:7px;background:#fff;color:#5a6478;font-size:12px;font-family:inherit;font-weight:500;cursor:pointer;white-space:nowrap;transition:all .15s ease}.osl-rg-toolbar-btn svg{flex-shrink:0}.osl-rg-toolbar-btn:hover{background:#f3f4f6;color:#1f2937;border-color:#c4c9d4;box-shadow:0 1px 3px #0000000f}.osl-rg-toolbar-btn--on{background:#eff6ff;border-color:#93c5fd;color:#1d4ed8}.osl-rg-toolbar-btn--active{background:#eff6ff;border-color:#93c5fd;color:#1d4ed8;font-weight:600}.osl-rg-toolbar-btn--danger{background:#fff5f5;border-color:#fca5a5;color:#dc2626}.osl-rg-toolbar-btn--danger:hover{background:#fee2e2;border-color:#f87171}.osl-rg-toolbar-btn--excel{background:linear-gradient(135deg,#1a7a3e,#1d6f42);border-color:#166534;color:#fff;font-weight:600;box-shadow:0 1px 3px #1665344d}.osl-rg-toolbar-btn--excel:hover{background:linear-gradient(135deg,#166534,#145a2c);border-color:#14532d;color:#fff;box-shadow:0 2px 6px #16653466}.osl-rg-toolbar-btn--pdf{background:linear-gradient(135deg,#e53935,#c62828);border-color:#b71c1c;color:#fff;font-weight:600;box-shadow:0 1px 3px #b71c1c4d}.osl-rg-toolbar-btn--pdf:hover{background:linear-gradient(135deg,#c62828,#ad1515);border-color:#ad1515;color:#fff;box-shadow:0 2px 6px #b71c1c66}.rg-icon--pdf{width:18px;height:18px}.osl-rg-global-search{position:relative;display:inline-flex;align-items:center}.osl-rg-global-search__icon{position:absolute;left:9px;width:14px;height:14px;color:#9ca3af;pointer-events:none;display:block}.osl-rg-global-search__input{height:32px;padding:0 30px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:7px;background:#fff;font-size:12.5px;font-family:inherit;color:#111827;outline:none;width:210px;transition:border-color .15s,width .2s,box-shadow .15s}.osl-rg-global-search__input::placeholder{color:#b0b7c3}.osl-rg-global-search__input:focus{border-color:var(--osl-primary, #2563eb);width:260px;box-shadow:0 0 0 3px #2563eb1a}.osl-rg-global-search__clear{position:absolute;right:6px;display:flex;align-items:center;justify-content:center;width:18px;height:18px;border:none;background:transparent;color:#9ca3af;cursor:pointer;padding:0;border-radius:4px;transition:all .12s}.osl-rg-global-search__clear svg{width:12px;height:12px}.osl-rg-global-search__clear:hover{color:#374151;background:#0000000d}.osl-rg-group-panel{display:flex;align-items:center;gap:8px;padding:6px 14px;background:linear-gradient(135deg,#fafbff,#f5f7ff);border-bottom:1px dashed #dde5ff;min-height:40px;flex-shrink:0;flex-wrap:wrap}.osl-rg-group-panel__label{display:flex;align-items:center;gap:5px;font-size:11px;font-weight:700;color:#6b7280;text-transform:uppercase;letter-spacing:.05em;white-space:nowrap;flex-shrink:0}.osl-rg-group-panel__label svg{color:#9ca3af}.osl-rg-group-panel__hint{font-size:11px;color:#b0b7c3;font-style:italic}.osl-rg-group-panel__clear{margin-left:auto;font-size:11px;color:#6b7280;background:none;border:none;cursor:pointer;text-decoration:underline;flex-shrink:0;padding:0;font-family:inherit}.osl-rg-group-panel__clear:hover{color:#dc2626}.osl-rg-group-chips{display:flex;gap:6px;flex-wrap:wrap;align-items:center;flex:1;min-height:26px}.osl-rg-group-chip{display:inline-flex;align-items:center;gap:5px;padding:3px 6px 3px 5px;background:linear-gradient(135deg,#dbeafe,#eff6ff);border:1px solid #bfdbfe;border-radius:20px;font-size:12px;font-weight:600;color:#1e40af;cursor:grab;-webkit-user-select:none;user-select:none;box-shadow:0 1px 2px #2563eb1a;transition:box-shadow .1s}.osl-rg-group-chip:active{cursor:grabbing;box-shadow:0 2px 6px #2563eb33}.osl-rg-group-chip .rg-drag-handle{color:#93c5fd}.osl-rg-group-chip__remove{display:flex;align-items:center;justify-content:center;width:16px;height:16px;border:none;background:transparent;cursor:pointer;padding:0;border-radius:50%;transition:all .1s}.osl-rg-group-chip__remove svg{width:10px;height:10px;color:#93c5fd}.osl-rg-group-chip__remove:hover{background:#dbeafe}.osl-rg-group-chip__remove:hover svg{color:#1e40af}.osl-rg-table-area{display:flex;flex-direction:column;flex:1 1 0;overflow:hidden;position:relative;min-height:0}.osl-rg-header-wrap{overflow:hidden;flex-shrink:0;border-bottom:2px solid #dde5ff;background:linear-gradient(180deg,#f0f4ff,#e8efff);z-index:10;position:relative}.osl-rg-header-row{display:flex;align-items:stretch}.osl-rg-header-row--groups{border-bottom:1px solid #c7d7f0}.osl-rg-th--group-span{display:flex;align-items:center;justify-content:center;height:28px;min-height:28px;cursor:default;padding:0;border-right:1px solid #dde5ff}.osl-rg-th--group-span:hover{background:transparent!important}.osl-rg-th--group-span:last-child{border-right:none}.osl-rg-th--group-span--labeled{background:linear-gradient(135deg,#dbeafe,#eff6ff);border-bottom:2px solid #93c5fd}.osl-rg-th-group-label{font-size:11px;font-weight:700;color:#1e40af;text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding:0 10px;text-align:center;width:100%}.osl-rg-header-cols{display:flex}.osl-rg-th{position:relative;display:flex;flex-direction:column;background:transparent;border-right:1px solid #dde5ff;cursor:pointer;flex-shrink:0;transition:background .12s;overflow:visible}.osl-rg-th:last-child{border-right:none}.osl-rg-th:hover{background:#2563eb0f}.osl-rg-th--pinned{background:linear-gradient(180deg,#e8efff,#dde8ff);border-right:1px solid #bfdbfe;z-index:3}.osl-rg-th--pinned:after{content:\"\";position:absolute;right:-5px;top:0;bottom:0;width:5px;background:linear-gradient(to right,rgba(37,99,235,.14),transparent);pointer-events:none;z-index:4}.osl-rg-th--sorted{background:#6366f114}.osl-rg-th--filtered{background:#f59e0b12}.osl-rg-th-drag-placeholder{background:#e0e7ff;border:2px dashed #6366f1;border-radius:4px;opacity:.6}.osl-rg-th-pin-badge{position:absolute;top:3px;right:24px;color:var(--osl-primary, #2563eb);opacity:.6;display:flex;align-items:center}.osl-rg-th-content{display:flex;align-items:center;justify-content:space-between;gap:4px;padding:0 8px 0 10px;height:36px;min-height:36px}.osl-rg-th-label{font-size:11.5px;font-weight:700;color:#1e3a6e;text-transform:uppercase;letter-spacing:.05em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.osl-rg-th-actions{display:flex;align-items:center;gap:2px;flex-shrink:0}.osl-rg-sort-icon{display:inline-flex;align-items:center;position:relative;color:#9ca3af;transition:color .12s;width:14px;height:14px}.osl-rg-sort-icon svg{width:13px;height:13px;display:block}.osl-rg-sort-icon--asc,.osl-rg-sort-icon--desc{color:var(--osl-primary, #2563eb)}.osl-rg-sort-idle{opacity:.3}.osl-rg-sort-badge{position:absolute;top:-4px;right:-6px;font-size:8px;font-weight:700;background:var(--osl-primary, #2563eb);color:#fff;border-radius:50%;width:11px;height:11px;display:flex;align-items:center;justify-content:center;line-height:1}.osl-rg-filter-btn,.osl-rg-col-menu-btn{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border:none;background:transparent;color:#b0b7c3;cursor:pointer;padding:0;border-radius:5px;transition:all .12s}.osl-rg-filter-btn svg,.osl-rg-col-menu-btn svg{width:13px;height:13px;display:block}.osl-rg-filter-btn:hover,.osl-rg-col-menu-btn:hover{background:#00000012;color:#374151}.osl-rg-filter-btn--active{color:var(--osl-primary, #2563eb)!important;background:#2563eb1f!important}.osl-rg-col-search{position:relative;display:flex;align-items:center;padding:3px 6px;border-top:1px solid #e0e7ff;background:#ffffff80}.osl-rg-col-search__icon{position:absolute;left:10px;width:12px;height:12px;display:block;pointer-events:none;color:#c4cad6}.osl-rg-col-search__input{width:100%;height:23px;padding:0 18px 0 22px;border:1px solid #dde5ff;border-radius:5px;font-size:11px;font-family:inherit;background:#fff;color:#111827;outline:none;transition:border-color .12s,box-shadow .12s}.osl-rg-col-search__input::placeholder{color:#c4cad6}.osl-rg-col-search__input:focus{border-color:var(--osl-primary, #2563eb);box-shadow:0 0 0 2px #2563eb1a}.osl-rg-col-search__clear{position:absolute;right:8px;display:flex;align-items:center;width:14px;height:14px;border:none;background:transparent;color:#9ca3af;cursor:pointer;padding:0;border-radius:3px;transition:all .1s}.osl-rg-col-search__clear svg{width:10px;height:10px;display:block}.osl-rg-col-search__clear:hover{color:#374151;background:#0000000f}.osl-rg-resize-handle{position:absolute;right:0;top:0;bottom:0;width:6px;cursor:col-resize;z-index:5}.osl-rg-resize-handle:after{content:\"\";position:absolute;right:2px;top:15%;bottom:15%;width:2px;border-radius:2px;background:#a5b4fc;opacity:0;transition:opacity .15s}.osl-rg-resize-handle:hover:after{opacity:1}.osl-rg-th:hover .osl-rg-resize-handle:after{opacity:.4}.osl-rg-excel-filter{position:fixed;z-index:9999;width:250px;background:#fff;border:1px solid #e5e7eb;border-radius:10px;box-shadow:0 12px 32px #00000026,0 2px 8px #00000012;animation:rg-fade-up .14s ease;overflow:hidden}.osl-rg-excel-filter__header{display:flex;align-items:center;justify-content:space-between;padding:9px 12px;background:linear-gradient(135deg,#f0f4ff,#e8efff);border-bottom:1px solid #dde5ff}.osl-rg-excel-filter__title{display:flex;align-items:center;gap:6px;font-size:11px;font-weight:700;color:#1e3a6e;text-transform:uppercase;letter-spacing:.06em}.osl-rg-excel-filter__clear-btn{font-size:11px;color:#dc2626;background:none;border:none;cursor:pointer;font-weight:600;padding:2px 6px;border-radius:4px;font-family:inherit;transition:background .1s}.osl-rg-excel-filter__clear-btn:hover{background:#fee2e2}.osl-rg-excel-filter__search{padding:7px 8px;border-bottom:1px solid #f3f4f6;position:relative}.osl-rg-excel-filter__search-input{width:100%;height:28px;padding:0 8px 0 28px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit;outline:none;box-sizing:border-box;transition:border-color .12s;color:#111827}.osl-rg-excel-filter__search-input::placeholder{color:#b0b7c3}.osl-rg-excel-filter__search-input:focus{border-color:var(--osl-primary, #2563eb)}.osl-rg-excel-filter__select-all{padding:0 8px;border-bottom:2px solid #f3f4f6;background:#fafbff}.osl-rg-excel-filter__list{max-height:210px;overflow-y:auto;padding:3px 8px}.osl-rg-excel-filter__list::-webkit-scrollbar{width:5px}.osl-rg-excel-filter__list::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:4px}.osl-rg-excel-filter__list::-webkit-scrollbar-thumb:hover{background:#9ca3af}.osl-rg-excel-filter__item{display:flex;align-items:center;gap:8px;padding:5px 4px;font-size:12.5px;color:#374151;cursor:pointer;border-radius:5px;transition:background .1s}.osl-rg-excel-filter__item:hover{background:#f3f4f6}.osl-rg-excel-filter__item--all{font-weight:600;color:#1f2937;padding:6px 4px}.osl-rg-excel-filter__item-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}.osl-rg-excel-filter__footer{display:flex;gap:6px;padding:8px 10px;border-top:1px solid #f3f4f6;background:#f9fafb}.osl-rg-excel-filter__apply{flex:1;height:30px;border:none;border-radius:6px;font-size:12px;font-family:inherit;font-weight:600;cursor:pointer;background:var(--osl-primary, #2563eb);color:#fff;transition:background .12s}.osl-rg-excel-filter__apply:hover{background:#1d4ed8}.osl-rg-excel-filter__cancel{height:30px;padding:0 14px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;font-family:inherit;font-weight:500;cursor:pointer;background:#fff;color:#6b7280;transition:all .12s}.osl-rg-excel-filter__cancel:hover{background:#f3f4f6;border-color:#9ca3af}.osl-rg-th--checkbox,.osl-rg-td--checkbox{display:flex;align-items:center;justify-content:center;width:36px;min-width:36px;flex-shrink:0;border-right:1px solid #dde5ff}.osl-rg-checkbox{width:14px;height:14px;cursor:pointer;accent-color:var(--osl-primary, #2563eb);flex-shrink:0}.osl-rg-body{overflow-x:auto;overflow-y:auto;flex-shrink:0}.osl-rg-body::-webkit-scrollbar{width:8px;height:8px}.osl-rg-body::-webkit-scrollbar-track{background:#f9fafb}.osl-rg-body::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:4px}.osl-rg-body::-webkit-scrollbar-thumb:hover{background:#9ca3af}.osl-rg-body::-webkit-scrollbar-corner{background:#f9fafb}.osl-rg-inner{display:block}.osl-rg-row{display:flex;align-items:stretch;border-bottom:1px solid #f3f4f6;transition:background .08s;cursor:default}.osl-rg-row:hover{background:#f0f5ff!important}.osl-rg-row--striped{background:#fafbff}.osl-rg-row--selected{background:#eff6ff!important;border-bottom-color:#dbeafe}.osl-rg-row--skeleton{pointer-events:none}.osl-rg-td{display:flex;align-items:center;padding:0 12px;border-right:1px solid #f0f2f7;overflow:hidden;background:inherit;flex-shrink:0}.osl-rg-td:last-child{border-right:none}.osl-rg-td--pinned{background:#fff;border-right:1px solid #e0e7ff;z-index:2}.osl-rg-td--pinned:after{content:\"\";position:absolute;right:-5px;top:0;bottom:0;width:5px;background:linear-gradient(to right,rgba(37,99,235,.07),transparent);pointer-events:none}.osl-rg-cell-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;width:100%;font-size:13px;color:#1f2937;line-height:1.4}.rg-profit-pos .osl-rg-cell-text{color:#15803d;font-weight:600}.rg-profit-neg .osl-rg-cell-text{color:#dc2626;font-weight:600}.rg-status-delivered .osl-rg-cell-text{color:#15803d;font-weight:600}.rg-status-shipped .osl-rg-cell-text{color:#2563eb;font-weight:600}.rg-status-processing .osl-rg-cell-text{color:#d97706;font-weight:600}.rg-status-pending .osl-rg-cell-text{color:#6b7280;font-weight:500}.rg-status-cancelled .osl-rg-cell-text{color:#dc2626;font-weight:500;text-decoration:line-through;opacity:.75}.osl-rg-group-row{display:flex;align-items:center;gap:7px;border-bottom:1px solid #e5e7eb;font-weight:600;cursor:pointer;flex-shrink:0;transition:filter .1s}.osl-rg-group-row--l0{background:linear-gradient(135deg,#eff6ff,#dbeafe);color:#1e40af;border-left:4px solid #3b82f6}.osl-rg-group-row--l1{background:linear-gradient(135deg,#f5f3ff,#ede9fe);color:#5b21b6;border-left:4px solid #8b5cf6}.osl-rg-group-row--l2{background:linear-gradient(135deg,#fff7ed,#fef3c7);color:#92400e;border-left:4px solid #f59e0b}.osl-rg-group-row:hover{filter:brightness(.97)}.osl-rg-group-row__key{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;opacity:.65;flex-shrink:0}.osl-rg-group-row__value{font-size:13px;font-weight:700;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.osl-rg-group-row__count{font-size:11px;font-weight:600;padding:2px 9px;border-radius:12px;background:#00000017;margin-right:12px;flex-shrink:0}.osl-rg-group-chevron{width:16px;height:16px;flex-shrink:0;opacity:.75;display:block}.osl-rg-aggregate-row{display:flex;align-items:center;border-top:2px solid #e5e7eb;background:linear-gradient(135deg,#f0fdf4,#f9fafb);flex-shrink:0;min-height:38px}.osl-rg-agg-cell{display:flex;align-items:center;gap:5px;padding:0 12px;border-right:1px solid #e5e7eb;height:38px;flex-shrink:0;overflow:hidden}.osl-rg-agg-cell--checkbox{width:36px;min-width:36px}.osl-rg-agg-label{font-size:9.5px;font-weight:800;color:#9ca3af;text-transform:uppercase;letter-spacing:.06em;flex-shrink:0;background:#e5e7eb;padding:1px 4px;border-radius:3px}.osl-rg-agg-value{font-size:12px;font-weight:700;color:#15803d;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.osl-rg-skel{height:12px;border-radius:4px;background:linear-gradient(90deg,#e5e7eb 25%,#f3f4f6,#e5e7eb 75%);background-size:200% 100%;animation:rg-pulse 1.5s ease-in-out infinite}.osl-rg-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:64px 20px;color:#9ca3af;text-align:center}.osl-rg-empty__icon{margin-bottom:18px;opacity:.7}.osl-rg-empty__title{font-size:15px;font-weight:600;color:#374151;margin:0 0 6px}.osl-rg-empty__sub{font-size:13px;color:#9ca3af;margin:0 0 18px;max-width:290px;line-height:1.5}.osl-rg-empty__action{height:34px;padding:0 18px;background:var(--osl-primary, #2563eb);color:#fff;border:none;border-radius:7px;font-size:13px;font-family:inherit;font-weight:600;cursor:pointer;transition:background .15s;box-shadow:0 2px 6px #2563eb4d}.osl-rg-empty__action:hover{background:#1d4ed8}.osl-rg-pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-top:1px solid var(--osl-border-color, #e5e7eb);background:linear-gradient(135deg,#f8faff,#f3f4f6);flex-shrink:0;flex-wrap:wrap}.osl-rg-pagination__info{font-size:12px;color:#6b7280;min-width:160px;white-space:nowrap}.osl-rg-pagination__info strong{color:#1f2937;font-weight:600}.osl-rg-pagination__controls{display:flex;align-items:center;gap:3px}.osl-rg-pagination__size{display:flex;align-items:center;gap:7px;min-width:120px;justify-content:flex-end}.osl-rg-pagination__size-label{font-size:11.5px;color:#9ca3af;white-space:nowrap}.osl-rg-page-btn{display:inline-flex;align-items:center;justify-content:center;gap:3px;height:30px;padding:0 8px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:6px;background:#fff;color:#374151;font-size:12px;font-family:inherit;font-weight:500;cursor:pointer;transition:all .12s;white-space:nowrap}.osl-rg-page-btn svg{display:block}.osl-rg-page-btn:hover:not(:disabled){background:#f3f4f6;border-color:#9ca3af;box-shadow:0 1px 2px #0000000f}.osl-rg-page-btn:disabled{opacity:.35;cursor:not-allowed}.osl-rg-page-btn--num{min-width:30px;padding:0;font-size:12.5px}.osl-rg-page-btn--active{background:var(--osl-primary, #2563eb);border-color:var(--osl-primary, #2563eb);color:#fff;font-weight:700;box-shadow:0 2px 5px #2563eb59}.osl-rg-page-btn--active:hover:not(:disabled){background:#1d4ed8;border-color:#1d4ed8}.osl-rg-page-ellipsis{display:inline-flex;align-items:center;justify-content:center;width:26px;color:#9ca3af;font-size:13px;pointer-events:none}.osl-rg-page-size{height:30px;padding:0 8px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:6px;background:#fff;font-size:12px;font-family:inherit;color:#374151;cursor:pointer;outline:none;transition:border-color .15s}.osl-rg-page-size:focus{border-color:var(--osl-primary, #2563eb)}.osl-rg-col-menu{position:fixed;z-index:9999;min-width:210px;background:#fff;border:1px solid #e5e7eb;border-radius:11px;box-shadow:0 12px 36px #00000029,0 3px 10px #00000014;overflow:hidden;animation:rg-fade-up .13s cubic-bezier(.16,1,.3,1)}.osl-rg-col-menu__header{padding:9px 14px 7px;font-size:10.5px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:#9ca3af;border-bottom:1px solid #f3f4f6;background:#fafbff}.osl-rg-col-menu__divider{height:1px;background:#f3f4f6;margin:2px 0}.osl-rg-col-menu__item{display:flex;align-items:center;gap:9px;width:100%;padding:9px 14px;background:transparent;border:none;border-left:3px solid transparent;border-bottom:none;text-align:left;font-size:13px;font-weight:500;font-family:inherit;color:#374151;cursor:pointer;transition:all .1s}.osl-rg-col-menu__item svg{color:#9ca3af;display:block}.osl-rg-col-menu__item:hover{background:#f0f4ff;color:var(--osl-primary, #2563eb);border-left-color:var(--osl-primary, #2563eb)}.osl-rg-col-menu__item:hover svg{color:var(--osl-primary, #2563eb)}.osl-rg-col-menu__item--danger:hover{background:#fff5f5;color:#dc2626;border-left-color:#dc2626}.osl-rg-col-menu__item--danger:hover svg{color:#dc2626}.osl-rg-config-backdrop{position:fixed;inset:0;z-index:9000;background:#0003;-webkit-backdrop-filter:blur(1px);backdrop-filter:blur(1px)}.osl-rg-config-panel{position:fixed;top:0;right:0;bottom:0;z-index:9001;width:300px;background:#fff;border-left:1px solid #e5e7eb;box-shadow:-6px 0 24px #00000021;display:flex;flex-direction:column;animation:rg-slide-in .2s cubic-bezier(.16,1,.3,1)}.osl-rg-config-panel__header{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid #e5e7eb;background:linear-gradient(135deg,#f0f4ff,#e8efff);flex-shrink:0}.osl-rg-config-panel__title{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:700;color:#1e3a6e}.osl-rg-config-panel__title svg{color:#3b82f6}.osl-rg-config-panel__close{display:flex;align-items:center;justify-content:center;width:30px;height:30px;border:none;background:transparent;color:#9ca3af;cursor:pointer;border-radius:7px;padding:0;transition:all .12s}.osl-rg-config-panel__close svg{width:16px;height:16px;display:block}.osl-rg-config-panel__close:hover{background:#00000012;color:#374151}.osl-rg-config-panel__actions{display:flex;gap:8px;padding:10px 14px;border-bottom:1px solid #f3f4f6;flex-shrink:0;background:#fafbff}.osl-rg-config-hint{padding:6px 16px;font-size:10.5px;color:#b0b7c3;font-style:italic;border-bottom:1px solid #f3f4f6;flex-shrink:0}.osl-rg-config-action{display:inline-flex;align-items:center;gap:5px;height:30px;padding:0 10px;border:1px solid #e5e7eb;border-radius:6px;background:#fff;color:#374151;font-size:12px;font-family:inherit;font-weight:500;cursor:pointer;transition:all .12s}.osl-rg-config-action svg{display:block}.osl-rg-config-action:hover{background:#f3f4f6;border-color:#9ca3af}.osl-rg-config-list{flex:1;overflow-y:auto;padding:6px 0}.osl-rg-config-list::-webkit-scrollbar{width:5px}.osl-rg-config-list::-webkit-scrollbar-thumb{background:#e5e7eb;border-radius:4px}.osl-rg-config-list::-webkit-scrollbar-thumb:hover{background:#d1d5db}.osl-rg-config-item{display:flex;align-items:center;gap:9px;padding:7px 14px;transition:background .1s;cursor:grab}.osl-rg-config-item:hover{background:#f9fafb}.osl-rg-config-item:active{cursor:grabbing}.osl-rg-config-item--hidden{opacity:.5}.osl-rg-config-item__label{flex:1;font-size:13px;color:#374151;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}.osl-rg-config-item__label--hidden{color:#9ca3af;font-style:italic}.osl-rg-config-item__pin-btn{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border:1px solid #e5e7eb;background:#fff;color:#d1d5db;cursor:pointer;padding:0;border-radius:5px;transition:all .12s;flex-shrink:0}.osl-rg-config-item__pin-btn svg{display:block}.osl-rg-config-item__pin-btn:hover,.osl-rg-config-item__pin-btn--active{background:#eff6ff;border-color:#93c5fd;color:var(--osl-primary, #2563eb)}.osl-rg-copy-toast{position:fixed;bottom:24px;left:50%;transform:translate(-50%);z-index:99999;display:flex;align-items:center;gap:7px;background:#1f2937;color:#fff;font-size:12.5px;font-weight:500;padding:8px 18px;border-radius:22px;animation:rg-fade-up .2s ease;box-shadow:0 6px 16px #0000004d;pointer-events:none;white-space:nowrap;font-family:inherit}.osl-rg-copy-toast svg{flex-shrink:0;color:#4ade80}\n"], dependencies: [{ kind: "directive", type: i1$2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { 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.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { 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: i3.CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type: i3.CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: i3.CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: i3.CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "pipe", type: i1$2.DecimalPipe, name: "number" }, { kind: "pipe", type: i1$2.UpperCasePipe, name: "uppercase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5305
5348
|
}
|
|
5306
5349
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslReportGrid, decorators: [{
|
|
5307
5350
|
type: Component,
|
|
@@ -7252,6 +7295,50 @@ var validation_util = /*#__PURE__*/Object.freeze({
|
|
|
7252
7295
|
passwordStrength: passwordStrength
|
|
7253
7296
|
});
|
|
7254
7297
|
|
|
7298
|
+
class UnsavedChangesDialog {
|
|
7299
|
+
dialogRef;
|
|
7300
|
+
data;
|
|
7301
|
+
constructor(dialogRef, data) {
|
|
7302
|
+
this.dialogRef = dialogRef;
|
|
7303
|
+
this.data = data;
|
|
7304
|
+
}
|
|
7305
|
+
save() { this.dialogRef.close('save'); }
|
|
7306
|
+
leave() { this.dialogRef.close('leave'); }
|
|
7307
|
+
cancel() { this.dialogRef.close(null); }
|
|
7308
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: UnsavedChangesDialog, deps: [{ token: i1.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component });
|
|
7309
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: UnsavedChangesDialog, isStandalone: true, selector: "unsaved-changes-dialog", ngImport: i0, template: "<div class=\"unsaved-dialog-container\">\n\n <!-- Header: centered icon + top-right close -->\n <div class=\"unsaved-dialog-header\">\n <div class=\"unsaved-icon-wrapper\">\n <div class=\"unsaved-icon-ring\">\n <svg class=\"unsaved-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/>\n </svg>\n </div>\n </div>\n <button mat-icon-button class=\"close-btn\" (click)=\"cancel()\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n <path d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\n </svg>\n </button>\n </div>\n\n <!-- Body -->\n <mat-dialog-content class=\"unsaved-dialog-content\">\n <h2 class=\"unsaved-title\">Unsaved Changes</h2>\n <p class=\"unsaved-message\">\n You have unsaved changes that will be lost if you leave this page.\n What would you like to do?\n </p>\n </mat-dialog-content>\n\n <!-- Actions -->\n <mat-dialog-actions class=\"unsaved-dialog-actions\">\n <button mat-stroked-button class=\"stay-btn\" (click)=\"cancel()\">\n <svg class=\"btn-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z\"/>\n </svg>\n Stay on Page\n </button>\n <button mat-stroked-button class=\"leave-btn\" (click)=\"leave()\">\n <svg class=\"btn-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z\"/>\n </svg>\n Leave\n </button>\n @if (data.hasSaveCallback) {\n <button mat-flat-button class=\"save-btn\" (click)=\"save()\">\n <svg class=\"btn-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z\"/>\n </svg>\n Save & Leave\n </button>\n }\n </mat-dialog-actions>\n\n</div>\n", styles: [".unsaved-dialog-container{padding:8px 4px}.unsaved-dialog-header{display:flex;justify-content:space-between;align-items:flex-start;padding:8px 8px 0}.unsaved-icon-wrapper{display:flex;align-items:center;justify-content:center;flex:1;padding-left:40px}.unsaved-icon-ring{width:72px;height:72px;border-radius:50%;background:#f59e0b14;border:2px solid rgba(245,158,11,.25);display:flex;align-items:center;justify-content:center;animation:pulse-amber 2s ease-in-out infinite}.unsaved-icon{width:36px;height:36px;color:var(--osl-warning, #d97706)}.close-btn{color:var(--osl-secondary, #6b7280);flex-shrink:0}.close-btn:hover{color:var(--osl-black, #000)}.unsaved-dialog-content{text-align:center;padding:16px 24px 8px!important;max-height:unset!important;overflow:visible!important}.unsaved-title{font-size:20px;font-weight:600;color:var(--osl-black, #111827);margin:0 0 10px;line-height:1.3}.unsaved-message{font-size:14px;color:var(--osl-secondary, #6b7280);margin:0;line-height:1.6}.unsaved-dialog-actions{display:flex!important;justify-content:center;gap:10px;padding:16px 24px 20px!important;flex-wrap:wrap}.btn-icon{width:17px;height:17px;flex-shrink:0;margin-right:2px}.stay-btn{border-color:var(--osl-border-color, #d1d5db);color:var(--osl-secondary, #6b7280);border-radius:var(--osl-border-radius, 4px);height:38px;font-size:14px;font-weight:500;display:flex;align-items:center;gap:4px;padding:0 16px;transition:all .2s ease}.stay-btn:hover{border-color:var(--osl-secondary, #6b7280);background:#f9fafb}.leave-btn{border-color:#dc262659;color:var(--osl-danger, #dc2626);border-radius:var(--osl-border-radius, 4px);height:38px;font-size:14px;font-weight:500;display:flex;align-items:center;gap:4px;padding:0 16px;transition:all .2s ease}.leave-btn:hover{border-color:var(--osl-danger, #dc2626);background:#dc26260a}.save-btn{background:var(--osl-primary, #2563eb);color:#fff;border-radius:var(--osl-border-radius, 4px);height:38px;font-size:14px;font-weight:500;display:flex;align-items:center;gap:4px;padding:0 18px;transition:all .2s ease}.save-btn .btn-icon{color:#fff}.save-btn:hover{background:var(--osl-primary-hover, #1d4ed8);box-shadow:0 4px 12px #2563eb59}@keyframes pulse-amber{0%,to{box-shadow:0 0 #f59e0b26}50%{box-shadow:0 0 0 8px #f59e0b00}}\n"], dependencies: [{ kind: "ngmodule", type: MatDialogModule }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i2$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }] });
|
|
7310
|
+
}
|
|
7311
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: UnsavedChangesDialog, decorators: [{
|
|
7312
|
+
type: Component,
|
|
7313
|
+
args: [{ selector: 'unsaved-changes-dialog', standalone: true, imports: [MatDialogModule, MatButtonModule], template: "<div class=\"unsaved-dialog-container\">\n\n <!-- Header: centered icon + top-right close -->\n <div class=\"unsaved-dialog-header\">\n <div class=\"unsaved-icon-wrapper\">\n <div class=\"unsaved-icon-ring\">\n <svg class=\"unsaved-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/>\n </svg>\n </div>\n </div>\n <button mat-icon-button class=\"close-btn\" (click)=\"cancel()\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"20\" height=\"20\">\n <path d=\"M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/>\n </svg>\n </button>\n </div>\n\n <!-- Body -->\n <mat-dialog-content class=\"unsaved-dialog-content\">\n <h2 class=\"unsaved-title\">Unsaved Changes</h2>\n <p class=\"unsaved-message\">\n You have unsaved changes that will be lost if you leave this page.\n What would you like to do?\n </p>\n </mat-dialog-content>\n\n <!-- Actions -->\n <mat-dialog-actions class=\"unsaved-dialog-actions\">\n <button mat-stroked-button class=\"stay-btn\" (click)=\"cancel()\">\n <svg class=\"btn-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z\"/>\n </svg>\n Stay on Page\n </button>\n <button mat-stroked-button class=\"leave-btn\" (click)=\"leave()\">\n <svg class=\"btn-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z\"/>\n </svg>\n Leave\n </button>\n @if (data.hasSaveCallback) {\n <button mat-flat-button class=\"save-btn\" (click)=\"save()\">\n <svg class=\"btn-icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z\"/>\n </svg>\n Save & Leave\n </button>\n }\n </mat-dialog-actions>\n\n</div>\n", styles: [".unsaved-dialog-container{padding:8px 4px}.unsaved-dialog-header{display:flex;justify-content:space-between;align-items:flex-start;padding:8px 8px 0}.unsaved-icon-wrapper{display:flex;align-items:center;justify-content:center;flex:1;padding-left:40px}.unsaved-icon-ring{width:72px;height:72px;border-radius:50%;background:#f59e0b14;border:2px solid rgba(245,158,11,.25);display:flex;align-items:center;justify-content:center;animation:pulse-amber 2s ease-in-out infinite}.unsaved-icon{width:36px;height:36px;color:var(--osl-warning, #d97706)}.close-btn{color:var(--osl-secondary, #6b7280);flex-shrink:0}.close-btn:hover{color:var(--osl-black, #000)}.unsaved-dialog-content{text-align:center;padding:16px 24px 8px!important;max-height:unset!important;overflow:visible!important}.unsaved-title{font-size:20px;font-weight:600;color:var(--osl-black, #111827);margin:0 0 10px;line-height:1.3}.unsaved-message{font-size:14px;color:var(--osl-secondary, #6b7280);margin:0;line-height:1.6}.unsaved-dialog-actions{display:flex!important;justify-content:center;gap:10px;padding:16px 24px 20px!important;flex-wrap:wrap}.btn-icon{width:17px;height:17px;flex-shrink:0;margin-right:2px}.stay-btn{border-color:var(--osl-border-color, #d1d5db);color:var(--osl-secondary, #6b7280);border-radius:var(--osl-border-radius, 4px);height:38px;font-size:14px;font-weight:500;display:flex;align-items:center;gap:4px;padding:0 16px;transition:all .2s ease}.stay-btn:hover{border-color:var(--osl-secondary, #6b7280);background:#f9fafb}.leave-btn{border-color:#dc262659;color:var(--osl-danger, #dc2626);border-radius:var(--osl-border-radius, 4px);height:38px;font-size:14px;font-weight:500;display:flex;align-items:center;gap:4px;padding:0 16px;transition:all .2s ease}.leave-btn:hover{border-color:var(--osl-danger, #dc2626);background:#dc26260a}.save-btn{background:var(--osl-primary, #2563eb);color:#fff;border-radius:var(--osl-border-radius, 4px);height:38px;font-size:14px;font-weight:500;display:flex;align-items:center;gap:4px;padding:0 18px;transition:all .2s ease}.save-btn .btn-icon{color:#fff}.save-btn:hover{background:var(--osl-primary-hover, #1d4ed8);box-shadow:0 4px 12px #2563eb59}@keyframes pulse-amber{0%,to{box-shadow:0 0 #f59e0b26}50%{box-shadow:0 0 0 8px #f59e0b00}}\n"] }]
|
|
7314
|
+
}], ctorParameters: () => [{ type: i1.MatDialogRef }, { type: undefined, decorators: [{
|
|
7315
|
+
type: Inject,
|
|
7316
|
+
args: [MAT_DIALOG_DATA]
|
|
7317
|
+
}] }] });
|
|
7318
|
+
|
|
7319
|
+
const unsavedChangesGuard = () => {
|
|
7320
|
+
const dirty = inject(DirtyStateService);
|
|
7321
|
+
const dialog = inject(MatDialog);
|
|
7322
|
+
if (!dirty.isDirty)
|
|
7323
|
+
return true;
|
|
7324
|
+
return dialog.open(UnsavedChangesDialog, {
|
|
7325
|
+
width: '400px',
|
|
7326
|
+
maxWidth: '90vw',
|
|
7327
|
+
disableClose: true,
|
|
7328
|
+
data: { hasSaveCallback: dirty.hasSaveCallback },
|
|
7329
|
+
}).afterClosed().pipe(switchMap(result => {
|
|
7330
|
+
if (result === 'save') {
|
|
7331
|
+
return from(dirty.executeSave()).pipe(tap(saved => { if (saved)
|
|
7332
|
+
dirty.markClean(); }));
|
|
7333
|
+
}
|
|
7334
|
+
if (result === 'leave') {
|
|
7335
|
+
dirty.markClean();
|
|
7336
|
+
return of(true);
|
|
7337
|
+
}
|
|
7338
|
+
return of(false);
|
|
7339
|
+
}));
|
|
7340
|
+
};
|
|
7341
|
+
|
|
7255
7342
|
/*
|
|
7256
7343
|
* Public API Surface of osl-base-extended
|
|
7257
7344
|
*/
|
|
@@ -7260,5 +7347,5 @@ var validation_util = /*#__PURE__*/Object.freeze({
|
|
|
7260
7347
|
* Generated bundle index. Do not edit.
|
|
7261
7348
|
*/
|
|
7262
7349
|
|
|
7263
|
-
export { array_util as ArrayUtil, date_util as DateUtil, DeleteConfirmation, DeleteConfirmationData, Dialog, DialogWrapper, DynamicForm, FormStructureModule, Httpbase, number_util as NumberUtil, object_util as ObjectUtil, OslAutocomplete, OslAutocompleteLister, OslBaseExtended, OslButton, OslCheckbox, OslDatepicker, OslDatetimepicker, OslDocumentUploader, OslFileUpload, OslFormGrid, OslGrid, OslMenu, OslMenuTriggerFor, OslRadio, OslReportForm, OslReportGrid, OslSearchbar, OslSelect, OslSetup, OslSkeletonDirective, OslSkeletonModule, OslSkeletonThemeService, OslSlideToggle, OslTooltipDirective, OslUserLog, Oslinput, Osltextarea, storage_util as StorageUtil, string_util as StringUtil, validation_util as ValidationUtil, baseComponent };
|
|
7350
|
+
export { array_util as ArrayUtil, date_util as DateUtil, DeleteConfirmation, DeleteConfirmationData, Dialog, DialogWrapper, DirtyStateService, DynamicForm, FormStructureModule, Httpbase, number_util as NumberUtil, object_util as ObjectUtil, OslAutocomplete, OslAutocompleteLister, OslBaseExtended, OslButton, OslCheckbox, OslDatepicker, OslDatetimepicker, OslDocumentUploader, OslFileUpload, OslFormGrid, OslGrid, OslMenu, OslMenuTriggerFor, OslRadio, OslReportForm, OslReportGrid, OslSearchbar, OslSelect, OslSetup, OslSkeletonDirective, OslSkeletonModule, OslSkeletonThemeService, OslSlideToggle, OslTooltipDirective, OslUserLog, Oslinput, Osltextarea, storage_util as StorageUtil, string_util as StringUtil, UnsavedChangesDialog, validation_util as ValidationUtil, baseComponent, unsavedChangesGuard };
|
|
7264
7351
|
//# sourceMappingURL=osl-base-extended.mjs.map
|