osl-base-extended 9.8.0 → 10.0.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.
|
@@ -4218,8 +4218,10 @@ class OslFormGrid {
|
|
|
4218
4218
|
leftPanelWidth = 320;
|
|
4219
4219
|
dragTitle = 'Available Controls';
|
|
4220
4220
|
showSearch = true;
|
|
4221
|
-
allowReorder =
|
|
4221
|
+
allowReorder = false;
|
|
4222
4222
|
allowRemove = true;
|
|
4223
|
+
/** Per-row override to disable reordering for specific rows, e.g. (row) => row.locked. */
|
|
4224
|
+
reorderDisabledIf;
|
|
4223
4225
|
/** 'Local' filters draggingList in the component. 'Api' emits (panelSearch) for the parent to handle. */
|
|
4224
4226
|
panelSearchType = 'Local';
|
|
4225
4227
|
/** Show a loading skeleton in the panel (useful in Api mode while the parent fetches data). */
|
|
@@ -4426,6 +4428,8 @@ class OslFormGrid {
|
|
|
4426
4428
|
const baseIndex = this.isPaginated ? (this.currentPage - 1) * this.pageSize : 0;
|
|
4427
4429
|
const prevActual = baseIndex + event.previousIndex;
|
|
4428
4430
|
const currActual = baseIndex + event.currentIndex;
|
|
4431
|
+
if (this.isReorderDisabled(this.datasource[prevActual]))
|
|
4432
|
+
return;
|
|
4429
4433
|
moveItemInArray(this.datasource, prevActual, currActual);
|
|
4430
4434
|
this.datasource = [...this.datasource];
|
|
4431
4435
|
this.datasourceChange.emit(this.datasource);
|
|
@@ -4439,6 +4443,9 @@ class OslFormGrid {
|
|
|
4439
4443
|
});
|
|
4440
4444
|
this.gridChanged.emit(this.datasource);
|
|
4441
4445
|
}
|
|
4446
|
+
isReorderDisabled(row) {
|
|
4447
|
+
return this.reorderDisabledIf ? this.reorderDisabledIf(row) : false;
|
|
4448
|
+
}
|
|
4442
4449
|
removeGridRow(pagedIndex) {
|
|
4443
4450
|
if (!this.allowRemove)
|
|
4444
4451
|
return;
|
|
@@ -4517,11 +4524,11 @@ class OslFormGrid {
|
|
|
4517
4524
|
});
|
|
4518
4525
|
}
|
|
4519
4526
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslFormGrid, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4520
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslFormGrid, isStandalone: false, selector: "osl-form-grid", inputs: { columns: "columns", datasource: "datasource", isPaginated: "isPaginated", pageSize: "pageSize", canAdd: "canAdd", canDelete: "canDelete", loading: "loading", tableHeight: "tableHeight", footerColumns: "footerColumns", enableDragDrop: "enableDragDrop", draggingList: "draggingList", draggingColumns: "draggingColumns", leftPanelWidth: "leftPanelWidth", dragTitle: "dragTitle", showSearch: "showSearch", allowReorder: "allowReorder", allowRemove: "allowRemove", panelSearchType: "panelSearchType", panelLoading: "panelLoading", panelTotalRecords: "panelTotalRecords", panelPageSize: "panelPageSize" }, outputs: { datasourceChange: "datasourceChange", rowAdd: "rowAdd", rowDelete: "rowDelete", dragStarted: "dragStarted", dragEnded: "dragEnded", dragEntered: "dragEntered", dragExited: "dragExited", itemDropped: "itemDropped", itemMoved: "itemMoved", itemRemoved: "itemRemoved", itemSelected: "itemSelected", gridChanged: "gridChanged", panelSearch: "panelSearch", panelPageChange: "panelPageChange" }, ngImport: i0, template: "<div class=\"osl-fg-wrapper\" [class.osl-fg-wrapper--builder]=\"enableDragDrop\">\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 Available Controls panel (only in builder mode)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 Main grid area (always present)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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-fg-grid-area\">\n\n <!-- Panel-to-grid drop overlay: always in DOM so #gridDropZone is always resolvable.\n pointer-events: none by default; only activates visually when isDraggingFromPanel.\n CDK hit-tests by bounding rect, not pointer-events, so drops are captured regardless. -->\n <div class=\"osl-fg-table-drop-zone\"\n [class.osl-fg-table-drop-zone--active]=\"isDraggingFromPanel\"\n cdkDropList\n #gridDropZone=\"cdkDropList\"\n [cdkDropListSortingDisabled]=\"true\"\n [cdkDropListDisabled]=\"!enableDragDrop\"\n [cdkDropListData]=\"[]\"\n (cdkDropListDropped)=\"onPanelDrop($event)\">\n @if (isDraggingFromPanel) {\n <div class=\"osl-fg-drop-zone-badge\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n Drop to Add\n </div>\n }\n </div>\n\n <div class=\"osl-fg-table-container\"\n [style.height]=\"tableHeight\"\n [class.osl-fg-table-container--drop-active]=\"isDragging && enableDragDrop\">\n\n <table class=\"osl-fg-table\">\n\n <!-- \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <thead class=\"osl-fg-thead\">\n <tr>\n @if (enableDragDrop && allowReorder) {\n <th class=\"osl-fg-th osl-fg-th--drag\"></th>\n }\n @if (hasActions) {\n <th class=\"osl-fg-th osl-fg-th--actions\">\n @if (canAdd) {\n <button class=\"osl-grid-icon-btn\" (click)=\"addRow()\" [disabled]=\"loading\" title=\"Add row\">\n <mat-icon>add</mat-icon>\n </button>\n }\n </th>\n }\n @for (col of columns; track col.key) {\n <th class=\"osl-fg-th\" [style.width]=\"col.width\">\n {{ col.displayName }}\n @if (colRequired(col)) {\n <span class=\"osl-fg-th-required\">*</span>\n }\n </th>\n }\n </tr>\n </thead>\n\n <!-- \u2500\u2500 Body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <tbody\n cdkDropList\n #gridList=\"cdkDropList\"\n [cdkDropListData]=\"pagedData\"\n [cdkDropListDisabled]=\"!enableDragDrop\"\n (cdkDropListDropped)=\"onGridReorder($event)\">\n\n <!-- Loading state -->\n @if (loading) {\n @for (sk of skeletonRows; track $index) {\n <tr class=\"osl-fg-row osl-fg-row--skeleton\">\n @if (enableDragDrop && allowReorder) {\n <td class=\"osl-fg-td osl-fg-td--drag\"></td>\n }\n @if (hasActions) {\n <td class=\"osl-fg-td osl-fg-td--actions\">\n <div class=\"osl-fg-skeleton osl-fg-skeleton--sm\"></div>\n </td>\n }\n @for (col of columns; track col.key) {\n <td class=\"osl-fg-td\"><div class=\"osl-fg-skeleton\"></div></td>\n }\n </tr>\n }\n\n <!-- Empty state -->\n } @else if (pagedData.length === 0) {\n <tr>\n <td [attr.colspan]=\"columns.length + (hasActions ? 1 : 0) + (enableDragDrop && allowReorder ? 1 : 0)\"\n class=\"osl-fg-empty\">\n <div class=\"osl-fg-empty-inner\">\n @if (enableDragDrop && isDragging) {\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n <p>Drop here</p>\n } @else {\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/><line x1=\"9\" y1=\"9\" x2=\"9\" y2=\"21\"/>\n </svg>\n <p>No rows yet.{{ canAdd ? ' Use + to get started.' : (enableDragDrop ? ' Drag a control here.' : '') }}</p>\n }\n </div>\n </td>\n </tr>\n\n <!-- Data rows -->\n } @else {\n @for (row of pagedData; track $index; let i = $index) {\n <tr class=\"osl-fg-row\"\n cdkDrag\n [cdkDragData]=\"row\"\n [cdkDragDisabled]=\"!enableDragDrop\"\n (cdkDragStarted)=\"onDragStartedEvent($event, row, 'grid')\"\n (cdkDragEnded)=\"onDragEndedEvent($event, row, 'grid')\"\n (click)=\"enableDragDrop ? selectGridItem(row, i) : null\">\n\n <!-- Drag handle -->\n @if (enableDragDrop && allowReorder) {\n <td class=\"osl-fg-td osl-fg-td--drag\">\n <span class=\"osl-fg-row-handle\" cdkDragHandle title=\"Drag to reorder\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"9\" cy=\"5\" r=\"1.3\"/><circle cx=\"9\" cy=\"12\" r=\"1.3\"/><circle cx=\"9\" cy=\"19\" r=\"1.3\"/>\n <circle cx=\"15\" cy=\"5\" r=\"1.3\"/><circle cx=\"15\" cy=\"12\" r=\"1.3\"/><circle cx=\"15\" cy=\"19\" r=\"1.3\"/>\n </svg>\n </span>\n </td>\n }\n\n <!-- Action cell (remove / delete) -->\n @if (hasActions) {\n <td class=\"osl-fg-td osl-fg-td--actions\">\n @if (enableDragDrop && allowRemove) {\n <button class=\"osl-grid-icon-btn osl-grid-icon-btn--danger\"\n (click)=\"removeGridRow(i); $event.stopPropagation()\" title=\"Remove\">\n <mat-icon>close</mat-icon>\n </button>\n } @else if (!enableDragDrop && canDelete) {\n <button class=\"osl-grid-icon-btn osl-grid-icon-btn--danger\"\n (click)=\"deleteRow(i)\" title=\"Delete\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n }\n </td>\n }\n\n <!-- Data cells -->\n @for (col of columns; track col.key) {\n <td class=\"osl-fg-td\" [style.width]=\"col.width\">\n @if (col.formElem) {\n <div class=\"osl-fg-cell-form\" [style.width]=\"col.width\">\n @switch (col.formElem.elementType) {\n\n @case ('textbox') {\n <osl-input\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [type]=\"col.formElem.inputType || 'text'\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [mask]=\"col.formElem.mask || ''\"\n [min]=\"col.formElem.min ?? ''\"\n [max]=\"col.formElem.max ?? ''\"\n [minLength]=\"col.formElem.minLength ?? null\"\n [maxLength]=\"col.formElem.maxLength ?? null\"\n [prefixIcon]=\"col.formElem.prefixIcon || ''\"\n [suffixIcon]=\"col.formElem.suffixIcon || ''\"\n [decimalPortion]=\"col.formElem.decimalPortion ?? null\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-input>\n }\n\n @case ('textarea') {\n <osl-textarea\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [rows]=\"col.formElem.textareaRows || 3\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [maxLength]=\"col.formElem.maxLength ?? null\"\n [minLength]=\"col.formElem.minLength ?? null\"\n [characterCounter]=\"!!col.formElem.characterCounter\"\n [resize]=\"col.formElem.resize || 'none'\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-textarea>\n }\n\n @case ('select') {\n <osl-select\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [datasource]=\"col.formElem.datasource || []\"\n [displayField]=\"col.formElem.displayField || ''\"\n [valueField]=\"col.formElem.valueField || ''\"\n [placeholder]=\"col.formElem.selectPlaceholder || col.displayName\"\n [loading]=\"isLoading(row, col.formElem)\"\n [clearable]=\"!!col.formElem.clearable\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? onSelectChange(col, row, i, $event) : null\">\n </osl-select>\n }\n\n @case ('autocomplete') {\n <osl-autocomplete\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [datasource]=\"col.formElem.datasource || []\"\n [displayField]=\"col.formElem.displayField || ''\"\n [valueField]=\"col.formElem.valueField || ''\"\n [placeholder]=\"col.formElem.autocompletePlaceholder || col.displayName\"\n [loading]=\"isLoading(row, col.formElem)\"\n [searchType]=\"col.formElem.searchType ? col.formElem.searchType : 'Local'\"\n [methodName]=\"col.formElem.apiMethod || ''\"\n [configMethodName]=\"col.formElem.apiConfigMethod || ''\"\n [service]=\"col.formElem.apiService\"\n [object]=\"col.formElem.objectName ? row[col.formElem.objectName] : null\"\n [isLister]=\"!!col.formElem.isListerAutocomplete\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? onSelectChange(col, row, i, $event) : null\">\n </osl-autocomplete>\n }\n\n @case ('radio') {\n <osl-radio\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [datasource]=\"col.formElem.datasource || []\"\n [displayField]=\"col.formElem.displayField || ''\"\n [valueField]=\"col.formElem.valueField || ''\"\n [inline]=\"!!col.formElem.inline\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-radio>\n }\n\n @case ('checkbox') {\n <osl-checkbox\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [indeterminate]=\"!!col.formElem.indeterminate\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-checkbox>\n }\n\n @case ('slide-toggle') {\n <osl-slide-toggle\n [label]=\"''\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [labelPosition]=\"col.formElem.labelPosition || 'after'\"\n [trueLabel]=\"col.formElem.trueLabel || ''\"\n [falseLabel]=\"col.formElem.falseLabel || ''\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-slide-toggle>\n }\n\n @case ('datepicker') {\n <osl-datepicker\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [dateType]=\"col.formElem.dateType || 'date'\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [minDate]=\"col.formElem.minDate || ''\"\n [maxDate]=\"col.formElem.maxDate || ''\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-datepicker>\n }\n\n @case ('datetimepicker') {\n <osl-datetimepicker\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [minDatetime]=\"col.formElem.minDatetime || ''\"\n [maxDatetime]=\"col.formElem.maxDatetime || ''\"\n [format]=\"col.formElem.datetimepickerFormat || 'YYYY-MM-DDTHH:mm'\"\n [showSeconds]=\"!!col.formElem.datetimepickerShowSeconds\"\n [enableMeridian]=\"!!col.formElem.datetimepickerEnableMeridian\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-datetimepicker>\n }\n\n @case ('file-uploader') {\n <osl-file-upload\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [accept]=\"col.formElem.accept || ''\"\n [multiple]=\"!!col.formElem.multiple\"\n [maxSize]=\"col.formElem.maxFileSize || 0\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-file-upload>\n }\n\n @case ('templateRef') {\n <ng-container *ngTemplateOutlet=\"col.formElem.templateRef; context: { $implicit: row, index: i }\"></ng-container>\n }\n\n }\n </div>\n } @else {\n <span class=\"osl-fg-cell-text\">{{ row[col.key] ?? '--' }}</span>\n }\n </td>\n }\n\n <!-- Row drag preview -->\n <ng-template cdkDragPreview>\n <div class=\"osl-fg-row-preview\">\n @for (col of columns; track col.key) {\n <span class=\"osl-fg-row-preview__cell\">{{ row[col.key] ?? '\u2014' }}</span>\n }\n </div>\n </ng-template>\n\n <!-- Drop placeholder: spans full row width with \"Drop Here\" -->\n <ng-template cdkDragPlaceholder>\n <tr class=\"osl-fg-row-placeholder\">\n <td [attr.colspan]=\"columns.length + (hasActions ? 1 : 0) + (enableDragDrop && allowReorder ? 1 : 0)\">\n <div class=\"osl-fg-drop-here\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n Drop Here\n </div>\n </td>\n </tr>\n </ng-template>\n\n </tr>\n }\n }\n\n </tbody>\n\n <!-- \u2500\u2500 Footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (footerColumns && footerColumns.length > 0 && datasource && datasource.length > 0) {\n <tfoot class=\"osl-fg-tfoot\">\n <tr class=\"osl-fg-footer-row\">\n @for (fcol of footerColumns; track $index) {\n <td\n [class]=\"'osl-fg-footer-td ' + (fcol.class || '')\"\n [attr.colspan]=\"fcol.colspan || 1\">\n {{ fcol.displayFn ? fcol.displayFn(datasource) : (fcol.display || '') }}\n </td>\n }\n </tr>\n </tfoot>\n }\n\n </table>\n </div>\n\n <!-- \u2500\u2500 Pagination (unchanged) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (isPaginated) {\n <div class=\"osl-fg-pagination\">\n\n <span class=\"osl-fg-pagination__info\">\n @if (loading) {\n Loading...\n } @else if (_total > 0) {\n {{ startRecord }}\u2013{{ endRecord }} of {{ _total }} rows\n } @else {\n No rows\n }\n </span>\n\n <div class=\"osl-fg-pagination__controls\">\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(1)\" [disabled]=\"currentPage === 1 || loading\">\u00AB</button>\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(currentPage - 1)\" [disabled]=\"currentPage === 1 || loading\">\u2039 Prev</button>\n\n @for (page of pageNumbers; track $index) {\n @if (page === -1) {\n <span class=\"osl-fg-page-ellipsis\">\u2026</span>\n } @else {\n <button\n class=\"osl-fg-page-btn osl-fg-page-btn--num\"\n [class.osl-fg-page-btn--active]=\"page === currentPage\"\n [disabled]=\"loading\"\n (click)=\"goToPage(page)\">\n {{ page }}\n </button>\n }\n }\n\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(currentPage + 1)\" [disabled]=\"currentPage === totalPages || loading\">Next \u203A</button>\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(totalPages)\" [disabled]=\"currentPage === totalPages || loading\">\u00BB</button>\n </div>\n\n <div class=\"osl-fg-pagination__size\">\n <select class=\"osl-fg-page-size\" [ngModel]=\"pageSize\" (ngModelChange)=\"onPageSizeChange($event)\" [disabled]=\"loading\">\n @for (opt of pageSizeOptions; track opt) {\n <option [value]=\"opt\">{{ opt }} per page</option>\n }\n </select>\n </div>\n\n </div>\n }\n\n </div><!-- /.osl-fg-grid-area -->\n @if (enableDragDrop) {\n <div class=\"osl-fg-panel\" [style.width.px]=\"leftPanelWidth\" [style.height]=\"tableHeight\">\n\n <!-- Panel header -->\n <div class=\"osl-fg-panel__header\">\n <span class=\"osl-fg-panel__title\">{{ dragTitle }}</span>\n @if (panelSearchType === 'Api' && panelTotalRecords > 0) {\n <span class=\"osl-fg-panel__count\">{{ draggingList.length }}/{{ panelTotalRecords }}</span>\n } @else if (filteredDraggingList.length > 0) {\n <span class=\"osl-fg-panel__count\">{{ filteredDraggingList.length }}</span>\n }\n </div>\n\n <!-- Search box -->\n @if (showSearch) {\n <div class=\"osl-fg-panel__search\">\n <svg class=\"osl-fg-search-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/>\n </svg>\n <input\n class=\"osl-fg-search-input\"\n type=\"text\"\n [ngModel]=\"dragSearchQuery\"\n (ngModelChange)=\"onPanelSearchChange($event)\"\n placeholder=\"Search\u2026\"\n autocomplete=\"off\" />\n @if (dragSearchQuery) {\n <button class=\"osl-fg-search-clear\" (click)=\"clearPanelSearch()\" title=\"Clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n }\n </div>\n }\n\n <!-- Item list -->\n <div class=\"osl-fg-panel__list\"\n cdkDropList\n #availableList=\"cdkDropList\"\n [cdkDropListData]=\"filteredDraggingList\"\n [cdkDropListConnectedTo]=\"[gridDropZone]\"\n [cdkDropListSortingDisabled]=\"true\"\n (cdkDropListEntered)=\"onDragEnteredEvent($event, 'available')\"\n (cdkDropListExited)=\"onDragExitedEvent($event, 'available')\">\n\n <!-- Loading skeleton -->\n @if (panelLoading && draggingList.length === 0) {\n @for (sk of [1,2,3,4,5]; track sk) {\n <div class=\"osl-fg-item-skeleton\"></div>\n }\n\n } @else if (filteredDraggingList.length === 0) {\n <div class=\"osl-fg-panel__empty\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/>\n </svg>\n <span>{{ dragSearchQuery ? 'No results found' : 'No results available' }}</span>\n </div>\n\n } @else {\n @for (item of filteredDraggingList; track item.id) {\n <div class=\"osl-fg-drag-item\"\n cdkDrag\n [cdkDragData]=\"item\"\n (cdkDragStarted)=\"onDragStartedEvent($event, item, 'available')\"\n (cdkDragEnded)=\"onDragEndedEvent($event, item, 'available')\">\n\n <!-- Grip handle -->\n <span class=\"osl-fg-drag-item__grip\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"9\" cy=\"5\" r=\"1.4\"/><circle cx=\"9\" cy=\"12\" r=\"1.4\"/><circle cx=\"9\" cy=\"19\" r=\"1.4\"/>\n <circle cx=\"15\" cy=\"5\" r=\"1.4\"/><circle cx=\"15\" cy=\"12\" r=\"1.4\"/><circle cx=\"15\" cy=\"19\" r=\"1.4\"/>\n </svg>\n </span>\n\n <!-- Label + description -->\n <div class=\"osl-fg-drag-item__body\">\n <span class=\"osl-fg-drag-item__label\">{{ item.label }}</span>\n @if (item.description) {\n <span class=\"osl-fg-drag-item__desc\">{{ item.description }}</span>\n }\n </div>\n\n <!-- Drag-into-grid hint -->\n <span class=\"osl-fg-drag-item__arrow\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/><polyline points=\"12 5 19 12 12 19\"/>\n </svg>\n </span>\n\n <!-- Placeholder shown in panel while card is dragged -->\n <div *cdkDragPlaceholder class=\"osl-fg-item-placeholder\"></div>\n\n <!-- Preview under the cursor while dragging -->\n <ng-template cdkDragPreview>\n <div class=\"osl-fg-drag-preview\">{{ item.label }}</div>\n </ng-template>\n\n </div>\n }\n\n <!-- Load More (API mode) -->\n @if (panelHasMore) {\n <button class=\"osl-fg-load-more\" (click)=\"loadMorePanelItems()\" [disabled]=\"panelLoading\">\n @if (panelLoading) {\n <span class=\"osl-fg-load-more__spinner\"></span> Loading\u2026\n } @else {\n Load More\n }\n </button>\n }\n\n <!-- Inline loading indicator while appending -->\n @if (panelLoading && draggingList.length > 0) {\n <div class=\"osl-fg-item-skeleton\"></div>\n }\n }\n\n </div>\n </div>\n }\n</div><!-- /.osl-fg-wrapper -->\n", styles: [".osl-fg-wrapper{display:flex;flex-direction:column;width:100%;font-size:var(--osl-text-font-size);font-family:inherit;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 8px);overflow:hidden;background:#fff}.osl-fg-wrapper--builder{flex-direction:row;overflow:visible;align-items:stretch;gap:14px;border:none;background:transparent}.osl-fg-grid-area{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;overflow:hidden;border-radius:inherit;position:relative}.osl-fg-wrapper--builder .osl-fg-grid-area{border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 8px);background:#fff}.osl-fg-table-drop-zone{position:absolute;inset:0;z-index:3;pointer-events:none;border-radius:inherit;transition:background .15s,border-color .15s}.osl-fg-table-drop-zone--active{border:2px dashed var(--osl-primary, #6366f1);background:#6366f108}.osl-fg-table-drop-zone--active.cdk-drop-list-receiving{background:#6366f114;border-style:solid}.osl-fg-drop-zone-badge{position:absolute;bottom:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:5px;padding:5px 13px;background:var(--osl-primary, #6366f1);color:#fff;border-radius:20px;font-size:11px;font-weight:600;white-space:nowrap;pointer-events:none;box-shadow:0 4px 12px #6366f159}.osl-fg-table-container{overflow-x:auto;overflow-y:auto;min-height:120px;flex:1 1 auto}.osl-fg-table-container--drop-active{outline:2px dashed var(--osl-primary, #6366f1);outline-offset:-2px}.osl-fg-table-container--drop-active .osl-fg-empty-inner{color:var(--osl-primary, #6366f1)}.osl-fg-table-container--drop-active .osl-fg-empty-inner svg{opacity:.7}.osl-fg-table{width:100%;border-collapse:collapse;table-layout:auto}.osl-fg-thead{position:sticky;top:0;z-index:2}.osl-fg-th{background:#f3f4f6;color:#4b5563;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.04em;padding:10px 12px;text-align:left;border-bottom:2px solid var(--osl-border-color, #e5e7eb);border-right:1px solid var(--osl-border-color, #e5e7eb);white-space:nowrap;vertical-align:middle}.osl-fg-th:last-child{border-right:none}.osl-fg-th--actions{width:44px;min-width:44px;text-align:center;padding:6px}.osl-fg-th--drag{width:28px;min-width:28px;padding:0}.osl-fg-th-required{color:#ef4444;margin-left:2px;font-size:14px;line-height:1;vertical-align:middle}.osl-fg-row{border-bottom:1px solid #f0f0f0;transition:background .1s}.osl-fg-row:nth-child(odd){background:#f8fafc}.osl-fg-row:nth-child(2n){background:#fff}.osl-fg-row:hover{background:#eef2ff!important}.osl-fg-row:last-child{border-bottom:none}.osl-fg-row--skeleton{pointer-events:none}.osl-fg-td{padding:4px 10px;vertical-align:middle;border-right:1px solid #f0f0f0}.osl-fg-td:last-child{border-right:none}.osl-fg-td--actions{width:44px;min-width:44px;text-align:center;padding:4px 6px}.osl-fg-td--drag{width:28px;min-width:28px;padding:0;text-align:center}.osl-fg-cell-form{min-width:100px}.osl-fg-cell-form ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none!important}.osl-fg-cell-form ::ng-deep .mat-mdc-form-field,.osl-fg-cell-form ::ng-deep mat-form-field{width:100%}.osl-fg-cell-form ::ng-deep .mat-mdc-text-field-wrapper{padding-top:0}.osl-fg-cell-text{color:#111827;font-size:var(--osl-text-font-size)}.osl-grid-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1.5px solid var(--osl-border-color);border-radius:var(--osl-border-radius);background:transparent;color:#6b7280;cursor:pointer;padding:0;transition:border-color .15s,color .15s,background .15s;margin:0 2px}.osl-grid-icon-btn mat-icon{font-size:16px;width:16px;height:16px;line-height:16px}.osl-grid-icon-btn:hover{border-color:var(--osl-primary);color:var(--osl-primary);background:#6366f10f}.osl-grid-icon-btn--danger:hover{border-color:#ef4444;color:#ef4444;background:#ef44440f}.osl-fg-row-handle{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;cursor:grab;color:#d1d5db;border-radius:4px;transition:color .12s,background .12s}.osl-fg-row-handle svg{width:13px;height:13px}.osl-fg-row-handle:hover{color:#9ca3af;background:#f3f4f6}.osl-fg-row-handle:active{cursor:grabbing}.osl-fg-row-placeholder td{padding:4px 6px;background:transparent;border:none}.osl-fg-drop-here{display:flex;align-items:center;justify-content:center;gap:8px;height:46px;border:2px dashed var(--osl-primary, #6366f1);border-radius:6px;background:#eef2ff;color:var(--osl-primary, #6366f1);font-size:12px;font-weight:600;letter-spacing:.03em}.osl-fg-drop-here svg{width:16px;height:16px;flex-shrink:0}.osl-fg-row-preview{display:flex;align-items:center;gap:14px;padding:9px 16px;background:#fff;border:1.5px solid var(--osl-primary, #6366f1);border-radius:8px;box-shadow:0 8px 24px #6366f12e;font-size:12px;color:#374151;max-width:420px;white-space:nowrap;overflow:hidden}.osl-fg-row-preview__cell{overflow:hidden;text-overflow:ellipsis;max-width:130px}.osl-fg-row-preview__cell+.osl-fg-row-preview__cell{padding-left:14px;border-left:1px solid #e5e7eb}.osl-fg-empty{padding:40px 16px;text-align:center}.osl-fg-empty-inner{display:flex;flex-direction:column;align-items:center;gap:10px;color:#9ca3af}.osl-fg-empty-inner svg{opacity:.4}.osl-fg-empty-inner p{margin:0;font-size:13px}@keyframes osl-fg-pulse{0%,to{opacity:1}50%{opacity:.4}}.osl-fg-skeleton{height:36px;border-radius:6px;background:#e5e7eb;animation:osl-fg-pulse 1.4s ease-in-out infinite}.osl-fg-skeleton--sm{height:28px;width:28px;border-radius:4px;margin:0 auto}.osl-fg-tfoot{position:sticky;bottom:0;z-index:2}.osl-fg-footer-td{background:#f3f4f6;color:#4b5563;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.04em;padding:10px 12px;text-align:left;border-top:2px solid var(--osl-border-color, #e5e7eb);border-right:1px solid var(--osl-border-color, #e5e7eb);white-space:nowrap;vertical-align:middle}.osl-fg-footer-td:last-child{border-right:none}.osl-fg-pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 16px;border-top:1px solid var(--osl-border-color, #e5e7eb);background:#f9fafb;flex-shrink:0;flex-wrap:wrap}.osl-fg-pagination__info{font-size:12px;color:#6b7280;white-space:nowrap;min-width:120px}.osl-fg-pagination__controls{display:flex;align-items:center;gap:3px;flex-wrap:nowrap}.osl-fg-pagination__size{display:flex;align-items:center;justify-content:flex-end;min-width:120px}.osl-fg-page-btn{display:inline-flex;align-items:center;justify-content:center;height:30px;padding:0 10px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:13px;font-family:inherit;cursor:pointer;transition:background .12s,border-color .12s,color .12s;white-space:nowrap}.osl-fg-page-btn:hover:not(:disabled){background:#f3f4f6;border-color:#9ca3af}.osl-fg-page-btn:disabled{opacity:.35;cursor:not-allowed}.osl-fg-page-btn--nav{font-size:12px;color:#6b7280}.osl-fg-page-btn--num{min-width:30px;padding:0;font-size:13px}.osl-fg-page-btn--active{background:var(--osl-primary, #6366f1);border-color:var(--osl-primary, #6366f1);color:#fff;font-weight:600}.osl-fg-page-btn--active:hover:not(:disabled){background:var(--osl-primary-hover, #4f46e5);border-color:var(--osl-primary-hover, #4f46e5)}.osl-fg-page-ellipsis{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;color:#9ca3af;font-size:13px;pointer-events:none}.osl-fg-page-size{height:30px;padding:0 8px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:12px;font-family:inherit;cursor:pointer;outline:none}.osl-fg-page-size:focus{border-color:var(--osl-primary, #6366f1)}.osl-fg-page-size:disabled{opacity:.4;cursor:not-allowed}.osl-fg-panel{flex:0 0 auto;display:flex;flex-direction:column;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 8px);background:linear-gradient(180deg,#eceafb,#e2defa);box-shadow:0 2px 6px #0f172a0f,0 8px 20px -6px #6366f129;min-height:200px;overflow:hidden}.osl-fg-panel__header{display:flex;align-items:center;justify-content:space-between;padding:11px 14px 10px;border-bottom:1px solid rgba(99,102,241,.18);background:#6366f11a;flex-shrink:0}.osl-fg-panel__title{font-size:11px;font-weight:700;color:#374151;text-transform:uppercase;letter-spacing:.06em}.osl-fg-panel__count{font-size:11px;font-weight:600;color:#9ca3af;background:#e5e7eb;border-radius:10px;padding:1px 7px;line-height:18px}.osl-fg-panel__search{position:relative;padding:8px 10px;border-bottom:1px solid rgba(99,102,241,.14);flex-shrink:0;background:#ffffff8c}.osl-fg-search-icon{position:absolute;left:20px;top:50%;transform:translateY(-50%);width:13px;height:13px;color:#d1d5db;pointer-events:none}.osl-fg-search-input{width:100%;height:30px;padding:0 26px 0 30px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:6px;background:#f9fafb;font-size:12px;font-family:inherit;color:#374151;outline:none;box-sizing:border-box;transition:border-color .15s,background .15s}.osl-fg-search-input:focus{border-color:var(--osl-primary, #6366f1);background:#fff}.osl-fg-search-input::placeholder{color:#d1d5db}.osl-fg-search-clear{position:absolute;right:16px;top:50%;transform:translateY(-50%);width:18px;height:18px;padding:0;border:none;background:transparent;color:#9ca3af;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:3px}.osl-fg-search-clear svg{width:11px;height:11px}.osl-fg-search-clear:hover{color:#374151}.osl-fg-panel__list{flex:1 1 auto;min-height:0;overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:2px}.osl-fg-panel__empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:28px 12px;color:#c4c9d4;font-size:12px;text-align:center}.osl-fg-panel__empty svg{width:26px;height:26px;opacity:.5}.osl-fg-drag-item{display:flex;align-items:center;gap:9px;padding:8px 10px 8px 8px;border-radius:6px;cursor:grab;-webkit-user-select:none;user-select:none;transition:background .12s;background:transparent;position:relative}.osl-fg-drag-item:hover{background:#f0f4ff}.osl-fg-drag-item:hover .osl-fg-drag-item__grip{opacity:1;background:#6366f12e;color:var(--osl-primary, #6366f1)}.osl-fg-drag-item:hover .osl-fg-drag-item__arrow{opacity:.5}.osl-fg-drag-item:active{cursor:grabbing}.osl-fg-drag-item__grip{flex-shrink:0;width:20px;height:20px;opacity:.85;color:var(--osl-primary, #6366f1);background:#6366f11a;border-radius:4px;transition:opacity .15s,background .15s,color .15s;display:flex;align-items:center;justify-content:center}.osl-fg-drag-item__grip svg{width:13px;height:13px}.osl-fg-drag-item__body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px}.osl-fg-drag-item__label{font-size:13px;font-weight:500;color:#1f2937;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.4}.osl-fg-drag-item__desc{font-size:11px;color:#9ca3af;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.3}.osl-fg-drag-item__arrow{flex-shrink:0;opacity:0;color:var(--osl-primary, #6366f1);transition:opacity .15s}.osl-fg-drag-item__arrow svg{width:13px;height:13px}.osl-fg-item-placeholder{height:36px;border:1.5px dashed #d1d5db;border-radius:6px;background:#f9fafb}.osl-fg-drag-preview{padding:8px 16px;background:var(--osl-primary, #6366f1);color:#fff;border-radius:8px;box-shadow:0 6px 18px #6366f159;font-size:12px;font-weight:600;white-space:nowrap;pointer-events:none}.osl-fg-item-skeleton{height:36px;border-radius:6px;background:#e5e7eb;animation:osl-fg-pulse 1.4s ease-in-out infinite;flex-shrink:0}.osl-fg-load-more{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;height:32px;margin-top:4px;border:1px dashed var(--osl-border-color, #e5e7eb);border-radius:6px;background:transparent;color:#6b7280;font-size:12px;font-family:inherit;cursor:pointer;transition:border-color .15s,color .15s,background .15s;flex-shrink:0}.osl-fg-load-more:hover:not(:disabled){border-color:var(--osl-primary, #6366f1);color:var(--osl-primary, #6366f1);background:#f5f3ff}.osl-fg-load-more:disabled{opacity:.5;cursor:not-allowed}.osl-fg-load-more__spinner{width:12px;height:12px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:osl-fg-spin .7s linear infinite;display:inline-block}@keyframes osl-fg-spin{to{transform:rotate(360deg)}}:host ::ng-deep .cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}:host ::ng-deep .cdk-drop-list-dragging .osl-fg-drag-item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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.CdkDragPreview, selector: "ng-template[cdkDragPreview]", inputs: ["data", "matchSize"] }, { kind: "directive", type: i3$1.CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "component", type: Oslinput, selector: "osl-input", inputs: ["label", "required", "disabled", "model", "type", "placeholder", "mask", "min", "max", "minLength", "maxLength", "prefixIcon", "suffixIcon", "suffixRef", "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", "displayFn"], outputs: ["datasourceChange", "modelChange", "changeEv"] }, { kind: "component", type: OslFileUpload, selector: "osl-file-upload", inputs: ["label", "required", "disabled", "model", "accept", "multiple", "maxSize", "skeletonLoading", "skeletonTheme", "fileMode", "downloadFn"], 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"] }] });
|
|
4527
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslFormGrid, isStandalone: false, selector: "osl-form-grid", inputs: { columns: "columns", datasource: "datasource", isPaginated: "isPaginated", pageSize: "pageSize", canAdd: "canAdd", canDelete: "canDelete", loading: "loading", tableHeight: "tableHeight", footerColumns: "footerColumns", enableDragDrop: "enableDragDrop", draggingList: "draggingList", draggingColumns: "draggingColumns", leftPanelWidth: "leftPanelWidth", dragTitle: "dragTitle", showSearch: "showSearch", allowReorder: "allowReorder", allowRemove: "allowRemove", reorderDisabledIf: "reorderDisabledIf", panelSearchType: "panelSearchType", panelLoading: "panelLoading", panelTotalRecords: "panelTotalRecords", panelPageSize: "panelPageSize" }, outputs: { datasourceChange: "datasourceChange", rowAdd: "rowAdd", rowDelete: "rowDelete", dragStarted: "dragStarted", dragEnded: "dragEnded", dragEntered: "dragEntered", dragExited: "dragExited", itemDropped: "itemDropped", itemMoved: "itemMoved", itemRemoved: "itemRemoved", itemSelected: "itemSelected", gridChanged: "gridChanged", panelSearch: "panelSearch", panelPageChange: "panelPageChange" }, ngImport: i0, template: "<div class=\"osl-fg-wrapper\" [class.osl-fg-wrapper--builder]=\"enableDragDrop\">\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 Available Controls panel (only in builder mode)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 Main grid area (always present)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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-fg-grid-area\">\n\n <!-- Panel-to-grid drop overlay: always in DOM so #gridDropZone is always resolvable.\n pointer-events: none by default; only activates visually when isDraggingFromPanel.\n CDK hit-tests by bounding rect, not pointer-events, so drops are captured regardless. -->\n <div class=\"osl-fg-table-drop-zone\"\n [class.osl-fg-table-drop-zone--active]=\"isDraggingFromPanel\"\n cdkDropList\n #gridDropZone=\"cdkDropList\"\n [cdkDropListSortingDisabled]=\"true\"\n [cdkDropListDisabled]=\"!enableDragDrop\"\n [cdkDropListData]=\"[]\"\n (cdkDropListDropped)=\"onPanelDrop($event)\">\n @if (isDraggingFromPanel) {\n <div class=\"osl-fg-drop-zone-badge\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n Drop to Add\n </div>\n }\n </div>\n\n <div class=\"osl-fg-table-container\"\n [style.height]=\"tableHeight\"\n [class.osl-fg-table-container--drop-active]=\"isDragging && enableDragDrop\">\n\n <table class=\"osl-fg-table\">\n\n <!-- \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <thead class=\"osl-fg-thead\">\n <tr>\n @if (allowReorder) {\n <th class=\"osl-fg-th osl-fg-th--drag\"></th>\n }\n @if (hasActions) {\n <th class=\"osl-fg-th osl-fg-th--actions\">\n @if (canAdd) {\n <button class=\"osl-grid-icon-btn\" (click)=\"addRow()\" [disabled]=\"loading\" title=\"Add row\">\n <mat-icon>add</mat-icon>\n </button>\n }\n </th>\n }\n @for (col of columns; track col.key) {\n <th class=\"osl-fg-th\" [style.width]=\"col.width\">\n {{ col.displayName }}\n @if (colRequired(col)) {\n <span class=\"osl-fg-th-required\">*</span>\n }\n </th>\n }\n </tr>\n </thead>\n\n <!-- \u2500\u2500 Body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <tbody\n cdkDropList\n #gridList=\"cdkDropList\"\n [cdkDropListData]=\"pagedData\"\n [cdkDropListDisabled]=\"!allowReorder\"\n (cdkDropListDropped)=\"onGridReorder($event)\">\n\n <!-- Loading state -->\n @if (loading) {\n @for (sk of skeletonRows; track $index) {\n <tr class=\"osl-fg-row osl-fg-row--skeleton\">\n @if (allowReorder) {\n <td class=\"osl-fg-td osl-fg-td--drag\"></td>\n }\n @if (hasActions) {\n <td class=\"osl-fg-td osl-fg-td--actions\">\n <div class=\"osl-fg-skeleton osl-fg-skeleton--sm\"></div>\n </td>\n }\n @for (col of columns; track col.key) {\n <td class=\"osl-fg-td\"><div class=\"osl-fg-skeleton\"></div></td>\n }\n </tr>\n }\n\n <!-- Empty state -->\n } @else if (pagedData.length === 0) {\n <tr>\n <td [attr.colspan]=\"columns.length + (hasActions ? 1 : 0) + (allowReorder ? 1 : 0)\"\n class=\"osl-fg-empty\">\n <div class=\"osl-fg-empty-inner\">\n @if (enableDragDrop && isDragging) {\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n <p>Drop here</p>\n } @else {\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/><line x1=\"9\" y1=\"9\" x2=\"9\" y2=\"21\"/>\n </svg>\n <p>No rows yet.{{ canAdd ? ' Use + to get started.' : (enableDragDrop ? ' Drag a control here.' : '') }}</p>\n }\n </div>\n </td>\n </tr>\n\n <!-- Data rows -->\n } @else {\n @for (row of pagedData; track $index; let i = $index) {\n <tr class=\"osl-fg-row\"\n cdkDrag\n [cdkDragData]=\"row\"\n [cdkDragDisabled]=\"!allowReorder || isReorderDisabled(row)\"\n (cdkDragStarted)=\"onDragStartedEvent($event, row, 'grid')\"\n (cdkDragEnded)=\"onDragEndedEvent($event, row, 'grid')\"\n (click)=\"enableDragDrop ? selectGridItem(row, i) : null\">\n\n <!-- Drag handle -->\n @if (allowReorder) {\n <td class=\"osl-fg-td osl-fg-td--drag\">\n <span class=\"osl-fg-row-handle\"\n [class.osl-fg-row-handle--disabled]=\"isReorderDisabled(row)\"\n cdkDragHandle\n [cdkDragHandleDisabled]=\"isReorderDisabled(row)\"\n [title]=\"isReorderDisabled(row) ? 'Reorder disabled' : 'Drag to reorder'\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"9\" cy=\"5\" r=\"1.3\"/><circle cx=\"9\" cy=\"12\" r=\"1.3\"/><circle cx=\"9\" cy=\"19\" r=\"1.3\"/>\n <circle cx=\"15\" cy=\"5\" r=\"1.3\"/><circle cx=\"15\" cy=\"12\" r=\"1.3\"/><circle cx=\"15\" cy=\"19\" r=\"1.3\"/>\n </svg>\n </span>\n </td>\n }\n\n <!-- Action cell (remove / delete) -->\n @if (hasActions) {\n <td class=\"osl-fg-td osl-fg-td--actions\">\n @if (enableDragDrop && allowRemove) {\n <button class=\"osl-grid-icon-btn osl-grid-icon-btn--danger\"\n (click)=\"removeGridRow(i); $event.stopPropagation()\" title=\"Remove\">\n <mat-icon>close</mat-icon>\n </button>\n } @else if (!enableDragDrop && canDelete) {\n <button class=\"osl-grid-icon-btn osl-grid-icon-btn--danger\"\n (click)=\"deleteRow(i)\" title=\"Delete\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n }\n </td>\n }\n\n <!-- Data cells -->\n @for (col of columns; track col.key) {\n <td class=\"osl-fg-td\" [style.width]=\"col.width\">\n @if (col.formElem) {\n <div class=\"osl-fg-cell-form\" [style.width]=\"col.width\">\n @switch (col.formElem.elementType) {\n\n @case ('textbox') {\n <osl-input\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [type]=\"col.formElem.inputType || 'text'\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [mask]=\"col.formElem.mask || ''\"\n [min]=\"col.formElem.min ?? ''\"\n [max]=\"col.formElem.max ?? ''\"\n [minLength]=\"col.formElem.minLength ?? null\"\n [maxLength]=\"col.formElem.maxLength ?? null\"\n [prefixIcon]=\"col.formElem.prefixIcon || ''\"\n [suffixIcon]=\"col.formElem.suffixIcon || ''\"\n [decimalPortion]=\"col.formElem.decimalPortion ?? null\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-input>\n }\n\n @case ('textarea') {\n <osl-textarea\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [rows]=\"col.formElem.textareaRows || 3\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [maxLength]=\"col.formElem.maxLength ?? null\"\n [minLength]=\"col.formElem.minLength ?? null\"\n [characterCounter]=\"!!col.formElem.characterCounter\"\n [resize]=\"col.formElem.resize || 'none'\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-textarea>\n }\n\n @case ('select') {\n <osl-select\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [datasource]=\"col.formElem.datasource || []\"\n [displayField]=\"col.formElem.displayField || ''\"\n [valueField]=\"col.formElem.valueField || ''\"\n [placeholder]=\"col.formElem.selectPlaceholder || col.displayName\"\n [loading]=\"isLoading(row, col.formElem)\"\n [clearable]=\"!!col.formElem.clearable\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? onSelectChange(col, row, i, $event) : null\">\n </osl-select>\n }\n\n @case ('autocomplete') {\n <osl-autocomplete\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [datasource]=\"col.formElem.datasource || []\"\n [displayField]=\"col.formElem.displayField || ''\"\n [valueField]=\"col.formElem.valueField || ''\"\n [placeholder]=\"col.formElem.autocompletePlaceholder || col.displayName\"\n [loading]=\"isLoading(row, col.formElem)\"\n [searchType]=\"col.formElem.searchType ? col.formElem.searchType : 'Local'\"\n [methodName]=\"col.formElem.apiMethod || ''\"\n [configMethodName]=\"col.formElem.apiConfigMethod || ''\"\n [service]=\"col.formElem.apiService\"\n [object]=\"col.formElem.objectName ? row[col.formElem.objectName] : null\"\n [isLister]=\"!!col.formElem.isListerAutocomplete\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? onSelectChange(col, row, i, $event) : null\">\n </osl-autocomplete>\n }\n\n @case ('radio') {\n <osl-radio\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [datasource]=\"col.formElem.datasource || []\"\n [displayField]=\"col.formElem.displayField || ''\"\n [valueField]=\"col.formElem.valueField || ''\"\n [inline]=\"!!col.formElem.inline\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-radio>\n }\n\n @case ('checkbox') {\n <osl-checkbox\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [indeterminate]=\"!!col.formElem.indeterminate\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-checkbox>\n }\n\n @case ('slide-toggle') {\n <osl-slide-toggle\n [label]=\"''\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [labelPosition]=\"col.formElem.labelPosition || 'after'\"\n [trueLabel]=\"col.formElem.trueLabel || ''\"\n [falseLabel]=\"col.formElem.falseLabel || ''\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-slide-toggle>\n }\n\n @case ('datepicker') {\n <osl-datepicker\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [dateType]=\"col.formElem.dateType || 'date'\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [minDate]=\"col.formElem.minDate || ''\"\n [maxDate]=\"col.formElem.maxDate || ''\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-datepicker>\n }\n\n @case ('datetimepicker') {\n <osl-datetimepicker\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [minDatetime]=\"col.formElem.minDatetime || ''\"\n [maxDatetime]=\"col.formElem.maxDatetime || ''\"\n [format]=\"col.formElem.datetimepickerFormat || 'YYYY-MM-DDTHH:mm'\"\n [showSeconds]=\"!!col.formElem.datetimepickerShowSeconds\"\n [enableMeridian]=\"!!col.formElem.datetimepickerEnableMeridian\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-datetimepicker>\n }\n\n @case ('file-uploader') {\n <osl-file-upload\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [accept]=\"col.formElem.accept || ''\"\n [multiple]=\"!!col.formElem.multiple\"\n [maxSize]=\"col.formElem.maxFileSize || 0\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-file-upload>\n }\n\n @case ('templateRef') {\n <ng-container *ngTemplateOutlet=\"col.formElem.templateRef; context: { $implicit: row, index: i }\"></ng-container>\n }\n\n }\n </div>\n } @else {\n <span class=\"osl-fg-cell-text\">{{ row[col.key] ?? '--' }}</span>\n }\n </td>\n }\n\n <!-- Row drag preview -->\n <ng-template cdkDragPreview>\n <div class=\"osl-fg-row-preview\">\n @for (col of columns; track col.key) {\n <span class=\"osl-fg-row-preview__cell\">{{ row[col.key] ?? '\u2014' }}</span>\n }\n </div>\n </ng-template>\n\n <!-- Drop placeholder: spans full row width with \"Drop Here\" -->\n <ng-template cdkDragPlaceholder>\n <tr class=\"osl-fg-row-placeholder\">\n <td [attr.colspan]=\"columns.length + (hasActions ? 1 : 0) + (allowReorder ? 1 : 0)\">\n <div class=\"osl-fg-drop-here\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n Drop Here\n </div>\n </td>\n </tr>\n </ng-template>\n\n </tr>\n }\n }\n\n </tbody>\n\n <!-- \u2500\u2500 Footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (footerColumns && footerColumns.length > 0 && datasource && datasource.length > 0) {\n <tfoot class=\"osl-fg-tfoot\">\n <tr class=\"osl-fg-footer-row\">\n @for (fcol of footerColumns; track $index) {\n <td\n [class]=\"'osl-fg-footer-td ' + (fcol.class || '')\"\n [attr.colspan]=\"fcol.colspan || 1\">\n {{ fcol.displayFn ? fcol.displayFn(datasource) : (fcol.display || '') }}\n </td>\n }\n </tr>\n </tfoot>\n }\n\n </table>\n </div>\n\n <!-- \u2500\u2500 Pagination (unchanged) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (isPaginated) {\n <div class=\"osl-fg-pagination\">\n\n <span class=\"osl-fg-pagination__info\">\n @if (loading) {\n Loading...\n } @else if (_total > 0) {\n {{ startRecord }}\u2013{{ endRecord }} of {{ _total }} rows\n } @else {\n No rows\n }\n </span>\n\n <div class=\"osl-fg-pagination__controls\">\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(1)\" [disabled]=\"currentPage === 1 || loading\">\u00AB</button>\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(currentPage - 1)\" [disabled]=\"currentPage === 1 || loading\">\u2039 Prev</button>\n\n @for (page of pageNumbers; track $index) {\n @if (page === -1) {\n <span class=\"osl-fg-page-ellipsis\">\u2026</span>\n } @else {\n <button\n class=\"osl-fg-page-btn osl-fg-page-btn--num\"\n [class.osl-fg-page-btn--active]=\"page === currentPage\"\n [disabled]=\"loading\"\n (click)=\"goToPage(page)\">\n {{ page }}\n </button>\n }\n }\n\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(currentPage + 1)\" [disabled]=\"currentPage === totalPages || loading\">Next \u203A</button>\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(totalPages)\" [disabled]=\"currentPage === totalPages || loading\">\u00BB</button>\n </div>\n\n <div class=\"osl-fg-pagination__size\">\n <select class=\"osl-fg-page-size\" [ngModel]=\"pageSize\" (ngModelChange)=\"onPageSizeChange($event)\" [disabled]=\"loading\">\n @for (opt of pageSizeOptions; track opt) {\n <option [value]=\"opt\">{{ opt }} per page</option>\n }\n </select>\n </div>\n\n </div>\n }\n\n </div><!-- /.osl-fg-grid-area -->\n @if (enableDragDrop) {\n <div class=\"osl-fg-panel\" [style.width.px]=\"leftPanelWidth\" [style.height]=\"tableHeight\">\n\n <!-- Panel header -->\n <div class=\"osl-fg-panel__header\">\n <span class=\"osl-fg-panel__title\">{{ dragTitle }}</span>\n @if (panelSearchType === 'Api' && panelTotalRecords > 0) {\n <span class=\"osl-fg-panel__count\">{{ draggingList.length }}/{{ panelTotalRecords }}</span>\n } @else if (filteredDraggingList.length > 0) {\n <span class=\"osl-fg-panel__count\">{{ filteredDraggingList.length }}</span>\n }\n </div>\n\n <!-- Search box -->\n @if (showSearch) {\n <div class=\"osl-fg-panel__search\">\n <svg class=\"osl-fg-search-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/>\n </svg>\n <input\n class=\"osl-fg-search-input\"\n type=\"text\"\n [ngModel]=\"dragSearchQuery\"\n (ngModelChange)=\"onPanelSearchChange($event)\"\n placeholder=\"Search\u2026\"\n autocomplete=\"off\" />\n @if (dragSearchQuery) {\n <button class=\"osl-fg-search-clear\" (click)=\"clearPanelSearch()\" title=\"Clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n }\n </div>\n }\n\n <!-- Item list -->\n <div class=\"osl-fg-panel__list\"\n cdkDropList\n #availableList=\"cdkDropList\"\n [cdkDropListData]=\"filteredDraggingList\"\n [cdkDropListConnectedTo]=\"[gridDropZone]\"\n [cdkDropListSortingDisabled]=\"true\"\n (cdkDropListEntered)=\"onDragEnteredEvent($event, 'available')\"\n (cdkDropListExited)=\"onDragExitedEvent($event, 'available')\">\n\n <!-- Loading skeleton -->\n @if (panelLoading && draggingList.length === 0) {\n @for (sk of [1,2,3,4,5]; track sk) {\n <div class=\"osl-fg-item-skeleton\"></div>\n }\n\n } @else if (filteredDraggingList.length === 0) {\n <div class=\"osl-fg-panel__empty\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/>\n </svg>\n <span>{{ dragSearchQuery ? 'No results found' : 'No results available' }}</span>\n </div>\n\n } @else {\n @for (item of filteredDraggingList; track item.id) {\n <div class=\"osl-fg-drag-item\"\n cdkDrag\n [cdkDragData]=\"item\"\n (cdkDragStarted)=\"onDragStartedEvent($event, item, 'available')\"\n (cdkDragEnded)=\"onDragEndedEvent($event, item, 'available')\">\n\n <!-- Grip handle -->\n <span class=\"osl-fg-drag-item__grip\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"9\" cy=\"5\" r=\"1.4\"/><circle cx=\"9\" cy=\"12\" r=\"1.4\"/><circle cx=\"9\" cy=\"19\" r=\"1.4\"/>\n <circle cx=\"15\" cy=\"5\" r=\"1.4\"/><circle cx=\"15\" cy=\"12\" r=\"1.4\"/><circle cx=\"15\" cy=\"19\" r=\"1.4\"/>\n </svg>\n </span>\n\n <!-- Label + description -->\n <div class=\"osl-fg-drag-item__body\">\n <span class=\"osl-fg-drag-item__label\">{{ item.label }}</span>\n @if (item.description) {\n <span class=\"osl-fg-drag-item__desc\">{{ item.description }}</span>\n }\n </div>\n\n <!-- Drag-into-grid hint -->\n <span class=\"osl-fg-drag-item__arrow\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/><polyline points=\"12 5 19 12 12 19\"/>\n </svg>\n </span>\n\n <!-- Placeholder shown in panel while card is dragged -->\n <div *cdkDragPlaceholder class=\"osl-fg-item-placeholder\"></div>\n\n <!-- Preview under the cursor while dragging -->\n <ng-template cdkDragPreview>\n <div class=\"osl-fg-drag-preview\">{{ item.label }}</div>\n </ng-template>\n\n </div>\n }\n\n <!-- Load More (API mode) -->\n @if (panelHasMore) {\n <button class=\"osl-fg-load-more\" (click)=\"loadMorePanelItems()\" [disabled]=\"panelLoading\">\n @if (panelLoading) {\n <span class=\"osl-fg-load-more__spinner\"></span> Loading\u2026\n } @else {\n Load More\n }\n </button>\n }\n\n <!-- Inline loading indicator while appending -->\n @if (panelLoading && draggingList.length > 0) {\n <div class=\"osl-fg-item-skeleton\"></div>\n }\n }\n\n </div>\n </div>\n }\n</div><!-- /.osl-fg-wrapper -->\n", styles: [".osl-fg-wrapper{display:flex;flex-direction:column;width:100%;font-size:var(--osl-text-font-size);font-family:inherit;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 8px);overflow:hidden;background:#fff}.osl-fg-wrapper--builder{flex-direction:row;overflow:visible;align-items:stretch;gap:14px;border:none;background:transparent}.osl-fg-grid-area{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;overflow:hidden;border-radius:inherit;position:relative}.osl-fg-wrapper--builder .osl-fg-grid-area{border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 8px);background:#fff}.osl-fg-table-drop-zone{position:absolute;inset:0;z-index:3;pointer-events:none;border-radius:inherit;transition:background .15s,border-color .15s}.osl-fg-table-drop-zone--active{border:2px dashed var(--osl-primary, #6366f1);background:#6366f108}.osl-fg-table-drop-zone--active.cdk-drop-list-receiving{background:#6366f114;border-style:solid}.osl-fg-drop-zone-badge{position:absolute;bottom:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:5px;padding:5px 13px;background:var(--osl-primary, #6366f1);color:#fff;border-radius:20px;font-size:11px;font-weight:600;white-space:nowrap;pointer-events:none;box-shadow:0 4px 12px #6366f159}.osl-fg-table-container{overflow-x:auto;overflow-y:auto;min-height:120px;flex:1 1 auto}.osl-fg-table-container--drop-active{outline:2px dashed var(--osl-primary, #6366f1);outline-offset:-2px}.osl-fg-table-container--drop-active .osl-fg-empty-inner{color:var(--osl-primary, #6366f1)}.osl-fg-table-container--drop-active .osl-fg-empty-inner svg{opacity:.7}.osl-fg-table{width:100%;border-collapse:collapse;table-layout:auto}.osl-fg-thead{position:sticky;top:0;z-index:2}.osl-fg-th{background:#f3f4f6;color:#4b5563;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.04em;padding:10px 12px;text-align:left;border-bottom:2px solid var(--osl-border-color, #e5e7eb);border-right:1px solid var(--osl-border-color, #e5e7eb);white-space:nowrap;vertical-align:middle}.osl-fg-th:last-child{border-right:none}.osl-fg-th--actions{width:44px;min-width:44px;text-align:center;padding:6px}.osl-fg-th--drag{width:28px;min-width:28px;padding:0}.osl-fg-th-required{color:#ef4444;margin-left:2px;font-size:14px;line-height:1;vertical-align:middle}.osl-fg-row{border-bottom:1px solid #f0f0f0;transition:background .1s}.osl-fg-row:nth-child(odd){background:#f8fafc}.osl-fg-row:nth-child(2n){background:#fff}.osl-fg-row:hover{background:#eef2ff!important}.osl-fg-row:last-child{border-bottom:none}.osl-fg-row--skeleton{pointer-events:none}.osl-fg-td{padding:4px 10px;vertical-align:middle;border-right:1px solid #f0f0f0}.osl-fg-td:last-child{border-right:none}.osl-fg-td--actions{width:44px;min-width:44px;text-align:center;padding:4px 6px}.osl-fg-td--drag{width:28px;min-width:28px;padding:0;text-align:center}.osl-fg-cell-form{min-width:100px}.osl-fg-cell-form ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none!important}.osl-fg-cell-form ::ng-deep .mat-mdc-form-field,.osl-fg-cell-form ::ng-deep mat-form-field{width:100%}.osl-fg-cell-form ::ng-deep .mat-mdc-text-field-wrapper{padding-top:0}.osl-fg-cell-text{color:#111827;font-size:var(--osl-text-font-size)}.osl-grid-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1.5px solid var(--osl-border-color);border-radius:var(--osl-border-radius);background:transparent;color:#6b7280;cursor:pointer;padding:0;transition:border-color .15s,color .15s,background .15s;margin:0 2px}.osl-grid-icon-btn mat-icon{font-size:16px;width:16px;height:16px;line-height:16px}.osl-grid-icon-btn:hover{border-color:var(--osl-primary);color:var(--osl-primary);background:#6366f10f}.osl-grid-icon-btn--danger:hover{border-color:#ef4444;color:#ef4444;background:#ef44440f}.osl-fg-row-handle{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;cursor:grab;color:#d1d5db;border-radius:4px;transition:color .12s,background .12s}.osl-fg-row-handle svg{width:13px;height:13px}.osl-fg-row-handle:hover{color:#9ca3af;background:#f3f4f6}.osl-fg-row-handle:active{cursor:grabbing}.osl-fg-row-handle--disabled{cursor:not-allowed;color:#e5e7eb}.osl-fg-row-handle--disabled:hover{color:#e5e7eb;background:transparent}.osl-fg-row-handle--disabled:active{cursor:not-allowed}.osl-fg-row-placeholder td{padding:4px 6px;background:transparent;border:none}.osl-fg-drop-here{display:flex;align-items:center;justify-content:center;gap:8px;height:46px;border:2px dashed var(--osl-primary, #6366f1);border-radius:6px;background:#eef2ff;color:var(--osl-primary, #6366f1);font-size:12px;font-weight:600;letter-spacing:.03em}.osl-fg-drop-here svg{width:16px;height:16px;flex-shrink:0}.osl-fg-row-preview{display:flex;align-items:center;gap:14px;padding:9px 16px;background:#fff;border:1.5px solid var(--osl-primary, #6366f1);border-radius:8px;box-shadow:0 8px 24px #6366f12e;font-size:12px;color:#374151;max-width:420px;white-space:nowrap;overflow:hidden}.osl-fg-row-preview__cell{overflow:hidden;text-overflow:ellipsis;max-width:130px}.osl-fg-row-preview__cell+.osl-fg-row-preview__cell{padding-left:14px;border-left:1px solid #e5e7eb}.osl-fg-empty{padding:40px 16px;text-align:center}.osl-fg-empty-inner{display:flex;flex-direction:column;align-items:center;gap:10px;color:#9ca3af}.osl-fg-empty-inner svg{opacity:.4}.osl-fg-empty-inner p{margin:0;font-size:13px}@keyframes osl-fg-pulse{0%,to{opacity:1}50%{opacity:.4}}.osl-fg-skeleton{height:36px;border-radius:6px;background:#e5e7eb;animation:osl-fg-pulse 1.4s ease-in-out infinite}.osl-fg-skeleton--sm{height:28px;width:28px;border-radius:4px;margin:0 auto}.osl-fg-tfoot{position:sticky;bottom:0;z-index:2}.osl-fg-footer-td{background:#f3f4f6;color:#4b5563;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.04em;padding:10px 12px;text-align:left;border-top:2px solid var(--osl-border-color, #e5e7eb);border-right:1px solid var(--osl-border-color, #e5e7eb);white-space:nowrap;vertical-align:middle}.osl-fg-footer-td:last-child{border-right:none}.osl-fg-pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 16px;border-top:1px solid var(--osl-border-color, #e5e7eb);background:#f9fafb;flex-shrink:0;flex-wrap:wrap}.osl-fg-pagination__info{font-size:12px;color:#6b7280;white-space:nowrap;min-width:120px}.osl-fg-pagination__controls{display:flex;align-items:center;gap:3px;flex-wrap:nowrap}.osl-fg-pagination__size{display:flex;align-items:center;justify-content:flex-end;min-width:120px}.osl-fg-page-btn{display:inline-flex;align-items:center;justify-content:center;height:30px;padding:0 10px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:13px;font-family:inherit;cursor:pointer;transition:background .12s,border-color .12s,color .12s;white-space:nowrap}.osl-fg-page-btn:hover:not(:disabled){background:#f3f4f6;border-color:#9ca3af}.osl-fg-page-btn:disabled{opacity:.35;cursor:not-allowed}.osl-fg-page-btn--nav{font-size:12px;color:#6b7280}.osl-fg-page-btn--num{min-width:30px;padding:0;font-size:13px}.osl-fg-page-btn--active{background:var(--osl-primary, #6366f1);border-color:var(--osl-primary, #6366f1);color:#fff;font-weight:600}.osl-fg-page-btn--active:hover:not(:disabled){background:var(--osl-primary-hover, #4f46e5);border-color:var(--osl-primary-hover, #4f46e5)}.osl-fg-page-ellipsis{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;color:#9ca3af;font-size:13px;pointer-events:none}.osl-fg-page-size{height:30px;padding:0 8px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:12px;font-family:inherit;cursor:pointer;outline:none}.osl-fg-page-size:focus{border-color:var(--osl-primary, #6366f1)}.osl-fg-page-size:disabled{opacity:.4;cursor:not-allowed}.osl-fg-panel{flex:0 0 auto;display:flex;flex-direction:column;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 8px);background:linear-gradient(180deg,#eceafb,#e2defa);box-shadow:0 2px 6px #0f172a0f,0 8px 20px -6px #6366f129;min-height:200px;overflow:hidden}.osl-fg-panel__header{display:flex;align-items:center;justify-content:space-between;padding:11px 14px 10px;border-bottom:1px solid rgba(99,102,241,.18);background:#6366f11a;flex-shrink:0}.osl-fg-panel__title{font-size:11px;font-weight:700;color:#374151;text-transform:uppercase;letter-spacing:.06em}.osl-fg-panel__count{font-size:11px;font-weight:600;color:#9ca3af;background:#e5e7eb;border-radius:10px;padding:1px 7px;line-height:18px}.osl-fg-panel__search{position:relative;padding:8px 10px;border-bottom:1px solid rgba(99,102,241,.14);flex-shrink:0;background:#ffffff8c}.osl-fg-search-icon{position:absolute;left:20px;top:50%;transform:translateY(-50%);width:13px;height:13px;color:#d1d5db;pointer-events:none}.osl-fg-search-input{width:100%;height:30px;padding:0 26px 0 30px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:6px;background:#f9fafb;font-size:12px;font-family:inherit;color:#374151;outline:none;box-sizing:border-box;transition:border-color .15s,background .15s}.osl-fg-search-input:focus{border-color:var(--osl-primary, #6366f1);background:#fff}.osl-fg-search-input::placeholder{color:#d1d5db}.osl-fg-search-clear{position:absolute;right:16px;top:50%;transform:translateY(-50%);width:18px;height:18px;padding:0;border:none;background:transparent;color:#9ca3af;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:3px}.osl-fg-search-clear svg{width:11px;height:11px}.osl-fg-search-clear:hover{color:#374151}.osl-fg-panel__list{flex:1 1 auto;min-height:0;overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:2px}.osl-fg-panel__empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:28px 12px;color:#c4c9d4;font-size:12px;text-align:center}.osl-fg-panel__empty svg{width:26px;height:26px;opacity:.5}.osl-fg-drag-item{display:flex;align-items:center;gap:9px;padding:8px 10px 8px 8px;border-radius:6px;cursor:grab;-webkit-user-select:none;user-select:none;transition:background .12s;background:transparent;position:relative}.osl-fg-drag-item:hover{background:#f0f4ff}.osl-fg-drag-item:hover .osl-fg-drag-item__grip{opacity:1;background:#6366f12e;color:var(--osl-primary, #6366f1)}.osl-fg-drag-item:hover .osl-fg-drag-item__arrow{opacity:.5}.osl-fg-drag-item:active{cursor:grabbing}.osl-fg-drag-item__grip{flex-shrink:0;width:20px;height:20px;opacity:.85;color:var(--osl-primary, #6366f1);background:#6366f11a;border-radius:4px;transition:opacity .15s,background .15s,color .15s;display:flex;align-items:center;justify-content:center}.osl-fg-drag-item__grip svg{width:13px;height:13px}.osl-fg-drag-item__body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px}.osl-fg-drag-item__label{font-size:13px;font-weight:500;color:#1f2937;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.4}.osl-fg-drag-item__desc{font-size:11px;color:#9ca3af;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.3}.osl-fg-drag-item__arrow{flex-shrink:0;opacity:0;color:var(--osl-primary, #6366f1);transition:opacity .15s}.osl-fg-drag-item__arrow svg{width:13px;height:13px}.osl-fg-item-placeholder{height:36px;border:1.5px dashed #d1d5db;border-radius:6px;background:#f9fafb}.osl-fg-drag-preview{padding:8px 16px;background:var(--osl-primary, #6366f1);color:#fff;border-radius:8px;box-shadow:0 6px 18px #6366f159;font-size:12px;font-weight:600;white-space:nowrap;pointer-events:none}.osl-fg-item-skeleton{height:36px;border-radius:6px;background:#e5e7eb;animation:osl-fg-pulse 1.4s ease-in-out infinite;flex-shrink:0}.osl-fg-load-more{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;height:32px;margin-top:4px;border:1px dashed var(--osl-border-color, #e5e7eb);border-radius:6px;background:transparent;color:#6b7280;font-size:12px;font-family:inherit;cursor:pointer;transition:border-color .15s,color .15s,background .15s;flex-shrink:0}.osl-fg-load-more:hover:not(:disabled){border-color:var(--osl-primary, #6366f1);color:var(--osl-primary, #6366f1);background:#f5f3ff}.osl-fg-load-more:disabled{opacity:.5;cursor:not-allowed}.osl-fg-load-more__spinner{width:12px;height:12px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:osl-fg-spin .7s linear infinite;display:inline-block}@keyframes osl-fg-spin{to{transform:rotate(360deg)}}:host ::ng-deep .cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}:host ::ng-deep .cdk-drop-list-dragging .osl-fg-drag-item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"], dependencies: [{ kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { 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.CdkDragPreview, selector: "ng-template[cdkDragPreview]", inputs: ["data", "matchSize"] }, { kind: "directive", type: i3$1.CdkDragPlaceholder, selector: "ng-template[cdkDragPlaceholder]", inputs: ["data"] }, { kind: "component", type: Oslinput, selector: "osl-input", inputs: ["label", "required", "disabled", "model", "type", "placeholder", "mask", "min", "max", "minLength", "maxLength", "prefixIcon", "suffixIcon", "suffixRef", "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", "displayFn"], outputs: ["datasourceChange", "modelChange", "changeEv"] }, { kind: "component", type: OslFileUpload, selector: "osl-file-upload", inputs: ["label", "required", "disabled", "model", "accept", "multiple", "maxSize", "skeletonLoading", "skeletonTheme", "fileMode", "downloadFn"], 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"] }] });
|
|
4521
4528
|
}
|
|
4522
4529
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslFormGrid, decorators: [{
|
|
4523
4530
|
type: Component,
|
|
4524
|
-
args: [{ selector: 'osl-form-grid', standalone: false, template: "<div class=\"osl-fg-wrapper\" [class.osl-fg-wrapper--builder]=\"enableDragDrop\">\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 Available Controls panel (only in builder mode)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 Main grid area (always present)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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-fg-grid-area\">\n\n <!-- Panel-to-grid drop overlay: always in DOM so #gridDropZone is always resolvable.\n pointer-events: none by default; only activates visually when isDraggingFromPanel.\n CDK hit-tests by bounding rect, not pointer-events, so drops are captured regardless. -->\n <div class=\"osl-fg-table-drop-zone\"\n [class.osl-fg-table-drop-zone--active]=\"isDraggingFromPanel\"\n cdkDropList\n #gridDropZone=\"cdkDropList\"\n [cdkDropListSortingDisabled]=\"true\"\n [cdkDropListDisabled]=\"!enableDragDrop\"\n [cdkDropListData]=\"[]\"\n (cdkDropListDropped)=\"onPanelDrop($event)\">\n @if (isDraggingFromPanel) {\n <div class=\"osl-fg-drop-zone-badge\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n Drop to Add\n </div>\n }\n </div>\n\n <div class=\"osl-fg-table-container\"\n [style.height]=\"tableHeight\"\n [class.osl-fg-table-container--drop-active]=\"isDragging && enableDragDrop\">\n\n <table class=\"osl-fg-table\">\n\n <!-- \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <thead class=\"osl-fg-thead\">\n <tr>\n @if (enableDragDrop && allowReorder) {\n <th class=\"osl-fg-th osl-fg-th--drag\"></th>\n }\n @if (hasActions) {\n <th class=\"osl-fg-th osl-fg-th--actions\">\n @if (canAdd) {\n <button class=\"osl-grid-icon-btn\" (click)=\"addRow()\" [disabled]=\"loading\" title=\"Add row\">\n <mat-icon>add</mat-icon>\n </button>\n }\n </th>\n }\n @for (col of columns; track col.key) {\n <th class=\"osl-fg-th\" [style.width]=\"col.width\">\n {{ col.displayName }}\n @if (colRequired(col)) {\n <span class=\"osl-fg-th-required\">*</span>\n }\n </th>\n }\n </tr>\n </thead>\n\n <!-- \u2500\u2500 Body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <tbody\n cdkDropList\n #gridList=\"cdkDropList\"\n [cdkDropListData]=\"pagedData\"\n [cdkDropListDisabled]=\"!enableDragDrop\"\n (cdkDropListDropped)=\"onGridReorder($event)\">\n\n <!-- Loading state -->\n @if (loading) {\n @for (sk of skeletonRows; track $index) {\n <tr class=\"osl-fg-row osl-fg-row--skeleton\">\n @if (enableDragDrop && allowReorder) {\n <td class=\"osl-fg-td osl-fg-td--drag\"></td>\n }\n @if (hasActions) {\n <td class=\"osl-fg-td osl-fg-td--actions\">\n <div class=\"osl-fg-skeleton osl-fg-skeleton--sm\"></div>\n </td>\n }\n @for (col of columns; track col.key) {\n <td class=\"osl-fg-td\"><div class=\"osl-fg-skeleton\"></div></td>\n }\n </tr>\n }\n\n <!-- Empty state -->\n } @else if (pagedData.length === 0) {\n <tr>\n <td [attr.colspan]=\"columns.length + (hasActions ? 1 : 0) + (enableDragDrop && allowReorder ? 1 : 0)\"\n class=\"osl-fg-empty\">\n <div class=\"osl-fg-empty-inner\">\n @if (enableDragDrop && isDragging) {\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n <p>Drop here</p>\n } @else {\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/><line x1=\"9\" y1=\"9\" x2=\"9\" y2=\"21\"/>\n </svg>\n <p>No rows yet.{{ canAdd ? ' Use + to get started.' : (enableDragDrop ? ' Drag a control here.' : '') }}</p>\n }\n </div>\n </td>\n </tr>\n\n <!-- Data rows -->\n } @else {\n @for (row of pagedData; track $index; let i = $index) {\n <tr class=\"osl-fg-row\"\n cdkDrag\n [cdkDragData]=\"row\"\n [cdkDragDisabled]=\"!enableDragDrop\"\n (cdkDragStarted)=\"onDragStartedEvent($event, row, 'grid')\"\n (cdkDragEnded)=\"onDragEndedEvent($event, row, 'grid')\"\n (click)=\"enableDragDrop ? selectGridItem(row, i) : null\">\n\n <!-- Drag handle -->\n @if (enableDragDrop && allowReorder) {\n <td class=\"osl-fg-td osl-fg-td--drag\">\n <span class=\"osl-fg-row-handle\" cdkDragHandle title=\"Drag to reorder\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"9\" cy=\"5\" r=\"1.3\"/><circle cx=\"9\" cy=\"12\" r=\"1.3\"/><circle cx=\"9\" cy=\"19\" r=\"1.3\"/>\n <circle cx=\"15\" cy=\"5\" r=\"1.3\"/><circle cx=\"15\" cy=\"12\" r=\"1.3\"/><circle cx=\"15\" cy=\"19\" r=\"1.3\"/>\n </svg>\n </span>\n </td>\n }\n\n <!-- Action cell (remove / delete) -->\n @if (hasActions) {\n <td class=\"osl-fg-td osl-fg-td--actions\">\n @if (enableDragDrop && allowRemove) {\n <button class=\"osl-grid-icon-btn osl-grid-icon-btn--danger\"\n (click)=\"removeGridRow(i); $event.stopPropagation()\" title=\"Remove\">\n <mat-icon>close</mat-icon>\n </button>\n } @else if (!enableDragDrop && canDelete) {\n <button class=\"osl-grid-icon-btn osl-grid-icon-btn--danger\"\n (click)=\"deleteRow(i)\" title=\"Delete\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n }\n </td>\n }\n\n <!-- Data cells -->\n @for (col of columns; track col.key) {\n <td class=\"osl-fg-td\" [style.width]=\"col.width\">\n @if (col.formElem) {\n <div class=\"osl-fg-cell-form\" [style.width]=\"col.width\">\n @switch (col.formElem.elementType) {\n\n @case ('textbox') {\n <osl-input\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [type]=\"col.formElem.inputType || 'text'\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [mask]=\"col.formElem.mask || ''\"\n [min]=\"col.formElem.min ?? ''\"\n [max]=\"col.formElem.max ?? ''\"\n [minLength]=\"col.formElem.minLength ?? null\"\n [maxLength]=\"col.formElem.maxLength ?? null\"\n [prefixIcon]=\"col.formElem.prefixIcon || ''\"\n [suffixIcon]=\"col.formElem.suffixIcon || ''\"\n [decimalPortion]=\"col.formElem.decimalPortion ?? null\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-input>\n }\n\n @case ('textarea') {\n <osl-textarea\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [rows]=\"col.formElem.textareaRows || 3\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [maxLength]=\"col.formElem.maxLength ?? null\"\n [minLength]=\"col.formElem.minLength ?? null\"\n [characterCounter]=\"!!col.formElem.characterCounter\"\n [resize]=\"col.formElem.resize || 'none'\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-textarea>\n }\n\n @case ('select') {\n <osl-select\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [datasource]=\"col.formElem.datasource || []\"\n [displayField]=\"col.formElem.displayField || ''\"\n [valueField]=\"col.formElem.valueField || ''\"\n [placeholder]=\"col.formElem.selectPlaceholder || col.displayName\"\n [loading]=\"isLoading(row, col.formElem)\"\n [clearable]=\"!!col.formElem.clearable\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? onSelectChange(col, row, i, $event) : null\">\n </osl-select>\n }\n\n @case ('autocomplete') {\n <osl-autocomplete\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [datasource]=\"col.formElem.datasource || []\"\n [displayField]=\"col.formElem.displayField || ''\"\n [valueField]=\"col.formElem.valueField || ''\"\n [placeholder]=\"col.formElem.autocompletePlaceholder || col.displayName\"\n [loading]=\"isLoading(row, col.formElem)\"\n [searchType]=\"col.formElem.searchType ? col.formElem.searchType : 'Local'\"\n [methodName]=\"col.formElem.apiMethod || ''\"\n [configMethodName]=\"col.formElem.apiConfigMethod || ''\"\n [service]=\"col.formElem.apiService\"\n [object]=\"col.formElem.objectName ? row[col.formElem.objectName] : null\"\n [isLister]=\"!!col.formElem.isListerAutocomplete\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? onSelectChange(col, row, i, $event) : null\">\n </osl-autocomplete>\n }\n\n @case ('radio') {\n <osl-radio\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [datasource]=\"col.formElem.datasource || []\"\n [displayField]=\"col.formElem.displayField || ''\"\n [valueField]=\"col.formElem.valueField || ''\"\n [inline]=\"!!col.formElem.inline\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-radio>\n }\n\n @case ('checkbox') {\n <osl-checkbox\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [indeterminate]=\"!!col.formElem.indeterminate\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-checkbox>\n }\n\n @case ('slide-toggle') {\n <osl-slide-toggle\n [label]=\"''\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [labelPosition]=\"col.formElem.labelPosition || 'after'\"\n [trueLabel]=\"col.formElem.trueLabel || ''\"\n [falseLabel]=\"col.formElem.falseLabel || ''\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-slide-toggle>\n }\n\n @case ('datepicker') {\n <osl-datepicker\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [dateType]=\"col.formElem.dateType || 'date'\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [minDate]=\"col.formElem.minDate || ''\"\n [maxDate]=\"col.formElem.maxDate || ''\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-datepicker>\n }\n\n @case ('datetimepicker') {\n <osl-datetimepicker\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [minDatetime]=\"col.formElem.minDatetime || ''\"\n [maxDatetime]=\"col.formElem.maxDatetime || ''\"\n [format]=\"col.formElem.datetimepickerFormat || 'YYYY-MM-DDTHH:mm'\"\n [showSeconds]=\"!!col.formElem.datetimepickerShowSeconds\"\n [enableMeridian]=\"!!col.formElem.datetimepickerEnableMeridian\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-datetimepicker>\n }\n\n @case ('file-uploader') {\n <osl-file-upload\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [accept]=\"col.formElem.accept || ''\"\n [multiple]=\"!!col.formElem.multiple\"\n [maxSize]=\"col.formElem.maxFileSize || 0\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-file-upload>\n }\n\n @case ('templateRef') {\n <ng-container *ngTemplateOutlet=\"col.formElem.templateRef; context: { $implicit: row, index: i }\"></ng-container>\n }\n\n }\n </div>\n } @else {\n <span class=\"osl-fg-cell-text\">{{ row[col.key] ?? '--' }}</span>\n }\n </td>\n }\n\n <!-- Row drag preview -->\n <ng-template cdkDragPreview>\n <div class=\"osl-fg-row-preview\">\n @for (col of columns; track col.key) {\n <span class=\"osl-fg-row-preview__cell\">{{ row[col.key] ?? '\u2014' }}</span>\n }\n </div>\n </ng-template>\n\n <!-- Drop placeholder: spans full row width with \"Drop Here\" -->\n <ng-template cdkDragPlaceholder>\n <tr class=\"osl-fg-row-placeholder\">\n <td [attr.colspan]=\"columns.length + (hasActions ? 1 : 0) + (enableDragDrop && allowReorder ? 1 : 0)\">\n <div class=\"osl-fg-drop-here\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n Drop Here\n </div>\n </td>\n </tr>\n </ng-template>\n\n </tr>\n }\n }\n\n </tbody>\n\n <!-- \u2500\u2500 Footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (footerColumns && footerColumns.length > 0 && datasource && datasource.length > 0) {\n <tfoot class=\"osl-fg-tfoot\">\n <tr class=\"osl-fg-footer-row\">\n @for (fcol of footerColumns; track $index) {\n <td\n [class]=\"'osl-fg-footer-td ' + (fcol.class || '')\"\n [attr.colspan]=\"fcol.colspan || 1\">\n {{ fcol.displayFn ? fcol.displayFn(datasource) : (fcol.display || '') }}\n </td>\n }\n </tr>\n </tfoot>\n }\n\n </table>\n </div>\n\n <!-- \u2500\u2500 Pagination (unchanged) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (isPaginated) {\n <div class=\"osl-fg-pagination\">\n\n <span class=\"osl-fg-pagination__info\">\n @if (loading) {\n Loading...\n } @else if (_total > 0) {\n {{ startRecord }}\u2013{{ endRecord }} of {{ _total }} rows\n } @else {\n No rows\n }\n </span>\n\n <div class=\"osl-fg-pagination__controls\">\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(1)\" [disabled]=\"currentPage === 1 || loading\">\u00AB</button>\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(currentPage - 1)\" [disabled]=\"currentPage === 1 || loading\">\u2039 Prev</button>\n\n @for (page of pageNumbers; track $index) {\n @if (page === -1) {\n <span class=\"osl-fg-page-ellipsis\">\u2026</span>\n } @else {\n <button\n class=\"osl-fg-page-btn osl-fg-page-btn--num\"\n [class.osl-fg-page-btn--active]=\"page === currentPage\"\n [disabled]=\"loading\"\n (click)=\"goToPage(page)\">\n {{ page }}\n </button>\n }\n }\n\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(currentPage + 1)\" [disabled]=\"currentPage === totalPages || loading\">Next \u203A</button>\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(totalPages)\" [disabled]=\"currentPage === totalPages || loading\">\u00BB</button>\n </div>\n\n <div class=\"osl-fg-pagination__size\">\n <select class=\"osl-fg-page-size\" [ngModel]=\"pageSize\" (ngModelChange)=\"onPageSizeChange($event)\" [disabled]=\"loading\">\n @for (opt of pageSizeOptions; track opt) {\n <option [value]=\"opt\">{{ opt }} per page</option>\n }\n </select>\n </div>\n\n </div>\n }\n\n </div><!-- /.osl-fg-grid-area -->\n @if (enableDragDrop) {\n <div class=\"osl-fg-panel\" [style.width.px]=\"leftPanelWidth\" [style.height]=\"tableHeight\">\n\n <!-- Panel header -->\n <div class=\"osl-fg-panel__header\">\n <span class=\"osl-fg-panel__title\">{{ dragTitle }}</span>\n @if (panelSearchType === 'Api' && panelTotalRecords > 0) {\n <span class=\"osl-fg-panel__count\">{{ draggingList.length }}/{{ panelTotalRecords }}</span>\n } @else if (filteredDraggingList.length > 0) {\n <span class=\"osl-fg-panel__count\">{{ filteredDraggingList.length }}</span>\n }\n </div>\n\n <!-- Search box -->\n @if (showSearch) {\n <div class=\"osl-fg-panel__search\">\n <svg class=\"osl-fg-search-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/>\n </svg>\n <input\n class=\"osl-fg-search-input\"\n type=\"text\"\n [ngModel]=\"dragSearchQuery\"\n (ngModelChange)=\"onPanelSearchChange($event)\"\n placeholder=\"Search\u2026\"\n autocomplete=\"off\" />\n @if (dragSearchQuery) {\n <button class=\"osl-fg-search-clear\" (click)=\"clearPanelSearch()\" title=\"Clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n }\n </div>\n }\n\n <!-- Item list -->\n <div class=\"osl-fg-panel__list\"\n cdkDropList\n #availableList=\"cdkDropList\"\n [cdkDropListData]=\"filteredDraggingList\"\n [cdkDropListConnectedTo]=\"[gridDropZone]\"\n [cdkDropListSortingDisabled]=\"true\"\n (cdkDropListEntered)=\"onDragEnteredEvent($event, 'available')\"\n (cdkDropListExited)=\"onDragExitedEvent($event, 'available')\">\n\n <!-- Loading skeleton -->\n @if (panelLoading && draggingList.length === 0) {\n @for (sk of [1,2,3,4,5]; track sk) {\n <div class=\"osl-fg-item-skeleton\"></div>\n }\n\n } @else if (filteredDraggingList.length === 0) {\n <div class=\"osl-fg-panel__empty\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/>\n </svg>\n <span>{{ dragSearchQuery ? 'No results found' : 'No results available' }}</span>\n </div>\n\n } @else {\n @for (item of filteredDraggingList; track item.id) {\n <div class=\"osl-fg-drag-item\"\n cdkDrag\n [cdkDragData]=\"item\"\n (cdkDragStarted)=\"onDragStartedEvent($event, item, 'available')\"\n (cdkDragEnded)=\"onDragEndedEvent($event, item, 'available')\">\n\n <!-- Grip handle -->\n <span class=\"osl-fg-drag-item__grip\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"9\" cy=\"5\" r=\"1.4\"/><circle cx=\"9\" cy=\"12\" r=\"1.4\"/><circle cx=\"9\" cy=\"19\" r=\"1.4\"/>\n <circle cx=\"15\" cy=\"5\" r=\"1.4\"/><circle cx=\"15\" cy=\"12\" r=\"1.4\"/><circle cx=\"15\" cy=\"19\" r=\"1.4\"/>\n </svg>\n </span>\n\n <!-- Label + description -->\n <div class=\"osl-fg-drag-item__body\">\n <span class=\"osl-fg-drag-item__label\">{{ item.label }}</span>\n @if (item.description) {\n <span class=\"osl-fg-drag-item__desc\">{{ item.description }}</span>\n }\n </div>\n\n <!-- Drag-into-grid hint -->\n <span class=\"osl-fg-drag-item__arrow\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/><polyline points=\"12 5 19 12 12 19\"/>\n </svg>\n </span>\n\n <!-- Placeholder shown in panel while card is dragged -->\n <div *cdkDragPlaceholder class=\"osl-fg-item-placeholder\"></div>\n\n <!-- Preview under the cursor while dragging -->\n <ng-template cdkDragPreview>\n <div class=\"osl-fg-drag-preview\">{{ item.label }}</div>\n </ng-template>\n\n </div>\n }\n\n <!-- Load More (API mode) -->\n @if (panelHasMore) {\n <button class=\"osl-fg-load-more\" (click)=\"loadMorePanelItems()\" [disabled]=\"panelLoading\">\n @if (panelLoading) {\n <span class=\"osl-fg-load-more__spinner\"></span> Loading\u2026\n } @else {\n Load More\n }\n </button>\n }\n\n <!-- Inline loading indicator while appending -->\n @if (panelLoading && draggingList.length > 0) {\n <div class=\"osl-fg-item-skeleton\"></div>\n }\n }\n\n </div>\n </div>\n }\n</div><!-- /.osl-fg-wrapper -->\n", styles: [".osl-fg-wrapper{display:flex;flex-direction:column;width:100%;font-size:var(--osl-text-font-size);font-family:inherit;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 8px);overflow:hidden;background:#fff}.osl-fg-wrapper--builder{flex-direction:row;overflow:visible;align-items:stretch;gap:14px;border:none;background:transparent}.osl-fg-grid-area{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;overflow:hidden;border-radius:inherit;position:relative}.osl-fg-wrapper--builder .osl-fg-grid-area{border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 8px);background:#fff}.osl-fg-table-drop-zone{position:absolute;inset:0;z-index:3;pointer-events:none;border-radius:inherit;transition:background .15s,border-color .15s}.osl-fg-table-drop-zone--active{border:2px dashed var(--osl-primary, #6366f1);background:#6366f108}.osl-fg-table-drop-zone--active.cdk-drop-list-receiving{background:#6366f114;border-style:solid}.osl-fg-drop-zone-badge{position:absolute;bottom:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:5px;padding:5px 13px;background:var(--osl-primary, #6366f1);color:#fff;border-radius:20px;font-size:11px;font-weight:600;white-space:nowrap;pointer-events:none;box-shadow:0 4px 12px #6366f159}.osl-fg-table-container{overflow-x:auto;overflow-y:auto;min-height:120px;flex:1 1 auto}.osl-fg-table-container--drop-active{outline:2px dashed var(--osl-primary, #6366f1);outline-offset:-2px}.osl-fg-table-container--drop-active .osl-fg-empty-inner{color:var(--osl-primary, #6366f1)}.osl-fg-table-container--drop-active .osl-fg-empty-inner svg{opacity:.7}.osl-fg-table{width:100%;border-collapse:collapse;table-layout:auto}.osl-fg-thead{position:sticky;top:0;z-index:2}.osl-fg-th{background:#f3f4f6;color:#4b5563;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.04em;padding:10px 12px;text-align:left;border-bottom:2px solid var(--osl-border-color, #e5e7eb);border-right:1px solid var(--osl-border-color, #e5e7eb);white-space:nowrap;vertical-align:middle}.osl-fg-th:last-child{border-right:none}.osl-fg-th--actions{width:44px;min-width:44px;text-align:center;padding:6px}.osl-fg-th--drag{width:28px;min-width:28px;padding:0}.osl-fg-th-required{color:#ef4444;margin-left:2px;font-size:14px;line-height:1;vertical-align:middle}.osl-fg-row{border-bottom:1px solid #f0f0f0;transition:background .1s}.osl-fg-row:nth-child(odd){background:#f8fafc}.osl-fg-row:nth-child(2n){background:#fff}.osl-fg-row:hover{background:#eef2ff!important}.osl-fg-row:last-child{border-bottom:none}.osl-fg-row--skeleton{pointer-events:none}.osl-fg-td{padding:4px 10px;vertical-align:middle;border-right:1px solid #f0f0f0}.osl-fg-td:last-child{border-right:none}.osl-fg-td--actions{width:44px;min-width:44px;text-align:center;padding:4px 6px}.osl-fg-td--drag{width:28px;min-width:28px;padding:0;text-align:center}.osl-fg-cell-form{min-width:100px}.osl-fg-cell-form ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none!important}.osl-fg-cell-form ::ng-deep .mat-mdc-form-field,.osl-fg-cell-form ::ng-deep mat-form-field{width:100%}.osl-fg-cell-form ::ng-deep .mat-mdc-text-field-wrapper{padding-top:0}.osl-fg-cell-text{color:#111827;font-size:var(--osl-text-font-size)}.osl-grid-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1.5px solid var(--osl-border-color);border-radius:var(--osl-border-radius);background:transparent;color:#6b7280;cursor:pointer;padding:0;transition:border-color .15s,color .15s,background .15s;margin:0 2px}.osl-grid-icon-btn mat-icon{font-size:16px;width:16px;height:16px;line-height:16px}.osl-grid-icon-btn:hover{border-color:var(--osl-primary);color:var(--osl-primary);background:#6366f10f}.osl-grid-icon-btn--danger:hover{border-color:#ef4444;color:#ef4444;background:#ef44440f}.osl-fg-row-handle{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;cursor:grab;color:#d1d5db;border-radius:4px;transition:color .12s,background .12s}.osl-fg-row-handle svg{width:13px;height:13px}.osl-fg-row-handle:hover{color:#9ca3af;background:#f3f4f6}.osl-fg-row-handle:active{cursor:grabbing}.osl-fg-row-placeholder td{padding:4px 6px;background:transparent;border:none}.osl-fg-drop-here{display:flex;align-items:center;justify-content:center;gap:8px;height:46px;border:2px dashed var(--osl-primary, #6366f1);border-radius:6px;background:#eef2ff;color:var(--osl-primary, #6366f1);font-size:12px;font-weight:600;letter-spacing:.03em}.osl-fg-drop-here svg{width:16px;height:16px;flex-shrink:0}.osl-fg-row-preview{display:flex;align-items:center;gap:14px;padding:9px 16px;background:#fff;border:1.5px solid var(--osl-primary, #6366f1);border-radius:8px;box-shadow:0 8px 24px #6366f12e;font-size:12px;color:#374151;max-width:420px;white-space:nowrap;overflow:hidden}.osl-fg-row-preview__cell{overflow:hidden;text-overflow:ellipsis;max-width:130px}.osl-fg-row-preview__cell+.osl-fg-row-preview__cell{padding-left:14px;border-left:1px solid #e5e7eb}.osl-fg-empty{padding:40px 16px;text-align:center}.osl-fg-empty-inner{display:flex;flex-direction:column;align-items:center;gap:10px;color:#9ca3af}.osl-fg-empty-inner svg{opacity:.4}.osl-fg-empty-inner p{margin:0;font-size:13px}@keyframes osl-fg-pulse{0%,to{opacity:1}50%{opacity:.4}}.osl-fg-skeleton{height:36px;border-radius:6px;background:#e5e7eb;animation:osl-fg-pulse 1.4s ease-in-out infinite}.osl-fg-skeleton--sm{height:28px;width:28px;border-radius:4px;margin:0 auto}.osl-fg-tfoot{position:sticky;bottom:0;z-index:2}.osl-fg-footer-td{background:#f3f4f6;color:#4b5563;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.04em;padding:10px 12px;text-align:left;border-top:2px solid var(--osl-border-color, #e5e7eb);border-right:1px solid var(--osl-border-color, #e5e7eb);white-space:nowrap;vertical-align:middle}.osl-fg-footer-td:last-child{border-right:none}.osl-fg-pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 16px;border-top:1px solid var(--osl-border-color, #e5e7eb);background:#f9fafb;flex-shrink:0;flex-wrap:wrap}.osl-fg-pagination__info{font-size:12px;color:#6b7280;white-space:nowrap;min-width:120px}.osl-fg-pagination__controls{display:flex;align-items:center;gap:3px;flex-wrap:nowrap}.osl-fg-pagination__size{display:flex;align-items:center;justify-content:flex-end;min-width:120px}.osl-fg-page-btn{display:inline-flex;align-items:center;justify-content:center;height:30px;padding:0 10px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:13px;font-family:inherit;cursor:pointer;transition:background .12s,border-color .12s,color .12s;white-space:nowrap}.osl-fg-page-btn:hover:not(:disabled){background:#f3f4f6;border-color:#9ca3af}.osl-fg-page-btn:disabled{opacity:.35;cursor:not-allowed}.osl-fg-page-btn--nav{font-size:12px;color:#6b7280}.osl-fg-page-btn--num{min-width:30px;padding:0;font-size:13px}.osl-fg-page-btn--active{background:var(--osl-primary, #6366f1);border-color:var(--osl-primary, #6366f1);color:#fff;font-weight:600}.osl-fg-page-btn--active:hover:not(:disabled){background:var(--osl-primary-hover, #4f46e5);border-color:var(--osl-primary-hover, #4f46e5)}.osl-fg-page-ellipsis{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;color:#9ca3af;font-size:13px;pointer-events:none}.osl-fg-page-size{height:30px;padding:0 8px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:12px;font-family:inherit;cursor:pointer;outline:none}.osl-fg-page-size:focus{border-color:var(--osl-primary, #6366f1)}.osl-fg-page-size:disabled{opacity:.4;cursor:not-allowed}.osl-fg-panel{flex:0 0 auto;display:flex;flex-direction:column;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 8px);background:linear-gradient(180deg,#eceafb,#e2defa);box-shadow:0 2px 6px #0f172a0f,0 8px 20px -6px #6366f129;min-height:200px;overflow:hidden}.osl-fg-panel__header{display:flex;align-items:center;justify-content:space-between;padding:11px 14px 10px;border-bottom:1px solid rgba(99,102,241,.18);background:#6366f11a;flex-shrink:0}.osl-fg-panel__title{font-size:11px;font-weight:700;color:#374151;text-transform:uppercase;letter-spacing:.06em}.osl-fg-panel__count{font-size:11px;font-weight:600;color:#9ca3af;background:#e5e7eb;border-radius:10px;padding:1px 7px;line-height:18px}.osl-fg-panel__search{position:relative;padding:8px 10px;border-bottom:1px solid rgba(99,102,241,.14);flex-shrink:0;background:#ffffff8c}.osl-fg-search-icon{position:absolute;left:20px;top:50%;transform:translateY(-50%);width:13px;height:13px;color:#d1d5db;pointer-events:none}.osl-fg-search-input{width:100%;height:30px;padding:0 26px 0 30px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:6px;background:#f9fafb;font-size:12px;font-family:inherit;color:#374151;outline:none;box-sizing:border-box;transition:border-color .15s,background .15s}.osl-fg-search-input:focus{border-color:var(--osl-primary, #6366f1);background:#fff}.osl-fg-search-input::placeholder{color:#d1d5db}.osl-fg-search-clear{position:absolute;right:16px;top:50%;transform:translateY(-50%);width:18px;height:18px;padding:0;border:none;background:transparent;color:#9ca3af;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:3px}.osl-fg-search-clear svg{width:11px;height:11px}.osl-fg-search-clear:hover{color:#374151}.osl-fg-panel__list{flex:1 1 auto;min-height:0;overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:2px}.osl-fg-panel__empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:28px 12px;color:#c4c9d4;font-size:12px;text-align:center}.osl-fg-panel__empty svg{width:26px;height:26px;opacity:.5}.osl-fg-drag-item{display:flex;align-items:center;gap:9px;padding:8px 10px 8px 8px;border-radius:6px;cursor:grab;-webkit-user-select:none;user-select:none;transition:background .12s;background:transparent;position:relative}.osl-fg-drag-item:hover{background:#f0f4ff}.osl-fg-drag-item:hover .osl-fg-drag-item__grip{opacity:1;background:#6366f12e;color:var(--osl-primary, #6366f1)}.osl-fg-drag-item:hover .osl-fg-drag-item__arrow{opacity:.5}.osl-fg-drag-item:active{cursor:grabbing}.osl-fg-drag-item__grip{flex-shrink:0;width:20px;height:20px;opacity:.85;color:var(--osl-primary, #6366f1);background:#6366f11a;border-radius:4px;transition:opacity .15s,background .15s,color .15s;display:flex;align-items:center;justify-content:center}.osl-fg-drag-item__grip svg{width:13px;height:13px}.osl-fg-drag-item__body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px}.osl-fg-drag-item__label{font-size:13px;font-weight:500;color:#1f2937;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.4}.osl-fg-drag-item__desc{font-size:11px;color:#9ca3af;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.3}.osl-fg-drag-item__arrow{flex-shrink:0;opacity:0;color:var(--osl-primary, #6366f1);transition:opacity .15s}.osl-fg-drag-item__arrow svg{width:13px;height:13px}.osl-fg-item-placeholder{height:36px;border:1.5px dashed #d1d5db;border-radius:6px;background:#f9fafb}.osl-fg-drag-preview{padding:8px 16px;background:var(--osl-primary, #6366f1);color:#fff;border-radius:8px;box-shadow:0 6px 18px #6366f159;font-size:12px;font-weight:600;white-space:nowrap;pointer-events:none}.osl-fg-item-skeleton{height:36px;border-radius:6px;background:#e5e7eb;animation:osl-fg-pulse 1.4s ease-in-out infinite;flex-shrink:0}.osl-fg-load-more{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;height:32px;margin-top:4px;border:1px dashed var(--osl-border-color, #e5e7eb);border-radius:6px;background:transparent;color:#6b7280;font-size:12px;font-family:inherit;cursor:pointer;transition:border-color .15s,color .15s,background .15s;flex-shrink:0}.osl-fg-load-more:hover:not(:disabled){border-color:var(--osl-primary, #6366f1);color:var(--osl-primary, #6366f1);background:#f5f3ff}.osl-fg-load-more:disabled{opacity:.5;cursor:not-allowed}.osl-fg-load-more__spinner{width:12px;height:12px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:osl-fg-spin .7s linear infinite;display:inline-block}@keyframes osl-fg-spin{to{transform:rotate(360deg)}}:host ::ng-deep .cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}:host ::ng-deep .cdk-drop-list-dragging .osl-fg-drag-item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"] }]
|
|
4531
|
+
args: [{ selector: 'osl-form-grid', standalone: false, template: "<div class=\"osl-fg-wrapper\" [class.osl-fg-wrapper--builder]=\"enableDragDrop\">\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 Available Controls panel (only in builder mode)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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 Main grid area (always present)\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\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-fg-grid-area\">\n\n <!-- Panel-to-grid drop overlay: always in DOM so #gridDropZone is always resolvable.\n pointer-events: none by default; only activates visually when isDraggingFromPanel.\n CDK hit-tests by bounding rect, not pointer-events, so drops are captured regardless. -->\n <div class=\"osl-fg-table-drop-zone\"\n [class.osl-fg-table-drop-zone--active]=\"isDraggingFromPanel\"\n cdkDropList\n #gridDropZone=\"cdkDropList\"\n [cdkDropListSortingDisabled]=\"true\"\n [cdkDropListDisabled]=\"!enableDragDrop\"\n [cdkDropListData]=\"[]\"\n (cdkDropListDropped)=\"onPanelDrop($event)\">\n @if (isDraggingFromPanel) {\n <div class=\"osl-fg-drop-zone-badge\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\" width=\"13\" height=\"13\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n Drop to Add\n </div>\n }\n </div>\n\n <div class=\"osl-fg-table-container\"\n [style.height]=\"tableHeight\"\n [class.osl-fg-table-container--drop-active]=\"isDragging && enableDragDrop\">\n\n <table class=\"osl-fg-table\">\n\n <!-- \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <thead class=\"osl-fg-thead\">\n <tr>\n @if (allowReorder) {\n <th class=\"osl-fg-th osl-fg-th--drag\"></th>\n }\n @if (hasActions) {\n <th class=\"osl-fg-th osl-fg-th--actions\">\n @if (canAdd) {\n <button class=\"osl-grid-icon-btn\" (click)=\"addRow()\" [disabled]=\"loading\" title=\"Add row\">\n <mat-icon>add</mat-icon>\n </button>\n }\n </th>\n }\n @for (col of columns; track col.key) {\n <th class=\"osl-fg-th\" [style.width]=\"col.width\">\n {{ col.displayName }}\n @if (colRequired(col)) {\n <span class=\"osl-fg-th-required\">*</span>\n }\n </th>\n }\n </tr>\n </thead>\n\n <!-- \u2500\u2500 Body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <tbody\n cdkDropList\n #gridList=\"cdkDropList\"\n [cdkDropListData]=\"pagedData\"\n [cdkDropListDisabled]=\"!allowReorder\"\n (cdkDropListDropped)=\"onGridReorder($event)\">\n\n <!-- Loading state -->\n @if (loading) {\n @for (sk of skeletonRows; track $index) {\n <tr class=\"osl-fg-row osl-fg-row--skeleton\">\n @if (allowReorder) {\n <td class=\"osl-fg-td osl-fg-td--drag\"></td>\n }\n @if (hasActions) {\n <td class=\"osl-fg-td osl-fg-td--actions\">\n <div class=\"osl-fg-skeleton osl-fg-skeleton--sm\"></div>\n </td>\n }\n @for (col of columns; track col.key) {\n <td class=\"osl-fg-td\"><div class=\"osl-fg-skeleton\"></div></td>\n }\n </tr>\n }\n\n <!-- Empty state -->\n } @else if (pagedData.length === 0) {\n <tr>\n <td [attr.colspan]=\"columns.length + (hasActions ? 1 : 0) + (allowReorder ? 1 : 0)\"\n class=\"osl-fg-empty\">\n <div class=\"osl-fg-empty-inner\">\n @if (enableDragDrop && isDragging) {\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n <p>Drop here</p>\n } @else {\n <svg width=\"36\" height=\"36\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\"/>\n <line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/><line x1=\"9\" y1=\"9\" x2=\"9\" y2=\"21\"/>\n </svg>\n <p>No rows yet.{{ canAdd ? ' Use + to get started.' : (enableDragDrop ? ' Drag a control here.' : '') }}</p>\n }\n </div>\n </td>\n </tr>\n\n <!-- Data rows -->\n } @else {\n @for (row of pagedData; track $index; let i = $index) {\n <tr class=\"osl-fg-row\"\n cdkDrag\n [cdkDragData]=\"row\"\n [cdkDragDisabled]=\"!allowReorder || isReorderDisabled(row)\"\n (cdkDragStarted)=\"onDragStartedEvent($event, row, 'grid')\"\n (cdkDragEnded)=\"onDragEndedEvent($event, row, 'grid')\"\n (click)=\"enableDragDrop ? selectGridItem(row, i) : null\">\n\n <!-- Drag handle -->\n @if (allowReorder) {\n <td class=\"osl-fg-td osl-fg-td--drag\">\n <span class=\"osl-fg-row-handle\"\n [class.osl-fg-row-handle--disabled]=\"isReorderDisabled(row)\"\n cdkDragHandle\n [cdkDragHandleDisabled]=\"isReorderDisabled(row)\"\n [title]=\"isReorderDisabled(row) ? 'Reorder disabled' : 'Drag to reorder'\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"9\" cy=\"5\" r=\"1.3\"/><circle cx=\"9\" cy=\"12\" r=\"1.3\"/><circle cx=\"9\" cy=\"19\" r=\"1.3\"/>\n <circle cx=\"15\" cy=\"5\" r=\"1.3\"/><circle cx=\"15\" cy=\"12\" r=\"1.3\"/><circle cx=\"15\" cy=\"19\" r=\"1.3\"/>\n </svg>\n </span>\n </td>\n }\n\n <!-- Action cell (remove / delete) -->\n @if (hasActions) {\n <td class=\"osl-fg-td osl-fg-td--actions\">\n @if (enableDragDrop && allowRemove) {\n <button class=\"osl-grid-icon-btn osl-grid-icon-btn--danger\"\n (click)=\"removeGridRow(i); $event.stopPropagation()\" title=\"Remove\">\n <mat-icon>close</mat-icon>\n </button>\n } @else if (!enableDragDrop && canDelete) {\n <button class=\"osl-grid-icon-btn osl-grid-icon-btn--danger\"\n (click)=\"deleteRow(i)\" title=\"Delete\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n }\n </td>\n }\n\n <!-- Data cells -->\n @for (col of columns; track col.key) {\n <td class=\"osl-fg-td\" [style.width]=\"col.width\">\n @if (col.formElem) {\n <div class=\"osl-fg-cell-form\" [style.width]=\"col.width\">\n @switch (col.formElem.elementType) {\n\n @case ('textbox') {\n <osl-input\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [type]=\"col.formElem.inputType || 'text'\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [mask]=\"col.formElem.mask || ''\"\n [min]=\"col.formElem.min ?? ''\"\n [max]=\"col.formElem.max ?? ''\"\n [minLength]=\"col.formElem.minLength ?? null\"\n [maxLength]=\"col.formElem.maxLength ?? null\"\n [prefixIcon]=\"col.formElem.prefixIcon || ''\"\n [suffixIcon]=\"col.formElem.suffixIcon || ''\"\n [decimalPortion]=\"col.formElem.decimalPortion ?? null\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-input>\n }\n\n @case ('textarea') {\n <osl-textarea\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [rows]=\"col.formElem.textareaRows || 3\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [maxLength]=\"col.formElem.maxLength ?? null\"\n [minLength]=\"col.formElem.minLength ?? null\"\n [characterCounter]=\"!!col.formElem.characterCounter\"\n [resize]=\"col.formElem.resize || 'none'\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-textarea>\n }\n\n @case ('select') {\n <osl-select\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [datasource]=\"col.formElem.datasource || []\"\n [displayField]=\"col.formElem.displayField || ''\"\n [valueField]=\"col.formElem.valueField || ''\"\n [placeholder]=\"col.formElem.selectPlaceholder || col.displayName\"\n [loading]=\"isLoading(row, col.formElem)\"\n [clearable]=\"!!col.formElem.clearable\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? onSelectChange(col, row, i, $event) : null\">\n </osl-select>\n }\n\n @case ('autocomplete') {\n <osl-autocomplete\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [datasource]=\"col.formElem.datasource || []\"\n [displayField]=\"col.formElem.displayField || ''\"\n [valueField]=\"col.formElem.valueField || ''\"\n [placeholder]=\"col.formElem.autocompletePlaceholder || col.displayName\"\n [loading]=\"isLoading(row, col.formElem)\"\n [searchType]=\"col.formElem.searchType ? col.formElem.searchType : 'Local'\"\n [methodName]=\"col.formElem.apiMethod || ''\"\n [configMethodName]=\"col.formElem.apiConfigMethod || ''\"\n [service]=\"col.formElem.apiService\"\n [object]=\"col.formElem.objectName ? row[col.formElem.objectName] : null\"\n [isLister]=\"!!col.formElem.isListerAutocomplete\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? onSelectChange(col, row, i, $event) : null\">\n </osl-autocomplete>\n }\n\n @case ('radio') {\n <osl-radio\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [datasource]=\"col.formElem.datasource || []\"\n [displayField]=\"col.formElem.displayField || ''\"\n [valueField]=\"col.formElem.valueField || ''\"\n [inline]=\"!!col.formElem.inline\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-radio>\n }\n\n @case ('checkbox') {\n <osl-checkbox\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [indeterminate]=\"!!col.formElem.indeterminate\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-checkbox>\n }\n\n @case ('slide-toggle') {\n <osl-slide-toggle\n [label]=\"''\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [labelPosition]=\"col.formElem.labelPosition || 'after'\"\n [trueLabel]=\"col.formElem.trueLabel || ''\"\n [falseLabel]=\"col.formElem.falseLabel || ''\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-slide-toggle>\n }\n\n @case ('datepicker') {\n <osl-datepicker\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [dateType]=\"col.formElem.dateType || 'date'\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [minDate]=\"col.formElem.minDate || ''\"\n [maxDate]=\"col.formElem.maxDate || ''\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-datepicker>\n }\n\n @case ('datetimepicker') {\n <osl-datetimepicker\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [placeholder]=\"col.formElem.placeholder || col.displayName\"\n [minDatetime]=\"col.formElem.minDatetime || ''\"\n [maxDatetime]=\"col.formElem.maxDatetime || ''\"\n [format]=\"col.formElem.datetimepickerFormat || 'YYYY-MM-DDTHH:mm'\"\n [showSeconds]=\"!!col.formElem.datetimepickerShowSeconds\"\n [enableMeridian]=\"!!col.formElem.datetimepickerEnableMeridian\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-datetimepicker>\n }\n\n @case ('file-uploader') {\n <osl-file-upload\n [label]=\"''\"\n [required]=\"false\"\n [disabled]=\"isDisabled(col.formElem,row,i)\"\n [accept]=\"col.formElem.accept || ''\"\n [multiple]=\"!!col.formElem.multiple\"\n [maxSize]=\"col.formElem.maxFileSize || 0\"\n [model]=\"row[col.key]\"\n (modelChange)=\"onCellChange(row, col, $event)\"\n (changeEv)=\"col.formElem.change ? col.formElem.change(row,i) : null\">\n </osl-file-upload>\n }\n\n @case ('templateRef') {\n <ng-container *ngTemplateOutlet=\"col.formElem.templateRef; context: { $implicit: row, index: i }\"></ng-container>\n }\n\n }\n </div>\n } @else {\n <span class=\"osl-fg-cell-text\">{{ row[col.key] ?? '--' }}</span>\n }\n </td>\n }\n\n <!-- Row drag preview -->\n <ng-template cdkDragPreview>\n <div class=\"osl-fg-row-preview\">\n @for (col of columns; track col.key) {\n <span class=\"osl-fg-row-preview__cell\">{{ row[col.key] ?? '\u2014' }}</span>\n }\n </div>\n </ng-template>\n\n <!-- Drop placeholder: spans full row width with \"Drop Here\" -->\n <ng-template cdkDragPlaceholder>\n <tr class=\"osl-fg-row-placeholder\">\n <td [attr.colspan]=\"columns.length + (hasActions ? 1 : 0) + (allowReorder ? 1 : 0)\">\n <div class=\"osl-fg-drop-here\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path d=\"M12 5v14M5 12l7 7 7-7\"/>\n </svg>\n Drop Here\n </div>\n </td>\n </tr>\n </ng-template>\n\n </tr>\n }\n }\n\n </tbody>\n\n <!-- \u2500\u2500 Footer \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (footerColumns && footerColumns.length > 0 && datasource && datasource.length > 0) {\n <tfoot class=\"osl-fg-tfoot\">\n <tr class=\"osl-fg-footer-row\">\n @for (fcol of footerColumns; track $index) {\n <td\n [class]=\"'osl-fg-footer-td ' + (fcol.class || '')\"\n [attr.colspan]=\"fcol.colspan || 1\">\n {{ fcol.displayFn ? fcol.displayFn(datasource) : (fcol.display || '') }}\n </td>\n }\n </tr>\n </tfoot>\n }\n\n </table>\n </div>\n\n <!-- \u2500\u2500 Pagination (unchanged) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n @if (isPaginated) {\n <div class=\"osl-fg-pagination\">\n\n <span class=\"osl-fg-pagination__info\">\n @if (loading) {\n Loading...\n } @else if (_total > 0) {\n {{ startRecord }}\u2013{{ endRecord }} of {{ _total }} rows\n } @else {\n No rows\n }\n </span>\n\n <div class=\"osl-fg-pagination__controls\">\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(1)\" [disabled]=\"currentPage === 1 || loading\">\u00AB</button>\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(currentPage - 1)\" [disabled]=\"currentPage === 1 || loading\">\u2039 Prev</button>\n\n @for (page of pageNumbers; track $index) {\n @if (page === -1) {\n <span class=\"osl-fg-page-ellipsis\">\u2026</span>\n } @else {\n <button\n class=\"osl-fg-page-btn osl-fg-page-btn--num\"\n [class.osl-fg-page-btn--active]=\"page === currentPage\"\n [disabled]=\"loading\"\n (click)=\"goToPage(page)\">\n {{ page }}\n </button>\n }\n }\n\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(currentPage + 1)\" [disabled]=\"currentPage === totalPages || loading\">Next \u203A</button>\n <button class=\"osl-fg-page-btn osl-fg-page-btn--nav\" (click)=\"goToPage(totalPages)\" [disabled]=\"currentPage === totalPages || loading\">\u00BB</button>\n </div>\n\n <div class=\"osl-fg-pagination__size\">\n <select class=\"osl-fg-page-size\" [ngModel]=\"pageSize\" (ngModelChange)=\"onPageSizeChange($event)\" [disabled]=\"loading\">\n @for (opt of pageSizeOptions; track opt) {\n <option [value]=\"opt\">{{ opt }} per page</option>\n }\n </select>\n </div>\n\n </div>\n }\n\n </div><!-- /.osl-fg-grid-area -->\n @if (enableDragDrop) {\n <div class=\"osl-fg-panel\" [style.width.px]=\"leftPanelWidth\" [style.height]=\"tableHeight\">\n\n <!-- Panel header -->\n <div class=\"osl-fg-panel__header\">\n <span class=\"osl-fg-panel__title\">{{ dragTitle }}</span>\n @if (panelSearchType === 'Api' && panelTotalRecords > 0) {\n <span class=\"osl-fg-panel__count\">{{ draggingList.length }}/{{ panelTotalRecords }}</span>\n } @else if (filteredDraggingList.length > 0) {\n <span class=\"osl-fg-panel__count\">{{ filteredDraggingList.length }}</span>\n }\n </div>\n\n <!-- Search box -->\n @if (showSearch) {\n <div class=\"osl-fg-panel__search\">\n <svg class=\"osl-fg-search-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/>\n </svg>\n <input\n class=\"osl-fg-search-input\"\n type=\"text\"\n [ngModel]=\"dragSearchQuery\"\n (ngModelChange)=\"onPanelSearchChange($event)\"\n placeholder=\"Search\u2026\"\n autocomplete=\"off\" />\n @if (dragSearchQuery) {\n <button class=\"osl-fg-search-clear\" (click)=\"clearPanelSearch()\" title=\"Clear\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"/><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"/>\n </svg>\n </button>\n }\n </div>\n }\n\n <!-- Item list -->\n <div class=\"osl-fg-panel__list\"\n cdkDropList\n #availableList=\"cdkDropList\"\n [cdkDropListData]=\"filteredDraggingList\"\n [cdkDropListConnectedTo]=\"[gridDropZone]\"\n [cdkDropListSortingDisabled]=\"true\"\n (cdkDropListEntered)=\"onDragEnteredEvent($event, 'available')\"\n (cdkDropListExited)=\"onDragExitedEvent($event, 'available')\">\n\n <!-- Loading skeleton -->\n @if (panelLoading && draggingList.length === 0) {\n @for (sk of [1,2,3,4,5]; track sk) {\n <div class=\"osl-fg-item-skeleton\"></div>\n }\n\n } @else if (filteredDraggingList.length === 0) {\n <div class=\"osl-fg-panel__empty\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/>\n </svg>\n <span>{{ dragSearchQuery ? 'No results found' : 'No results available' }}</span>\n </div>\n\n } @else {\n @for (item of filteredDraggingList; track item.id) {\n <div class=\"osl-fg-drag-item\"\n cdkDrag\n [cdkDragData]=\"item\"\n (cdkDragStarted)=\"onDragStartedEvent($event, item, 'available')\"\n (cdkDragEnded)=\"onDragEndedEvent($event, item, 'available')\">\n\n <!-- Grip handle -->\n <span class=\"osl-fg-drag-item__grip\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"9\" cy=\"5\" r=\"1.4\"/><circle cx=\"9\" cy=\"12\" r=\"1.4\"/><circle cx=\"9\" cy=\"19\" r=\"1.4\"/>\n <circle cx=\"15\" cy=\"5\" r=\"1.4\"/><circle cx=\"15\" cy=\"12\" r=\"1.4\"/><circle cx=\"15\" cy=\"19\" r=\"1.4\"/>\n </svg>\n </span>\n\n <!-- Label + description -->\n <div class=\"osl-fg-drag-item__body\">\n <span class=\"osl-fg-drag-item__label\">{{ item.label }}</span>\n @if (item.description) {\n <span class=\"osl-fg-drag-item__desc\">{{ item.description }}</span>\n }\n </div>\n\n <!-- Drag-into-grid hint -->\n <span class=\"osl-fg-drag-item__arrow\">\n <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\"/><polyline points=\"12 5 19 12 12 19\"/>\n </svg>\n </span>\n\n <!-- Placeholder shown in panel while card is dragged -->\n <div *cdkDragPlaceholder class=\"osl-fg-item-placeholder\"></div>\n\n <!-- Preview under the cursor while dragging -->\n <ng-template cdkDragPreview>\n <div class=\"osl-fg-drag-preview\">{{ item.label }}</div>\n </ng-template>\n\n </div>\n }\n\n <!-- Load More (API mode) -->\n @if (panelHasMore) {\n <button class=\"osl-fg-load-more\" (click)=\"loadMorePanelItems()\" [disabled]=\"panelLoading\">\n @if (panelLoading) {\n <span class=\"osl-fg-load-more__spinner\"></span> Loading\u2026\n } @else {\n Load More\n }\n </button>\n }\n\n <!-- Inline loading indicator while appending -->\n @if (panelLoading && draggingList.length > 0) {\n <div class=\"osl-fg-item-skeleton\"></div>\n }\n }\n\n </div>\n </div>\n }\n</div><!-- /.osl-fg-wrapper -->\n", styles: [".osl-fg-wrapper{display:flex;flex-direction:column;width:100%;font-size:var(--osl-text-font-size);font-family:inherit;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 8px);overflow:hidden;background:#fff}.osl-fg-wrapper--builder{flex-direction:row;overflow:visible;align-items:stretch;gap:14px;border:none;background:transparent}.osl-fg-grid-area{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;overflow:hidden;border-radius:inherit;position:relative}.osl-fg-wrapper--builder .osl-fg-grid-area{border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 8px);background:#fff}.osl-fg-table-drop-zone{position:absolute;inset:0;z-index:3;pointer-events:none;border-radius:inherit;transition:background .15s,border-color .15s}.osl-fg-table-drop-zone--active{border:2px dashed var(--osl-primary, #6366f1);background:#6366f108}.osl-fg-table-drop-zone--active.cdk-drop-list-receiving{background:#6366f114;border-style:solid}.osl-fg-drop-zone-badge{position:absolute;bottom:12px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:5px;padding:5px 13px;background:var(--osl-primary, #6366f1);color:#fff;border-radius:20px;font-size:11px;font-weight:600;white-space:nowrap;pointer-events:none;box-shadow:0 4px 12px #6366f159}.osl-fg-table-container{overflow-x:auto;overflow-y:auto;min-height:120px;flex:1 1 auto}.osl-fg-table-container--drop-active{outline:2px dashed var(--osl-primary, #6366f1);outline-offset:-2px}.osl-fg-table-container--drop-active .osl-fg-empty-inner{color:var(--osl-primary, #6366f1)}.osl-fg-table-container--drop-active .osl-fg-empty-inner svg{opacity:.7}.osl-fg-table{width:100%;border-collapse:collapse;table-layout:auto}.osl-fg-thead{position:sticky;top:0;z-index:2}.osl-fg-th{background:#f3f4f6;color:#4b5563;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.04em;padding:10px 12px;text-align:left;border-bottom:2px solid var(--osl-border-color, #e5e7eb);border-right:1px solid var(--osl-border-color, #e5e7eb);white-space:nowrap;vertical-align:middle}.osl-fg-th:last-child{border-right:none}.osl-fg-th--actions{width:44px;min-width:44px;text-align:center;padding:6px}.osl-fg-th--drag{width:28px;min-width:28px;padding:0}.osl-fg-th-required{color:#ef4444;margin-left:2px;font-size:14px;line-height:1;vertical-align:middle}.osl-fg-row{border-bottom:1px solid #f0f0f0;transition:background .1s}.osl-fg-row:nth-child(odd){background:#f8fafc}.osl-fg-row:nth-child(2n){background:#fff}.osl-fg-row:hover{background:#eef2ff!important}.osl-fg-row:last-child{border-bottom:none}.osl-fg-row--skeleton{pointer-events:none}.osl-fg-td{padding:4px 10px;vertical-align:middle;border-right:1px solid #f0f0f0}.osl-fg-td:last-child{border-right:none}.osl-fg-td--actions{width:44px;min-width:44px;text-align:center;padding:4px 6px}.osl-fg-td--drag{width:28px;min-width:28px;padding:0;text-align:center}.osl-fg-cell-form{min-width:100px}.osl-fg-cell-form ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none!important}.osl-fg-cell-form ::ng-deep .mat-mdc-form-field,.osl-fg-cell-form ::ng-deep mat-form-field{width:100%}.osl-fg-cell-form ::ng-deep .mat-mdc-text-field-wrapper{padding-top:0}.osl-fg-cell-text{color:#111827;font-size:var(--osl-text-font-size)}.osl-grid-icon-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:1.5px solid var(--osl-border-color);border-radius:var(--osl-border-radius);background:transparent;color:#6b7280;cursor:pointer;padding:0;transition:border-color .15s,color .15s,background .15s;margin:0 2px}.osl-grid-icon-btn mat-icon{font-size:16px;width:16px;height:16px;line-height:16px}.osl-grid-icon-btn:hover{border-color:var(--osl-primary);color:var(--osl-primary);background:#6366f10f}.osl-grid-icon-btn--danger:hover{border-color:#ef4444;color:#ef4444;background:#ef44440f}.osl-fg-row-handle{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;cursor:grab;color:#d1d5db;border-radius:4px;transition:color .12s,background .12s}.osl-fg-row-handle svg{width:13px;height:13px}.osl-fg-row-handle:hover{color:#9ca3af;background:#f3f4f6}.osl-fg-row-handle:active{cursor:grabbing}.osl-fg-row-handle--disabled{cursor:not-allowed;color:#e5e7eb}.osl-fg-row-handle--disabled:hover{color:#e5e7eb;background:transparent}.osl-fg-row-handle--disabled:active{cursor:not-allowed}.osl-fg-row-placeholder td{padding:4px 6px;background:transparent;border:none}.osl-fg-drop-here{display:flex;align-items:center;justify-content:center;gap:8px;height:46px;border:2px dashed var(--osl-primary, #6366f1);border-radius:6px;background:#eef2ff;color:var(--osl-primary, #6366f1);font-size:12px;font-weight:600;letter-spacing:.03em}.osl-fg-drop-here svg{width:16px;height:16px;flex-shrink:0}.osl-fg-row-preview{display:flex;align-items:center;gap:14px;padding:9px 16px;background:#fff;border:1.5px solid var(--osl-primary, #6366f1);border-radius:8px;box-shadow:0 8px 24px #6366f12e;font-size:12px;color:#374151;max-width:420px;white-space:nowrap;overflow:hidden}.osl-fg-row-preview__cell{overflow:hidden;text-overflow:ellipsis;max-width:130px}.osl-fg-row-preview__cell+.osl-fg-row-preview__cell{padding-left:14px;border-left:1px solid #e5e7eb}.osl-fg-empty{padding:40px 16px;text-align:center}.osl-fg-empty-inner{display:flex;flex-direction:column;align-items:center;gap:10px;color:#9ca3af}.osl-fg-empty-inner svg{opacity:.4}.osl-fg-empty-inner p{margin:0;font-size:13px}@keyframes osl-fg-pulse{0%,to{opacity:1}50%{opacity:.4}}.osl-fg-skeleton{height:36px;border-radius:6px;background:#e5e7eb;animation:osl-fg-pulse 1.4s ease-in-out infinite}.osl-fg-skeleton--sm{height:28px;width:28px;border-radius:4px;margin:0 auto}.osl-fg-tfoot{position:sticky;bottom:0;z-index:2}.osl-fg-footer-td{background:#f3f4f6;color:#4b5563;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.04em;padding:10px 12px;text-align:left;border-top:2px solid var(--osl-border-color, #e5e7eb);border-right:1px solid var(--osl-border-color, #e5e7eb);white-space:nowrap;vertical-align:middle}.osl-fg-footer-td:last-child{border-right:none}.osl-fg-pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 16px;border-top:1px solid var(--osl-border-color, #e5e7eb);background:#f9fafb;flex-shrink:0;flex-wrap:wrap}.osl-fg-pagination__info{font-size:12px;color:#6b7280;white-space:nowrap;min-width:120px}.osl-fg-pagination__controls{display:flex;align-items:center;gap:3px;flex-wrap:nowrap}.osl-fg-pagination__size{display:flex;align-items:center;justify-content:flex-end;min-width:120px}.osl-fg-page-btn{display:inline-flex;align-items:center;justify-content:center;height:30px;padding:0 10px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:13px;font-family:inherit;cursor:pointer;transition:background .12s,border-color .12s,color .12s;white-space:nowrap}.osl-fg-page-btn:hover:not(:disabled){background:#f3f4f6;border-color:#9ca3af}.osl-fg-page-btn:disabled{opacity:.35;cursor:not-allowed}.osl-fg-page-btn--nav{font-size:12px;color:#6b7280}.osl-fg-page-btn--num{min-width:30px;padding:0;font-size:13px}.osl-fg-page-btn--active{background:var(--osl-primary, #6366f1);border-color:var(--osl-primary, #6366f1);color:#fff;font-weight:600}.osl-fg-page-btn--active:hover:not(:disabled){background:var(--osl-primary-hover, #4f46e5);border-color:var(--osl-primary-hover, #4f46e5)}.osl-fg-page-ellipsis{display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;color:#9ca3af;font-size:13px;pointer-events:none}.osl-fg-page-size{height:30px;padding:0 8px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 6px);background:#fff;color:#374151;font-size:12px;font-family:inherit;cursor:pointer;outline:none}.osl-fg-page-size:focus{border-color:var(--osl-primary, #6366f1)}.osl-fg-page-size:disabled{opacity:.4;cursor:not-allowed}.osl-fg-panel{flex:0 0 auto;display:flex;flex-direction:column;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:var(--osl-border-radius, 8px);background:linear-gradient(180deg,#eceafb,#e2defa);box-shadow:0 2px 6px #0f172a0f,0 8px 20px -6px #6366f129;min-height:200px;overflow:hidden}.osl-fg-panel__header{display:flex;align-items:center;justify-content:space-between;padding:11px 14px 10px;border-bottom:1px solid rgba(99,102,241,.18);background:#6366f11a;flex-shrink:0}.osl-fg-panel__title{font-size:11px;font-weight:700;color:#374151;text-transform:uppercase;letter-spacing:.06em}.osl-fg-panel__count{font-size:11px;font-weight:600;color:#9ca3af;background:#e5e7eb;border-radius:10px;padding:1px 7px;line-height:18px}.osl-fg-panel__search{position:relative;padding:8px 10px;border-bottom:1px solid rgba(99,102,241,.14);flex-shrink:0;background:#ffffff8c}.osl-fg-search-icon{position:absolute;left:20px;top:50%;transform:translateY(-50%);width:13px;height:13px;color:#d1d5db;pointer-events:none}.osl-fg-search-input{width:100%;height:30px;padding:0 26px 0 30px;border:1px solid var(--osl-border-color, #e5e7eb);border-radius:6px;background:#f9fafb;font-size:12px;font-family:inherit;color:#374151;outline:none;box-sizing:border-box;transition:border-color .15s,background .15s}.osl-fg-search-input:focus{border-color:var(--osl-primary, #6366f1);background:#fff}.osl-fg-search-input::placeholder{color:#d1d5db}.osl-fg-search-clear{position:absolute;right:16px;top:50%;transform:translateY(-50%);width:18px;height:18px;padding:0;border:none;background:transparent;color:#9ca3af;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:3px}.osl-fg-search-clear svg{width:11px;height:11px}.osl-fg-search-clear:hover{color:#374151}.osl-fg-panel__list{flex:1 1 auto;min-height:0;overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:2px}.osl-fg-panel__empty{display:flex;flex-direction:column;align-items:center;gap:8px;padding:28px 12px;color:#c4c9d4;font-size:12px;text-align:center}.osl-fg-panel__empty svg{width:26px;height:26px;opacity:.5}.osl-fg-drag-item{display:flex;align-items:center;gap:9px;padding:8px 10px 8px 8px;border-radius:6px;cursor:grab;-webkit-user-select:none;user-select:none;transition:background .12s;background:transparent;position:relative}.osl-fg-drag-item:hover{background:#f0f4ff}.osl-fg-drag-item:hover .osl-fg-drag-item__grip{opacity:1;background:#6366f12e;color:var(--osl-primary, #6366f1)}.osl-fg-drag-item:hover .osl-fg-drag-item__arrow{opacity:.5}.osl-fg-drag-item:active{cursor:grabbing}.osl-fg-drag-item__grip{flex-shrink:0;width:20px;height:20px;opacity:.85;color:var(--osl-primary, #6366f1);background:#6366f11a;border-radius:4px;transition:opacity .15s,background .15s,color .15s;display:flex;align-items:center;justify-content:center}.osl-fg-drag-item__grip svg{width:13px;height:13px}.osl-fg-drag-item__body{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;gap:1px}.osl-fg-drag-item__label{font-size:13px;font-weight:500;color:#1f2937;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.4}.osl-fg-drag-item__desc{font-size:11px;color:#9ca3af;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;line-height:1.3}.osl-fg-drag-item__arrow{flex-shrink:0;opacity:0;color:var(--osl-primary, #6366f1);transition:opacity .15s}.osl-fg-drag-item__arrow svg{width:13px;height:13px}.osl-fg-item-placeholder{height:36px;border:1.5px dashed #d1d5db;border-radius:6px;background:#f9fafb}.osl-fg-drag-preview{padding:8px 16px;background:var(--osl-primary, #6366f1);color:#fff;border-radius:8px;box-shadow:0 6px 18px #6366f159;font-size:12px;font-weight:600;white-space:nowrap;pointer-events:none}.osl-fg-item-skeleton{height:36px;border-radius:6px;background:#e5e7eb;animation:osl-fg-pulse 1.4s ease-in-out infinite;flex-shrink:0}.osl-fg-load-more{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;height:32px;margin-top:4px;border:1px dashed var(--osl-border-color, #e5e7eb);border-radius:6px;background:transparent;color:#6b7280;font-size:12px;font-family:inherit;cursor:pointer;transition:border-color .15s,color .15s,background .15s;flex-shrink:0}.osl-fg-load-more:hover:not(:disabled){border-color:var(--osl-primary, #6366f1);color:var(--osl-primary, #6366f1);background:#f5f3ff}.osl-fg-load-more:disabled{opacity:.5;cursor:not-allowed}.osl-fg-load-more__spinner{width:12px;height:12px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:osl-fg-spin .7s linear infinite;display:inline-block}@keyframes osl-fg-spin{to{transform:rotate(360deg)}}:host ::ng-deep .cdk-drag-animating{transition:transform .25s cubic-bezier(0,0,.2,1)}:host ::ng-deep .cdk-drop-list-dragging .osl-fg-drag-item:not(.cdk-drag-placeholder){transition:transform .25s cubic-bezier(0,0,.2,1)}\n"] }]
|
|
4525
4532
|
}], propDecorators: { columns: [{
|
|
4526
4533
|
type: Input,
|
|
4527
4534
|
args: ['columns']
|
|
@@ -4571,6 +4578,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
4571
4578
|
type: Input
|
|
4572
4579
|
}], allowRemove: [{
|
|
4573
4580
|
type: Input
|
|
4581
|
+
}], reorderDisabledIf: [{
|
|
4582
|
+
type: Input,
|
|
4583
|
+
args: ['reorderDisabledIf']
|
|
4574
4584
|
}], panelSearchType: [{
|
|
4575
4585
|
type: Input
|
|
4576
4586
|
}], panelLoading: [{
|
|
@@ -5798,6 +5808,7 @@ class OslReportForm {
|
|
|
5798
5808
|
skeletonTheme = 'light';
|
|
5799
5809
|
pageSize = 50;
|
|
5800
5810
|
pdfConfig = {};
|
|
5811
|
+
moreMenuActions = [];
|
|
5801
5812
|
_grid;
|
|
5802
5813
|
get _mergedPdfConfig() {
|
|
5803
5814
|
return { ...this.pdfConfig, reportName: this.pdfConfig.reportName ?? this.title };
|
|
@@ -5809,6 +5820,27 @@ class OslReportForm {
|
|
|
5809
5820
|
generating = false;
|
|
5810
5821
|
resultDatasource = [];
|
|
5811
5822
|
showResultView = false;
|
|
5823
|
+
// ── Footer more-actions menu state ─────────────────────────────
|
|
5824
|
+
moreMenuOpen = false;
|
|
5825
|
+
moreMenuPosition = { top: 0, left: 0 };
|
|
5826
|
+
onDocumentClick() {
|
|
5827
|
+
this.moreMenuOpen = false;
|
|
5828
|
+
}
|
|
5829
|
+
get hasVisibleMoreMenuActions() {
|
|
5830
|
+
return this.moreMenuActions.some(a => !a.hideIf || !a.hideIf(undefined));
|
|
5831
|
+
}
|
|
5832
|
+
toggleMoreMenu(event) {
|
|
5833
|
+
event.stopPropagation();
|
|
5834
|
+
if (this.moreMenuOpen) {
|
|
5835
|
+
this.moreMenuOpen = false;
|
|
5836
|
+
return;
|
|
5837
|
+
}
|
|
5838
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
5839
|
+
const menuWidth = 196;
|
|
5840
|
+
const left = Math.min(Math.max(rect.right - menuWidth, 8), window.innerWidth - menuWidth - 8);
|
|
5841
|
+
this.moreMenuPosition = { top: rect.bottom + 6, left };
|
|
5842
|
+
this.moreMenuOpen = true;
|
|
5843
|
+
}
|
|
5812
5844
|
ngOnInit() {
|
|
5813
5845
|
if (this.generateOptions.length > 0 && !this.generateOptions.includes(this.selectedType)) {
|
|
5814
5846
|
this.selectedType = this.generateOptions[0];
|
|
@@ -5860,11 +5892,11 @@ class OslReportForm {
|
|
|
5860
5892
|
this.modelChange.emit(val);
|
|
5861
5893
|
}
|
|
5862
5894
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslReportForm, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
5863
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslReportForm, isStandalone: false, selector: "osl-report-form", inputs: { title: "title", formElements: "formElements", model: "model", generateOptions: "generateOptions", reportColumns: "reportColumns", onGenerate: "onGenerate", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme", pageSize: "pageSize", pdfConfig: "pdfConfig" }, outputs: { modelChange: "modelChange" }, viewQueries: [{ propertyName: "_grid", first: true, predicate: OslReportGrid, descendants: true }], ngImport: i0, template: "<!-- \u2500\u2500 Form view (hidden when results are shown) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n@if (!showResultView) {\n <div class=\"orf-card\">\n\n <!-- Header -->\n <div class=\"orf-header\">\n <div class=\"orf-header__left\">\n <div class=\"orf-header__icon-wrap\">\n <svg class=\"orf-header__icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M8 16V11\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n <path d=\"M12 16V8\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n <path d=\"M16 16V13\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n <div class=\"orf-header__text\">\n <h4 class=\"orf-header__title\">{{ title }}</h4>\n <p class=\"orf-header__sub\">Configure parameters and generate report</p>\n </div>\n </div>\n\n <div class=\"orf-header__right\">\n @for (type of generateOptions; track type) {\n <button\n class=\"orf-type-btn\"\n [class.active]=\"selectedType === type\"\n [class.orf-type-btn--pdf]=\"type === 'pdf'\"\n [class.orf-type-btn--excel]=\"type === 'excel'\"\n [class.orf-type-btn--grid]=\"type === 'grid'\"\n (click)=\"selectType(type)\"\n type=\"button\"\n >\n @if (type === 'pdf') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M14 2v6h6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M9 13h1.5a1.5 1.5 0 0 1 0 3H9v-4\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\n <path d=\"M14 13v4\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\n <path d=\"M14 13h1.5a1.5 1.5 0 0 1 0 3H14\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\n </svg>\n }\n @if (type === 'excel') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M2 8h20\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M8 8v13M14 8v13\" stroke=\"currentColor\" stroke-width=\"1.2\"/>\n <path d=\"M2 13h20M2 18h20\" stroke=\"currentColor\" stroke-width=\"1.2\"/>\n </svg>\n }\n @if (type === 'grid') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n </svg>\n }\n <span>{{ typeLabel(type) }}</span>\n </button>\n }\n </div>\n </div>\n\n <!-- Section divider with label -->\n <div class=\"orf-section-divider\">\n <div class=\"orf-section-divider__line\"></div>\n <span class=\"orf-section-divider__label\">\n <svg viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M2 4h12M2 8h9M2 12h6\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\"/>\n </svg>\n Report Parameters\n </span>\n <div class=\"orf-section-divider__line\"></div>\n </div>\n\n <!-- Form Body -->\n <div class=\"orf-body\">\n <osl-dynamic-form\n [elements]=\"formElements\"\n [model]=\"model\"\n (modelChange)=\"onModelChange($event)\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n ></osl-dynamic-form>\n </div>\n\n <!-- Footer -->\n <div class=\"orf-footer\">\n <button\n class=\"orf-generate-btn\"\n (click)=\"generate()\"\n [disabled]=\"generating\"\n type=\"button\"\n >\n @if (generating) {\n <span class=\"orf-generate-btn__spinner\"></span>\n <span>Generating...</span>\n } @else {\n @if (selectedType === 'pdf') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linejoin=\"round\"/>\n <path d=\"M14 2v6h6\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linejoin=\"round\"/>\n </svg>\n }\n @if (selectedType === 'excel') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <path d=\"M2 8h20M8 8v13M14 8v13M2 13h20M2 18h20\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n </svg>\n }\n @if (selectedType === 'grid') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n </svg>\n }\n <span>Generate {{ typeLabel(selectedType) }}</span>\n <svg class=\"orf-generate-btn__arrow\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M4 10h12M11 5l5 5-5 5\" stroke=\"currentColor\" stroke-width=\"1.8\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n }\n </button>\n </div>\n\n </div>\n}\n\n<!-- \u2500\u2500 Full-screen results view \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n@if (showResultView) {\n\n <!-- Results header bar -->\n <div class=\"orf-results-header\">\n\n <div class=\"orf-results-header__left\">\n <button class=\"orf-back-btn\" (click)=\"backToForm()\" type=\"button\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M16 10H4M9 5l-5 5 5 5\" stroke=\"currentColor\" stroke-width=\"1.8\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>Back</span>\n </button>\n <div class=\"orf-results-header__sep\"></div>\n <svg class=\"orf-results-header__report-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M8 16V11M12 16V8M16 16V13\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n </svg>\n <span class=\"orf-results-header__title\">{{ title }}</span>\n <span class=\"orf-results-badge\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 16 16\" fill=\"none\">\n <rect x=\"2\" y=\"2\" width=\"12\" height=\"3\" rx=\"1\" fill=\"currentColor\"/>\n <rect x=\"2\" y=\"7\" width=\"12\" height=\"3\" rx=\"1\" fill=\"currentColor\" opacity=\".65\"/>\n <rect x=\"2\" y=\"12\" width=\"8\" height=\"2\" rx=\"1\" fill=\"currentColor\" opacity=\".35\"/>\n </svg>\n {{ resultDatasource.length | number }} records\n </span>\n </div>\n\n <!-- <div class=\"orf-results-header__right\">\n @if (generateOptions.includes('excel')) {\n <button class=\"orf-reexport-btn orf-reexport-btn--excel\" (click)=\"reExportExcel()\" type=\"button\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M2 8h20M8 8v13M14 8v13M2 13h20M2 18h20\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n </svg>\n <span>Excel</span>\n </button>\n }\n @if (generateOptions.includes('pdf')) {\n <button class=\"orf-reexport-btn orf-reexport-btn--pdf\" (click)=\"reExportPdf()\" type=\"button\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M14 2v6h6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n <span>PDF</span>\n </button>\n }\n </div> -->\n\n </div>\n\n <!-- Report grid -->\n <div class=\"orf-results-body\">\n <osl-report-grid\n [columns]=\"reportColumns\"\n [datasource]=\"resultDatasource\"\n [loading]=\"generating\"\n [title]=\"title\"\n tableHeight=\"calc(100vh - 365px)\"\n [pageSize]=\"pageSize\"\n [exportable]=\"true\"\n [pdfExportFromGrid]=\"true\"\n [pdfConfig]=\"_mergedPdfConfig\"\n [showAggregates]=\"true\"\n [striped]=\"true\"\n ></osl-report-grid>\n </div>\n\n}\n", styles: ["@keyframes orf-fade-in{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes orf-slide-in{0%{opacity:0;transform:translate(-10px)}to{opacity:1;transform:translate(0)}}@keyframes orf-spin{to{transform:rotate(360deg)}}@keyframes orf-shimmer{0%{transform:translate(-100%) skew(-12deg)}to{transform:translate(220%) skew(-12deg)}}@keyframes orf-badge-pop{0%{transform:scale(.7);opacity:0}70%{transform:scale(1.08)}to{transform:scale(1);opacity:1}}.orf-btn-icon{display:block;flex-shrink:0;width:16px;height:16px}.orf-card{background:#fff;border-radius:14px;border:1px solid #e5e7eb;box-shadow:0 4px 24px #00000012,0 1px 4px #0000000a;overflow:hidden;animation:orf-fade-in .28s ease}.orf-header{background:linear-gradient(135deg,#1e3a8a,#2563eb 55%,#60a5fa);padding:22px 24px;display:flex;align-items:center;justify-content:space-between;gap:16px;position:relative;overflow:hidden;flex-wrap:wrap}.orf-header:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,.12) 1px,transparent 1px);background-size:20px 20px;pointer-events:none}.orf-header:after{content:\"\";position:absolute;width:260px;height:260px;border-radius:50%;background:radial-gradient(circle,rgba(255,255,255,.1) 0%,transparent 70%);top:-120px;right:-20px;pointer-events:none}.orf-header__left{display:flex;align-items:center;gap:14px;position:relative;z-index:1}.orf-header__icon-wrap{width:52px;height:52px;border-radius:14px;background:#ffffff24;border:1px solid rgba(255,255,255,.24);display:flex;align-items:center;justify-content:center;flex-shrink:0;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 2px 8px #00000026,inset 0 1px #fff3}.orf-header__icon{width:26px;height:26px;color:#fff}.orf-header__text{display:flex;flex-direction:column;gap:3px}.orf-header__title{margin:0;font-size:19px;font-weight:700;color:#fff;letter-spacing:-.02em;line-height:1.2;text-shadow:0 1px 3px rgba(0,0,0,.15)}.orf-header__sub{margin:0;font-size:12px;color:#ffffffa6;letter-spacing:.01em}.orf-header__right{display:flex;align-items:center;gap:8px;position:relative;z-index:1;flex-wrap:wrap}.orf-type-btn{display:inline-flex;align-items:center;gap:7px;padding:8px 16px;border-radius:9px;border:1px solid rgba(255,255,255,.28);background:#ffffff1c;color:#ffffffd1;font-size:13px;font-weight:500;cursor:pointer;transition:all .2s ease;white-space:nowrap;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:inherit}.orf-type-btn .orf-btn-icon{width:15px;height:15px;transition:transform .2s ease}.orf-type-btn:hover{background:#ffffff36;border-color:#ffffff73;color:#fff;transform:translateY(-1px)}.orf-type-btn:hover .orf-btn-icon{transform:scale(1.1)}.orf-type-btn.active{background:#fffffff5;border-color:transparent;color:#1e3a8a;box-shadow:0 4px 16px #00000038,0 1px 3px #0000001a;transform:translateY(-1px);font-weight:600}.orf-type-btn.active .orf-btn-icon{color:#1e3a8a}.orf-type-btn--pdf.active,.orf-type-btn--pdf.active .orf-btn-icon{color:#dc2626}.orf-type-btn--excel.active,.orf-type-btn--excel.active .orf-btn-icon{color:#16a34a}.orf-type-btn--grid.active,.orf-type-btn--grid.active .orf-btn-icon{color:#2563eb}.orf-section-divider{display:flex;align-items:center;gap:12px;padding:0 24px;margin:18px 0 0}.orf-section-divider__line{flex:1;height:1px;background:linear-gradient(90deg,transparent,#e2e8f0,#c7d2fe,#e2e8f0,transparent)}.orf-section-divider__label{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:20px;background:#f0f4ff;border:1px solid #c7d2fe;color:#4f46e5;font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;flex-shrink:0}.orf-section-divider__label svg{width:12px;height:12px;color:#6366f1}.orf-body{padding:16px 20px 4px}.orf-footer{padding:12px 20px 20px;display:flex;justify-content:flex-end;align-items:center;gap:10px}.orf-generate-btn{display:inline-flex;align-items:center;gap:8px;padding:11px 24px;border-radius:10px;border:none;background:linear-gradient(135deg,#1e3a8a,#2563eb);color:#fff;font-size:14px;font-weight:600;cursor:pointer;transition:all .25s ease;box-shadow:0 4px 18px #2563eb59;letter-spacing:.01em;font-family:inherit;position:relative;overflow:hidden}.orf-generate-btn:after{content:\"\";position:absolute;inset:0;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.18) 50%,transparent 100%);transform:translate(-100%) skew(-12deg);pointer-events:none}.orf-generate-btn .orf-btn-icon{width:18px;height:18px}.orf-generate-btn__arrow{width:18px;height:18px;transition:transform .22s ease;flex-shrink:0}.orf-generate-btn:hover:not([disabled]){background:linear-gradient(135deg,#1e40af,#1d4ed8);box-shadow:0 6px 24px #2563eb80;transform:translateY(-1px)}.orf-generate-btn:hover:not([disabled]):after{animation:orf-shimmer .55s ease forwards}.orf-generate-btn:hover:not([disabled]) .orf-generate-btn__arrow{transform:translate(5px)}.orf-generate-btn:active:not([disabled]){transform:translateY(0);box-shadow:0 2px 8px #2563eb38}.orf-generate-btn[disabled]{opacity:.72;cursor:not-allowed;box-shadow:none}.orf-generate-btn__spinner{width:16px;height:16px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;display:inline-block;animation:orf-spin .7s linear infinite;flex-shrink:0}.orf-results-view{position:fixed;inset:0;z-index:200;display:flex;flex-direction:column;background:#f1f5f9;animation:orf-fade-in .25s ease}.orf-results-header{flex-shrink:0;height:56px;background:linear-gradient(135deg,#1e3a8a,#2563eb 60%,#3b82f6);display:flex;align-items:center;justify-content:space-between;padding:0 16px;gap:12px;position:relative;overflow:hidden;box-shadow:0 2px 14px #2563eb73}.orf-results-header:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,.09) 1px,transparent 1px);background-size:16px 16px;pointer-events:none}.orf-results-header:after{content:\"\";position:absolute;width:200px;height:200px;border-radius:50%;background:radial-gradient(circle,rgba(255,255,255,.08) 0%,transparent 70%);top:-90px;right:40px;pointer-events:none}.orf-results-header__left{display:flex;align-items:center;gap:10px;position:relative;z-index:1;min-width:0;flex:1}.orf-results-header__sep{width:1px;height:22px;background:#ffffff40;flex-shrink:0}.orf-results-header__report-icon{width:20px;height:20px;color:#ffffffd9;flex-shrink:0}.orf-results-header__title{font-size:15px;font-weight:700;color:#fff;letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:0 1px 2px rgba(0,0,0,.12)}.orf-results-badge{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;border-radius:20px;background:#ffffff26;border:1px solid rgba(255,255,255,.22);color:#ffffffeb;font-size:11px;font-weight:600;white-space:nowrap;flex-shrink:0;animation:orf-badge-pop .35s cubic-bezier(.34,1.56,.64,1) both}.orf-results-badge .orf-btn-icon{width:13px;height:13px}.orf-back-btn{display:inline-flex;align-items:center;gap:5px;padding:6px 14px;border-radius:8px;border:1px solid rgba(255,255,255,.3);background:#ffffff21;color:#fff;font-size:13px;font-weight:500;cursor:pointer;transition:all .18s ease;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:inherit;flex-shrink:0;position:relative;z-index:1}.orf-back-btn .orf-btn-icon{width:16px;height:16px;transition:transform .18s ease}.orf-back-btn:hover{background:#ffffff3d;border-color:#ffffff80}.orf-back-btn:hover .orf-btn-icon{transform:translate(-3px)}.orf-back-btn:active{transform:scale(.97)}.orf-results-header__right{display:flex;align-items:center;gap:8px;position:relative;z-index:1;flex-shrink:0}.orf-reexport-btn{display:inline-flex;align-items:center;gap:5px;padding:6px 13px;border-radius:8px;border:1px solid rgba(255,255,255,.28);background:#ffffff1f;color:#ffffffd9;font-size:12px;font-weight:500;cursor:pointer;transition:all .18s ease;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:inherit;white-space:nowrap}.orf-reexport-btn .orf-btn-icon{width:15px;height:15px}.orf-reexport-btn:hover{background:#ffffff38;border-color:#ffffff73;color:#fff;transform:translateY(-1px)}.orf-reexport-btn--excel:hover{background:#16a34a52;border-color:#16a34a8c}.orf-reexport-btn--pdf:hover{background:#dc262652;border-color:#dc26268c}.orf-results-body{flex:1;overflow:hidden;padding:12px 12px 0;display:flex;flex-direction:column}.orf-results-body osl-report-grid{flex:1;display:block}\n"], dependencies: [{ kind: "component", type: DynamicForm, selector: "osl-dynamic-form", inputs: ["elements", "model", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange"] }, { kind: "component", type: OslReportGrid, selector: "osl-report-grid", inputs: ["columns", "datasource", "loading", "totalRecords", "autoMode", "isPaginated", "pageSize", "tableHeight", "striped", "exportable", "rowHeight", "rowSelection", "showAggregates", "title", "pdfExportFromGrid", "pdfConfig"], outputs: ["pageChange", "pageSizeChange", "sortChange", "rowClick", "selectionChange"] }, { kind: "pipe", type: i1$1.DecimalPipe, name: "number" }] });
|
|
5895
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: OslReportForm, isStandalone: false, selector: "osl-report-form", inputs: { title: "title", formElements: "formElements", model: "model", generateOptions: "generateOptions", reportColumns: "reportColumns", onGenerate: "onGenerate", skeletonLoading: "skeletonLoading", skeletonTheme: "skeletonTheme", pageSize: "pageSize", pdfConfig: "pdfConfig", moreMenuActions: "moreMenuActions" }, outputs: { modelChange: "modelChange" }, host: { listeners: { "document:click": "onDocumentClick()" } }, viewQueries: [{ propertyName: "_grid", first: true, predicate: OslReportGrid, descendants: true }], ngImport: i0, template: "<!-- \u2500\u2500 Form view (hidden when results are shown) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n@if (!showResultView) {\n <div class=\"orf-card\">\n\n <!-- Header -->\n <div class=\"orf-header\">\n <div class=\"orf-header__left\">\n <div class=\"orf-header__icon-wrap\">\n <svg class=\"orf-header__icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M8 16V11\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n <path d=\"M12 16V8\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n <path d=\"M16 16V13\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n <div class=\"orf-header__text\">\n <h4 class=\"orf-header__title\">{{ title }}</h4>\n <p class=\"orf-header__sub\">Configure parameters and generate report</p>\n </div>\n </div>\n\n <div class=\"orf-header__right\">\n @for (type of generateOptions; track type) {\n <button\n class=\"orf-type-btn\"\n [class.active]=\"selectedType === type\"\n [class.orf-type-btn--pdf]=\"type === 'pdf'\"\n [class.orf-type-btn--excel]=\"type === 'excel'\"\n [class.orf-type-btn--grid]=\"type === 'grid'\"\n (click)=\"selectType(type)\"\n type=\"button\"\n >\n @if (type === 'pdf') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M14 2v6h6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M9 13h1.5a1.5 1.5 0 0 1 0 3H9v-4\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\n <path d=\"M14 13v4\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\n <path d=\"M14 13h1.5a1.5 1.5 0 0 1 0 3H14\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\n </svg>\n }\n @if (type === 'excel') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M2 8h20\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M8 8v13M14 8v13\" stroke=\"currentColor\" stroke-width=\"1.2\"/>\n <path d=\"M2 13h20M2 18h20\" stroke=\"currentColor\" stroke-width=\"1.2\"/>\n </svg>\n }\n @if (type === 'grid') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n </svg>\n }\n <span>{{ typeLabel(type) }}</span>\n </button>\n }\n </div>\n </div>\n\n <!-- Section divider with label -->\n <div class=\"orf-section-divider\">\n <div class=\"orf-section-divider__line\"></div>\n <span class=\"orf-section-divider__label\">\n <svg viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M2 4h12M2 8h9M2 12h6\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\"/>\n </svg>\n Report Parameters\n </span>\n <div class=\"orf-section-divider__line\"></div>\n </div>\n\n <!-- Form Body -->\n <div class=\"orf-body\">\n <osl-dynamic-form\n [elements]=\"formElements\"\n [model]=\"model\"\n (modelChange)=\"onModelChange($event)\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n ></osl-dynamic-form>\n </div>\n\n <!-- Footer -->\n <div class=\"orf-footer\">\n @if (moreMenuActions.length > 0 && hasVisibleMoreMenuActions) {\n <button class=\"orf-more-btn\" (click)=\"toggleMoreMenu($event)\" type=\"button\" title=\"More actions\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"12\" cy=\"5\" r=\"2.2\"/>\n <circle cx=\"12\" cy=\"12\" r=\"2.2\"/>\n <circle cx=\"12\" cy=\"19\" r=\"2.2\"/>\n </svg>\n </button>\n }\n <button\n class=\"orf-generate-btn\"\n (click)=\"generate()\"\n [disabled]=\"generating\"\n type=\"button\"\n >\n @if (generating) {\n <span class=\"orf-generate-btn__spinner\"></span>\n <span>Generating...</span>\n } @else {\n @if (selectedType === 'pdf') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linejoin=\"round\"/>\n <path d=\"M14 2v6h6\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linejoin=\"round\"/>\n </svg>\n }\n @if (selectedType === 'excel') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <path d=\"M2 8h20M8 8v13M14 8v13M2 13h20M2 18h20\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n </svg>\n }\n @if (selectedType === 'grid') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n </svg>\n }\n <span>Generate {{ typeLabel(selectedType) }}</span>\n <svg class=\"orf-generate-btn__arrow\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M4 10h12M11 5l5 5-5 5\" stroke=\"currentColor\" stroke-width=\"1.8\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n }\n </button>\n </div>\n\n </div>\n}\n\n<!-- \u2500\u2500 Full-screen results view \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n@if (showResultView) {\n\n <!-- Results header bar -->\n <div class=\"orf-results-header\">\n\n <div class=\"orf-results-header__left\">\n <button class=\"orf-back-btn\" (click)=\"backToForm()\" type=\"button\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M16 10H4M9 5l-5 5 5 5\" stroke=\"currentColor\" stroke-width=\"1.8\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>Back</span>\n </button>\n <div class=\"orf-results-header__sep\"></div>\n <svg class=\"orf-results-header__report-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M8 16V11M12 16V8M16 16V13\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n </svg>\n <span class=\"orf-results-header__title\">{{ title }}</span>\n <span class=\"orf-results-badge\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 16 16\" fill=\"none\">\n <rect x=\"2\" y=\"2\" width=\"12\" height=\"3\" rx=\"1\" fill=\"currentColor\"/>\n <rect x=\"2\" y=\"7\" width=\"12\" height=\"3\" rx=\"1\" fill=\"currentColor\" opacity=\".65\"/>\n <rect x=\"2\" y=\"12\" width=\"8\" height=\"2\" rx=\"1\" fill=\"currentColor\" opacity=\".35\"/>\n </svg>\n {{ resultDatasource.length | number }} records\n </span>\n </div>\n\n <!-- <div class=\"orf-results-header__right\">\n @if (generateOptions.includes('excel')) {\n <button class=\"orf-reexport-btn orf-reexport-btn--excel\" (click)=\"reExportExcel()\" type=\"button\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M2 8h20M8 8v13M14 8v13M2 13h20M2 18h20\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n </svg>\n <span>Excel</span>\n </button>\n }\n @if (generateOptions.includes('pdf')) {\n <button class=\"orf-reexport-btn orf-reexport-btn--pdf\" (click)=\"reExportPdf()\" type=\"button\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M14 2v6h6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n <span>PDF</span>\n </button>\n }\n </div> -->\n\n </div>\n\n <!-- Report grid -->\n <div class=\"orf-results-body\">\n <osl-report-grid\n [columns]=\"reportColumns\"\n [datasource]=\"resultDatasource\"\n [loading]=\"generating\"\n [title]=\"title\"\n tableHeight=\"calc(100vh - 365px)\"\n [pageSize]=\"pageSize\"\n [exportable]=\"true\"\n [pdfExportFromGrid]=\"true\"\n [pdfConfig]=\"_mergedPdfConfig\"\n [showAggregates]=\"true\"\n [striped]=\"true\"\n ></osl-report-grid>\n </div>\n\n}\n\n<!-- Floating footer more-actions menu -->\n@if (moreMenuOpen && moreMenuActions.length > 0) {\n <div class=\"orf-more-menu\"\n [style.top.px]=\"moreMenuPosition.top\"\n [style.left.px]=\"moreMenuPosition.left\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"orf-more-menu__header\">Actions</div>\n @for (action of moreMenuActions; track $index) {\n @if (!action.hideIf || !action.hideIf(undefined)) {\n <button class=\"orf-more-menu__item\" (click)=\"action.click(undefined); moreMenuOpen = false\">\n <span class=\"orf-more-menu__dot\"></span>\n {{ action.labelIf ? action.labelIf(undefined) : action.label }}\n </button>\n }\n }\n </div>\n}\n", styles: ["@keyframes orf-fade-in{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes orf-slide-in{0%{opacity:0;transform:translate(-10px)}to{opacity:1;transform:translate(0)}}@keyframes orf-spin{to{transform:rotate(360deg)}}@keyframes orf-shimmer{0%{transform:translate(-100%) skew(-12deg)}to{transform:translate(220%) skew(-12deg)}}@keyframes orf-badge-pop{0%{transform:scale(.7);opacity:0}70%{transform:scale(1.08)}to{transform:scale(1);opacity:1}}.orf-btn-icon{display:block;flex-shrink:0;width:16px;height:16px}.orf-card{background:#fff;border-radius:14px;border:1px solid #e5e7eb;box-shadow:0 4px 24px #00000012,0 1px 4px #0000000a;overflow:hidden;animation:orf-fade-in .28s ease}.orf-header{background:linear-gradient(135deg,#1e3a8a,#2563eb 55%,#60a5fa);padding:22px 24px;display:flex;align-items:center;justify-content:space-between;gap:16px;position:relative;overflow:hidden;flex-wrap:wrap}.orf-header:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,.12) 1px,transparent 1px);background-size:20px 20px;pointer-events:none}.orf-header:after{content:\"\";position:absolute;width:260px;height:260px;border-radius:50%;background:radial-gradient(circle,rgba(255,255,255,.1) 0%,transparent 70%);top:-120px;right:-20px;pointer-events:none}.orf-header__left{display:flex;align-items:center;gap:14px;position:relative;z-index:1}.orf-header__icon-wrap{width:52px;height:52px;border-radius:14px;background:#ffffff24;border:1px solid rgba(255,255,255,.24);display:flex;align-items:center;justify-content:center;flex-shrink:0;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 2px 8px #00000026,inset 0 1px #fff3}.orf-header__icon{width:26px;height:26px;color:#fff}.orf-header__text{display:flex;flex-direction:column;gap:3px}.orf-header__title{margin:0;font-size:19px;font-weight:700;color:#fff;letter-spacing:-.02em;line-height:1.2;text-shadow:0 1px 3px rgba(0,0,0,.15)}.orf-header__sub{margin:0;font-size:12px;color:#ffffffa6;letter-spacing:.01em}.orf-header__right{display:flex;align-items:center;gap:8px;position:relative;z-index:1;flex-wrap:wrap}.orf-type-btn{display:inline-flex;align-items:center;gap:7px;padding:8px 16px;border-radius:9px;border:1px solid rgba(255,255,255,.28);background:#ffffff1c;color:#ffffffd1;font-size:13px;font-weight:500;cursor:pointer;transition:all .2s ease;white-space:nowrap;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:inherit}.orf-type-btn .orf-btn-icon{width:15px;height:15px;transition:transform .2s ease}.orf-type-btn:hover{background:#ffffff36;border-color:#ffffff73;color:#fff;transform:translateY(-1px)}.orf-type-btn:hover .orf-btn-icon{transform:scale(1.1)}.orf-type-btn.active{background:#fffffff5;border-color:transparent;color:#1e3a8a;box-shadow:0 4px 16px #00000038,0 1px 3px #0000001a;transform:translateY(-1px);font-weight:600}.orf-type-btn.active .orf-btn-icon{color:#1e3a8a}.orf-type-btn--pdf.active,.orf-type-btn--pdf.active .orf-btn-icon{color:#dc2626}.orf-type-btn--excel.active,.orf-type-btn--excel.active .orf-btn-icon{color:#16a34a}.orf-type-btn--grid.active,.orf-type-btn--grid.active .orf-btn-icon{color:#2563eb}.orf-section-divider{display:flex;align-items:center;gap:12px;padding:0 24px;margin:18px 0 0}.orf-section-divider__line{flex:1;height:1px;background:linear-gradient(90deg,transparent,#e2e8f0,#c7d2fe,#e2e8f0,transparent)}.orf-section-divider__label{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:20px;background:#f0f4ff;border:1px solid #c7d2fe;color:#4f46e5;font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;flex-shrink:0}.orf-section-divider__label svg{width:12px;height:12px;color:#6366f1}.orf-body{padding:16px 20px 4px}.orf-footer{padding:12px 20px 20px;display:flex;justify-content:flex-end;align-items:center;gap:10px}.orf-generate-btn{display:inline-flex;align-items:center;gap:8px;padding:11px 24px;border-radius:10px;border:none;background:linear-gradient(135deg,#1e3a8a,#2563eb);color:#fff;font-size:14px;font-weight:600;cursor:pointer;transition:all .25s ease;box-shadow:0 4px 18px #2563eb59;letter-spacing:.01em;font-family:inherit;position:relative;overflow:hidden}.orf-generate-btn:after{content:\"\";position:absolute;inset:0;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.18) 50%,transparent 100%);transform:translate(-100%) skew(-12deg);pointer-events:none}.orf-generate-btn .orf-btn-icon{width:18px;height:18px}.orf-generate-btn__arrow{width:18px;height:18px;transition:transform .22s ease;flex-shrink:0}.orf-generate-btn:hover:not([disabled]){background:linear-gradient(135deg,#1e40af,#1d4ed8);box-shadow:0 6px 24px #2563eb80;transform:translateY(-1px)}.orf-generate-btn:hover:not([disabled]):after{animation:orf-shimmer .55s ease forwards}.orf-generate-btn:hover:not([disabled]) .orf-generate-btn__arrow{transform:translate(5px)}.orf-generate-btn:active:not([disabled]){transform:translateY(0);box-shadow:0 2px 8px #2563eb38}.orf-generate-btn[disabled]{opacity:.72;cursor:not-allowed;box-shadow:none}.orf-generate-btn__spinner{width:16px;height:16px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;display:inline-block;animation:orf-spin .7s linear infinite;flex-shrink:0}.orf-more-btn{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:10px;border:1.5px solid #e5e7eb;background:#fff;cursor:pointer;transition:background .15s,border-color .15s,transform .15s,box-shadow .15s;padding:0;flex-shrink:0}.orf-more-btn svg{width:15px;height:15px;fill:#9ca3af;transition:fill .15s}.orf-more-btn:hover{background:#f5f3ff;border-color:#6366f1;box-shadow:0 2px 8px #0000001a;transform:translateY(-1px)}.orf-more-btn:hover svg{fill:#6366f1}.orf-more-menu{position:fixed;z-index:9999;min-width:196px;background:#fff;border:1px solid #eaecf0;border-radius:12px;box-shadow:0 12px 32px #10182824,0 4px 12px #1018280f;overflow:hidden;animation:orf-menu-pop .16s cubic-bezier(.16,1,.3,1)}.orf-more-menu__header{padding:10px 14px 7px;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#9ca3af;border-bottom:1px solid #f2f4f7;-webkit-user-select:none;user-select:none}.orf-more-menu__item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 14px;background:transparent;border:none;border-left:3px solid transparent;border-bottom:1px solid #f9fafb;text-align:left;font-size:13px;font-weight:500;font-family:inherit;color:#374151;cursor:pointer;white-space:nowrap;transition:background .12s,color .12s,border-left-color .12s}.orf-more-menu__item:last-child{border-bottom:none}.orf-more-menu__item:hover{background:#f5f3ff;color:#6366f1;border-left-color:#6366f1}.orf-more-menu__item:hover .orf-more-menu__dot{background:#6366f1;box-shadow:0 0 0 3px #6366f129}.orf-more-menu__dot{flex-shrink:0;width:6px;height:6px;border-radius:50%;background:#d1d5db;transition:background .12s,box-shadow .12s}@keyframes orf-menu-pop{0%{opacity:0;transform:translateY(-8px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.orf-results-view{position:fixed;inset:0;z-index:200;display:flex;flex-direction:column;background:#f1f5f9;animation:orf-fade-in .25s ease}.orf-results-header{flex-shrink:0;height:56px;background:linear-gradient(135deg,#1e3a8a,#2563eb 60%,#3b82f6);display:flex;align-items:center;justify-content:space-between;padding:0 16px;gap:12px;position:relative;overflow:hidden;box-shadow:0 2px 14px #2563eb73}.orf-results-header:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,.09) 1px,transparent 1px);background-size:16px 16px;pointer-events:none}.orf-results-header:after{content:\"\";position:absolute;width:200px;height:200px;border-radius:50%;background:radial-gradient(circle,rgba(255,255,255,.08) 0%,transparent 70%);top:-90px;right:40px;pointer-events:none}.orf-results-header__left{display:flex;align-items:center;gap:10px;position:relative;z-index:1;min-width:0;flex:1}.orf-results-header__sep{width:1px;height:22px;background:#ffffff40;flex-shrink:0}.orf-results-header__report-icon{width:20px;height:20px;color:#ffffffd9;flex-shrink:0}.orf-results-header__title{font-size:15px;font-weight:700;color:#fff;letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:0 1px 2px rgba(0,0,0,.12)}.orf-results-badge{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;border-radius:20px;background:#ffffff26;border:1px solid rgba(255,255,255,.22);color:#ffffffeb;font-size:11px;font-weight:600;white-space:nowrap;flex-shrink:0;animation:orf-badge-pop .35s cubic-bezier(.34,1.56,.64,1) both}.orf-results-badge .orf-btn-icon{width:13px;height:13px}.orf-back-btn{display:inline-flex;align-items:center;gap:5px;padding:6px 14px;border-radius:8px;border:1px solid rgba(255,255,255,.3);background:#ffffff21;color:#fff;font-size:13px;font-weight:500;cursor:pointer;transition:all .18s ease;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:inherit;flex-shrink:0;position:relative;z-index:1}.orf-back-btn .orf-btn-icon{width:16px;height:16px;transition:transform .18s ease}.orf-back-btn:hover{background:#ffffff3d;border-color:#ffffff80}.orf-back-btn:hover .orf-btn-icon{transform:translate(-3px)}.orf-back-btn:active{transform:scale(.97)}.orf-results-header__right{display:flex;align-items:center;gap:8px;position:relative;z-index:1;flex-shrink:0}.orf-reexport-btn{display:inline-flex;align-items:center;gap:5px;padding:6px 13px;border-radius:8px;border:1px solid rgba(255,255,255,.28);background:#ffffff1f;color:#ffffffd9;font-size:12px;font-weight:500;cursor:pointer;transition:all .18s ease;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:inherit;white-space:nowrap}.orf-reexport-btn .orf-btn-icon{width:15px;height:15px}.orf-reexport-btn:hover{background:#ffffff38;border-color:#ffffff73;color:#fff;transform:translateY(-1px)}.orf-reexport-btn--excel:hover{background:#16a34a52;border-color:#16a34a8c}.orf-reexport-btn--pdf:hover{background:#dc262652;border-color:#dc26268c}.orf-results-body{flex:1;overflow:hidden;padding:12px 12px 0;display:flex;flex-direction:column}.orf-results-body osl-report-grid{flex:1;display:block}\n"], dependencies: [{ kind: "component", type: DynamicForm, selector: "osl-dynamic-form", inputs: ["elements", "model", "skeletonLoading", "skeletonTheme"], outputs: ["modelChange"] }, { kind: "component", type: OslReportGrid, selector: "osl-report-grid", inputs: ["columns", "datasource", "loading", "totalRecords", "autoMode", "isPaginated", "pageSize", "tableHeight", "striped", "exportable", "rowHeight", "rowSelection", "showAggregates", "title", "pdfExportFromGrid", "pdfConfig"], outputs: ["pageChange", "pageSizeChange", "sortChange", "rowClick", "selectionChange"] }, { kind: "pipe", type: i1$1.DecimalPipe, name: "number" }] });
|
|
5864
5896
|
}
|
|
5865
5897
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: OslReportForm, decorators: [{
|
|
5866
5898
|
type: Component,
|
|
5867
|
-
args: [{ selector: 'osl-report-form', standalone: false, template: "<!-- \u2500\u2500 Form view (hidden when results are shown) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n@if (!showResultView) {\n <div class=\"orf-card\">\n\n <!-- Header -->\n <div class=\"orf-header\">\n <div class=\"orf-header__left\">\n <div class=\"orf-header__icon-wrap\">\n <svg class=\"orf-header__icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M8 16V11\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n <path d=\"M12 16V8\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n <path d=\"M16 16V13\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n <div class=\"orf-header__text\">\n <h4 class=\"orf-header__title\">{{ title }}</h4>\n <p class=\"orf-header__sub\">Configure parameters and generate report</p>\n </div>\n </div>\n\n <div class=\"orf-header__right\">\n @for (type of generateOptions; track type) {\n <button\n class=\"orf-type-btn\"\n [class.active]=\"selectedType === type\"\n [class.orf-type-btn--pdf]=\"type === 'pdf'\"\n [class.orf-type-btn--excel]=\"type === 'excel'\"\n [class.orf-type-btn--grid]=\"type === 'grid'\"\n (click)=\"selectType(type)\"\n type=\"button\"\n >\n @if (type === 'pdf') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M14 2v6h6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M9 13h1.5a1.5 1.5 0 0 1 0 3H9v-4\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\n <path d=\"M14 13v4\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\n <path d=\"M14 13h1.5a1.5 1.5 0 0 1 0 3H14\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\n </svg>\n }\n @if (type === 'excel') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M2 8h20\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M8 8v13M14 8v13\" stroke=\"currentColor\" stroke-width=\"1.2\"/>\n <path d=\"M2 13h20M2 18h20\" stroke=\"currentColor\" stroke-width=\"1.2\"/>\n </svg>\n }\n @if (type === 'grid') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n </svg>\n }\n <span>{{ typeLabel(type) }}</span>\n </button>\n }\n </div>\n </div>\n\n <!-- Section divider with label -->\n <div class=\"orf-section-divider\">\n <div class=\"orf-section-divider__line\"></div>\n <span class=\"orf-section-divider__label\">\n <svg viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M2 4h12M2 8h9M2 12h6\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\"/>\n </svg>\n Report Parameters\n </span>\n <div class=\"orf-section-divider__line\"></div>\n </div>\n\n <!-- Form Body -->\n <div class=\"orf-body\">\n <osl-dynamic-form\n [elements]=\"formElements\"\n [model]=\"model\"\n (modelChange)=\"onModelChange($event)\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n ></osl-dynamic-form>\n </div>\n\n <!-- Footer -->\n <div class=\"orf-footer\">\n <button\n class=\"orf-generate-btn\"\n (click)=\"generate()\"\n [disabled]=\"generating\"\n type=\"button\"\n >\n @if (generating) {\n <span class=\"orf-generate-btn__spinner\"></span>\n <span>Generating...</span>\n } @else {\n @if (selectedType === 'pdf') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linejoin=\"round\"/>\n <path d=\"M14 2v6h6\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linejoin=\"round\"/>\n </svg>\n }\n @if (selectedType === 'excel') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <path d=\"M2 8h20M8 8v13M14 8v13M2 13h20M2 18h20\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n </svg>\n }\n @if (selectedType === 'grid') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n </svg>\n }\n <span>Generate {{ typeLabel(selectedType) }}</span>\n <svg class=\"orf-generate-btn__arrow\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M4 10h12M11 5l5 5-5 5\" stroke=\"currentColor\" stroke-width=\"1.8\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n }\n </button>\n </div>\n\n </div>\n}\n\n<!-- \u2500\u2500 Full-screen results view \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n@if (showResultView) {\n\n <!-- Results header bar -->\n <div class=\"orf-results-header\">\n\n <div class=\"orf-results-header__left\">\n <button class=\"orf-back-btn\" (click)=\"backToForm()\" type=\"button\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M16 10H4M9 5l-5 5 5 5\" stroke=\"currentColor\" stroke-width=\"1.8\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>Back</span>\n </button>\n <div class=\"orf-results-header__sep\"></div>\n <svg class=\"orf-results-header__report-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M8 16V11M12 16V8M16 16V13\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n </svg>\n <span class=\"orf-results-header__title\">{{ title }}</span>\n <span class=\"orf-results-badge\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 16 16\" fill=\"none\">\n <rect x=\"2\" y=\"2\" width=\"12\" height=\"3\" rx=\"1\" fill=\"currentColor\"/>\n <rect x=\"2\" y=\"7\" width=\"12\" height=\"3\" rx=\"1\" fill=\"currentColor\" opacity=\".65\"/>\n <rect x=\"2\" y=\"12\" width=\"8\" height=\"2\" rx=\"1\" fill=\"currentColor\" opacity=\".35\"/>\n </svg>\n {{ resultDatasource.length | number }} records\n </span>\n </div>\n\n <!-- <div class=\"orf-results-header__right\">\n @if (generateOptions.includes('excel')) {\n <button class=\"orf-reexport-btn orf-reexport-btn--excel\" (click)=\"reExportExcel()\" type=\"button\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M2 8h20M8 8v13M14 8v13M2 13h20M2 18h20\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n </svg>\n <span>Excel</span>\n </button>\n }\n @if (generateOptions.includes('pdf')) {\n <button class=\"orf-reexport-btn orf-reexport-btn--pdf\" (click)=\"reExportPdf()\" type=\"button\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M14 2v6h6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n <span>PDF</span>\n </button>\n }\n </div> -->\n\n </div>\n\n <!-- Report grid -->\n <div class=\"orf-results-body\">\n <osl-report-grid\n [columns]=\"reportColumns\"\n [datasource]=\"resultDatasource\"\n [loading]=\"generating\"\n [title]=\"title\"\n tableHeight=\"calc(100vh - 365px)\"\n [pageSize]=\"pageSize\"\n [exportable]=\"true\"\n [pdfExportFromGrid]=\"true\"\n [pdfConfig]=\"_mergedPdfConfig\"\n [showAggregates]=\"true\"\n [striped]=\"true\"\n ></osl-report-grid>\n </div>\n\n}\n", styles: ["@keyframes orf-fade-in{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes orf-slide-in{0%{opacity:0;transform:translate(-10px)}to{opacity:1;transform:translate(0)}}@keyframes orf-spin{to{transform:rotate(360deg)}}@keyframes orf-shimmer{0%{transform:translate(-100%) skew(-12deg)}to{transform:translate(220%) skew(-12deg)}}@keyframes orf-badge-pop{0%{transform:scale(.7);opacity:0}70%{transform:scale(1.08)}to{transform:scale(1);opacity:1}}.orf-btn-icon{display:block;flex-shrink:0;width:16px;height:16px}.orf-card{background:#fff;border-radius:14px;border:1px solid #e5e7eb;box-shadow:0 4px 24px #00000012,0 1px 4px #0000000a;overflow:hidden;animation:orf-fade-in .28s ease}.orf-header{background:linear-gradient(135deg,#1e3a8a,#2563eb 55%,#60a5fa);padding:22px 24px;display:flex;align-items:center;justify-content:space-between;gap:16px;position:relative;overflow:hidden;flex-wrap:wrap}.orf-header:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,.12) 1px,transparent 1px);background-size:20px 20px;pointer-events:none}.orf-header:after{content:\"\";position:absolute;width:260px;height:260px;border-radius:50%;background:radial-gradient(circle,rgba(255,255,255,.1) 0%,transparent 70%);top:-120px;right:-20px;pointer-events:none}.orf-header__left{display:flex;align-items:center;gap:14px;position:relative;z-index:1}.orf-header__icon-wrap{width:52px;height:52px;border-radius:14px;background:#ffffff24;border:1px solid rgba(255,255,255,.24);display:flex;align-items:center;justify-content:center;flex-shrink:0;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 2px 8px #00000026,inset 0 1px #fff3}.orf-header__icon{width:26px;height:26px;color:#fff}.orf-header__text{display:flex;flex-direction:column;gap:3px}.orf-header__title{margin:0;font-size:19px;font-weight:700;color:#fff;letter-spacing:-.02em;line-height:1.2;text-shadow:0 1px 3px rgba(0,0,0,.15)}.orf-header__sub{margin:0;font-size:12px;color:#ffffffa6;letter-spacing:.01em}.orf-header__right{display:flex;align-items:center;gap:8px;position:relative;z-index:1;flex-wrap:wrap}.orf-type-btn{display:inline-flex;align-items:center;gap:7px;padding:8px 16px;border-radius:9px;border:1px solid rgba(255,255,255,.28);background:#ffffff1c;color:#ffffffd1;font-size:13px;font-weight:500;cursor:pointer;transition:all .2s ease;white-space:nowrap;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:inherit}.orf-type-btn .orf-btn-icon{width:15px;height:15px;transition:transform .2s ease}.orf-type-btn:hover{background:#ffffff36;border-color:#ffffff73;color:#fff;transform:translateY(-1px)}.orf-type-btn:hover .orf-btn-icon{transform:scale(1.1)}.orf-type-btn.active{background:#fffffff5;border-color:transparent;color:#1e3a8a;box-shadow:0 4px 16px #00000038,0 1px 3px #0000001a;transform:translateY(-1px);font-weight:600}.orf-type-btn.active .orf-btn-icon{color:#1e3a8a}.orf-type-btn--pdf.active,.orf-type-btn--pdf.active .orf-btn-icon{color:#dc2626}.orf-type-btn--excel.active,.orf-type-btn--excel.active .orf-btn-icon{color:#16a34a}.orf-type-btn--grid.active,.orf-type-btn--grid.active .orf-btn-icon{color:#2563eb}.orf-section-divider{display:flex;align-items:center;gap:12px;padding:0 24px;margin:18px 0 0}.orf-section-divider__line{flex:1;height:1px;background:linear-gradient(90deg,transparent,#e2e8f0,#c7d2fe,#e2e8f0,transparent)}.orf-section-divider__label{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:20px;background:#f0f4ff;border:1px solid #c7d2fe;color:#4f46e5;font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;flex-shrink:0}.orf-section-divider__label svg{width:12px;height:12px;color:#6366f1}.orf-body{padding:16px 20px 4px}.orf-footer{padding:12px 20px 20px;display:flex;justify-content:flex-end;align-items:center;gap:10px}.orf-generate-btn{display:inline-flex;align-items:center;gap:8px;padding:11px 24px;border-radius:10px;border:none;background:linear-gradient(135deg,#1e3a8a,#2563eb);color:#fff;font-size:14px;font-weight:600;cursor:pointer;transition:all .25s ease;box-shadow:0 4px 18px #2563eb59;letter-spacing:.01em;font-family:inherit;position:relative;overflow:hidden}.orf-generate-btn:after{content:\"\";position:absolute;inset:0;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.18) 50%,transparent 100%);transform:translate(-100%) skew(-12deg);pointer-events:none}.orf-generate-btn .orf-btn-icon{width:18px;height:18px}.orf-generate-btn__arrow{width:18px;height:18px;transition:transform .22s ease;flex-shrink:0}.orf-generate-btn:hover:not([disabled]){background:linear-gradient(135deg,#1e40af,#1d4ed8);box-shadow:0 6px 24px #2563eb80;transform:translateY(-1px)}.orf-generate-btn:hover:not([disabled]):after{animation:orf-shimmer .55s ease forwards}.orf-generate-btn:hover:not([disabled]) .orf-generate-btn__arrow{transform:translate(5px)}.orf-generate-btn:active:not([disabled]){transform:translateY(0);box-shadow:0 2px 8px #2563eb38}.orf-generate-btn[disabled]{opacity:.72;cursor:not-allowed;box-shadow:none}.orf-generate-btn__spinner{width:16px;height:16px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;display:inline-block;animation:orf-spin .7s linear infinite;flex-shrink:0}.orf-results-view{position:fixed;inset:0;z-index:200;display:flex;flex-direction:column;background:#f1f5f9;animation:orf-fade-in .25s ease}.orf-results-header{flex-shrink:0;height:56px;background:linear-gradient(135deg,#1e3a8a,#2563eb 60%,#3b82f6);display:flex;align-items:center;justify-content:space-between;padding:0 16px;gap:12px;position:relative;overflow:hidden;box-shadow:0 2px 14px #2563eb73}.orf-results-header:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,.09) 1px,transparent 1px);background-size:16px 16px;pointer-events:none}.orf-results-header:after{content:\"\";position:absolute;width:200px;height:200px;border-radius:50%;background:radial-gradient(circle,rgba(255,255,255,.08) 0%,transparent 70%);top:-90px;right:40px;pointer-events:none}.orf-results-header__left{display:flex;align-items:center;gap:10px;position:relative;z-index:1;min-width:0;flex:1}.orf-results-header__sep{width:1px;height:22px;background:#ffffff40;flex-shrink:0}.orf-results-header__report-icon{width:20px;height:20px;color:#ffffffd9;flex-shrink:0}.orf-results-header__title{font-size:15px;font-weight:700;color:#fff;letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:0 1px 2px rgba(0,0,0,.12)}.orf-results-badge{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;border-radius:20px;background:#ffffff26;border:1px solid rgba(255,255,255,.22);color:#ffffffeb;font-size:11px;font-weight:600;white-space:nowrap;flex-shrink:0;animation:orf-badge-pop .35s cubic-bezier(.34,1.56,.64,1) both}.orf-results-badge .orf-btn-icon{width:13px;height:13px}.orf-back-btn{display:inline-flex;align-items:center;gap:5px;padding:6px 14px;border-radius:8px;border:1px solid rgba(255,255,255,.3);background:#ffffff21;color:#fff;font-size:13px;font-weight:500;cursor:pointer;transition:all .18s ease;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:inherit;flex-shrink:0;position:relative;z-index:1}.orf-back-btn .orf-btn-icon{width:16px;height:16px;transition:transform .18s ease}.orf-back-btn:hover{background:#ffffff3d;border-color:#ffffff80}.orf-back-btn:hover .orf-btn-icon{transform:translate(-3px)}.orf-back-btn:active{transform:scale(.97)}.orf-results-header__right{display:flex;align-items:center;gap:8px;position:relative;z-index:1;flex-shrink:0}.orf-reexport-btn{display:inline-flex;align-items:center;gap:5px;padding:6px 13px;border-radius:8px;border:1px solid rgba(255,255,255,.28);background:#ffffff1f;color:#ffffffd9;font-size:12px;font-weight:500;cursor:pointer;transition:all .18s ease;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:inherit;white-space:nowrap}.orf-reexport-btn .orf-btn-icon{width:15px;height:15px}.orf-reexport-btn:hover{background:#ffffff38;border-color:#ffffff73;color:#fff;transform:translateY(-1px)}.orf-reexport-btn--excel:hover{background:#16a34a52;border-color:#16a34a8c}.orf-reexport-btn--pdf:hover{background:#dc262652;border-color:#dc26268c}.orf-results-body{flex:1;overflow:hidden;padding:12px 12px 0;display:flex;flex-direction:column}.orf-results-body osl-report-grid{flex:1;display:block}\n"] }]
|
|
5899
|
+
args: [{ selector: 'osl-report-form', standalone: false, template: "<!-- \u2500\u2500 Form view (hidden when results are shown) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n@if (!showResultView) {\n <div class=\"orf-card\">\n\n <!-- Header -->\n <div class=\"orf-header\">\n <div class=\"orf-header__left\">\n <div class=\"orf-header__icon-wrap\">\n <svg class=\"orf-header__icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M8 16V11\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n <path d=\"M12 16V8\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n <path d=\"M16 16V13\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n <div class=\"orf-header__text\">\n <h4 class=\"orf-header__title\">{{ title }}</h4>\n <p class=\"orf-header__sub\">Configure parameters and generate report</p>\n </div>\n </div>\n\n <div class=\"orf-header__right\">\n @for (type of generateOptions; track type) {\n <button\n class=\"orf-type-btn\"\n [class.active]=\"selectedType === type\"\n [class.orf-type-btn--pdf]=\"type === 'pdf'\"\n [class.orf-type-btn--excel]=\"type === 'excel'\"\n [class.orf-type-btn--grid]=\"type === 'grid'\"\n (click)=\"selectType(type)\"\n type=\"button\"\n >\n @if (type === 'pdf') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M14 2v6h6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M9 13h1.5a1.5 1.5 0 0 1 0 3H9v-4\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\n <path d=\"M14 13v4\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\n <path d=\"M14 13h1.5a1.5 1.5 0 0 1 0 3H14\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\"/>\n </svg>\n }\n @if (type === 'excel') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M2 8h20\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M8 8v13M14 8v13\" stroke=\"currentColor\" stroke-width=\"1.2\"/>\n <path d=\"M2 13h20M2 18h20\" stroke=\"currentColor\" stroke-width=\"1.2\"/>\n </svg>\n }\n @if (type === 'grid') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n </svg>\n }\n <span>{{ typeLabel(type) }}</span>\n </button>\n }\n </div>\n </div>\n\n <!-- Section divider with label -->\n <div class=\"orf-section-divider\">\n <div class=\"orf-section-divider__line\"></div>\n <span class=\"orf-section-divider__label\">\n <svg viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M2 4h12M2 8h9M2 12h6\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\"/>\n </svg>\n Report Parameters\n </span>\n <div class=\"orf-section-divider__line\"></div>\n </div>\n\n <!-- Form Body -->\n <div class=\"orf-body\">\n <osl-dynamic-form\n [elements]=\"formElements\"\n [model]=\"model\"\n (modelChange)=\"onModelChange($event)\"\n [skeletonLoading]=\"skeletonLoading\"\n [skeletonTheme]=\"skeletonTheme\"\n ></osl-dynamic-form>\n </div>\n\n <!-- Footer -->\n <div class=\"orf-footer\">\n @if (moreMenuActions.length > 0 && hasVisibleMoreMenuActions) {\n <button class=\"orf-more-btn\" (click)=\"toggleMoreMenu($event)\" type=\"button\" title=\"More actions\">\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <circle cx=\"12\" cy=\"5\" r=\"2.2\"/>\n <circle cx=\"12\" cy=\"12\" r=\"2.2\"/>\n <circle cx=\"12\" cy=\"19\" r=\"2.2\"/>\n </svg>\n </button>\n }\n <button\n class=\"orf-generate-btn\"\n (click)=\"generate()\"\n [disabled]=\"generating\"\n type=\"button\"\n >\n @if (generating) {\n <span class=\"orf-generate-btn__spinner\"></span>\n <span>Generating...</span>\n } @else {\n @if (selectedType === 'pdf') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"\n stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linejoin=\"round\"/>\n <path d=\"M14 2v6h6\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linejoin=\"round\"/>\n </svg>\n }\n @if (selectedType === 'excel') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <path d=\"M2 8h20M8 8v13M14 8v13M2 13h20M2 18h20\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n </svg>\n }\n @if (selectedType === 'grid') {\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <rect x=\"14\" y=\"3\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <rect x=\"3\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n <rect x=\"14\" y=\"14\" width=\"7\" height=\"7\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.8\"/>\n </svg>\n }\n <span>Generate {{ typeLabel(selectedType) }}</span>\n <svg class=\"orf-generate-btn__arrow\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M4 10h12M11 5l5 5-5 5\" stroke=\"currentColor\" stroke-width=\"1.8\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n }\n </button>\n </div>\n\n </div>\n}\n\n<!-- \u2500\u2500 Full-screen results view \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n@if (showResultView) {\n\n <!-- Results header bar -->\n <div class=\"orf-results-header\">\n\n <div class=\"orf-results-header__left\">\n <button class=\"orf-back-btn\" (click)=\"backToForm()\" type=\"button\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 20 20\" fill=\"none\">\n <path d=\"M16 10H4M9 5l-5 5 5 5\" stroke=\"currentColor\" stroke-width=\"1.8\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>Back</span>\n </button>\n <div class=\"orf-results-header__sep\"></div>\n <svg class=\"orf-results-header__report-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"3\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M8 16V11M12 16V8M16 16V13\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>\n </svg>\n <span class=\"orf-results-header__title\">{{ title }}</span>\n <span class=\"orf-results-badge\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 16 16\" fill=\"none\">\n <rect x=\"2\" y=\"2\" width=\"12\" height=\"3\" rx=\"1\" fill=\"currentColor\"/>\n <rect x=\"2\" y=\"7\" width=\"12\" height=\"3\" rx=\"1\" fill=\"currentColor\" opacity=\".65\"/>\n <rect x=\"2\" y=\"12\" width=\"8\" height=\"2\" rx=\"1\" fill=\"currentColor\" opacity=\".35\"/>\n </svg>\n {{ resultDatasource.length | number }} records\n </span>\n </div>\n\n <!-- <div class=\"orf-results-header__right\">\n @if (generateOptions.includes('excel')) {\n <button class=\"orf-reexport-btn orf-reexport-btn--excel\" (click)=\"reExportExcel()\" type=\"button\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"18\" rx=\"2\" stroke=\"currentColor\" stroke-width=\"1.5\"/>\n <path d=\"M2 8h20M8 8v13M14 8v13M2 13h20M2 18h20\" stroke=\"currentColor\" stroke-width=\"1.3\"/>\n </svg>\n <span>Excel</span>\n </button>\n }\n @if (generateOptions.includes('pdf')) {\n <button class=\"orf-reexport-btn orf-reexport-btn--pdf\" (click)=\"reExportPdf()\" type=\"button\">\n <svg class=\"orf-btn-icon\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\"\n stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n <path d=\"M14 2v6h6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/>\n </svg>\n <span>PDF</span>\n </button>\n }\n </div> -->\n\n </div>\n\n <!-- Report grid -->\n <div class=\"orf-results-body\">\n <osl-report-grid\n [columns]=\"reportColumns\"\n [datasource]=\"resultDatasource\"\n [loading]=\"generating\"\n [title]=\"title\"\n tableHeight=\"calc(100vh - 365px)\"\n [pageSize]=\"pageSize\"\n [exportable]=\"true\"\n [pdfExportFromGrid]=\"true\"\n [pdfConfig]=\"_mergedPdfConfig\"\n [showAggregates]=\"true\"\n [striped]=\"true\"\n ></osl-report-grid>\n </div>\n\n}\n\n<!-- Floating footer more-actions menu -->\n@if (moreMenuOpen && moreMenuActions.length > 0) {\n <div class=\"orf-more-menu\"\n [style.top.px]=\"moreMenuPosition.top\"\n [style.left.px]=\"moreMenuPosition.left\"\n (click)=\"$event.stopPropagation()\">\n <div class=\"orf-more-menu__header\">Actions</div>\n @for (action of moreMenuActions; track $index) {\n @if (!action.hideIf || !action.hideIf(undefined)) {\n <button class=\"orf-more-menu__item\" (click)=\"action.click(undefined); moreMenuOpen = false\">\n <span class=\"orf-more-menu__dot\"></span>\n {{ action.labelIf ? action.labelIf(undefined) : action.label }}\n </button>\n }\n }\n </div>\n}\n", styles: ["@keyframes orf-fade-in{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes orf-slide-in{0%{opacity:0;transform:translate(-10px)}to{opacity:1;transform:translate(0)}}@keyframes orf-spin{to{transform:rotate(360deg)}}@keyframes orf-shimmer{0%{transform:translate(-100%) skew(-12deg)}to{transform:translate(220%) skew(-12deg)}}@keyframes orf-badge-pop{0%{transform:scale(.7);opacity:0}70%{transform:scale(1.08)}to{transform:scale(1);opacity:1}}.orf-btn-icon{display:block;flex-shrink:0;width:16px;height:16px}.orf-card{background:#fff;border-radius:14px;border:1px solid #e5e7eb;box-shadow:0 4px 24px #00000012,0 1px 4px #0000000a;overflow:hidden;animation:orf-fade-in .28s ease}.orf-header{background:linear-gradient(135deg,#1e3a8a,#2563eb 55%,#60a5fa);padding:22px 24px;display:flex;align-items:center;justify-content:space-between;gap:16px;position:relative;overflow:hidden;flex-wrap:wrap}.orf-header:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,.12) 1px,transparent 1px);background-size:20px 20px;pointer-events:none}.orf-header:after{content:\"\";position:absolute;width:260px;height:260px;border-radius:50%;background:radial-gradient(circle,rgba(255,255,255,.1) 0%,transparent 70%);top:-120px;right:-20px;pointer-events:none}.orf-header__left{display:flex;align-items:center;gap:14px;position:relative;z-index:1}.orf-header__icon-wrap{width:52px;height:52px;border-radius:14px;background:#ffffff24;border:1px solid rgba(255,255,255,.24);display:flex;align-items:center;justify-content:center;flex-shrink:0;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);box-shadow:0 2px 8px #00000026,inset 0 1px #fff3}.orf-header__icon{width:26px;height:26px;color:#fff}.orf-header__text{display:flex;flex-direction:column;gap:3px}.orf-header__title{margin:0;font-size:19px;font-weight:700;color:#fff;letter-spacing:-.02em;line-height:1.2;text-shadow:0 1px 3px rgba(0,0,0,.15)}.orf-header__sub{margin:0;font-size:12px;color:#ffffffa6;letter-spacing:.01em}.orf-header__right{display:flex;align-items:center;gap:8px;position:relative;z-index:1;flex-wrap:wrap}.orf-type-btn{display:inline-flex;align-items:center;gap:7px;padding:8px 16px;border-radius:9px;border:1px solid rgba(255,255,255,.28);background:#ffffff1c;color:#ffffffd1;font-size:13px;font-weight:500;cursor:pointer;transition:all .2s ease;white-space:nowrap;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:inherit}.orf-type-btn .orf-btn-icon{width:15px;height:15px;transition:transform .2s ease}.orf-type-btn:hover{background:#ffffff36;border-color:#ffffff73;color:#fff;transform:translateY(-1px)}.orf-type-btn:hover .orf-btn-icon{transform:scale(1.1)}.orf-type-btn.active{background:#fffffff5;border-color:transparent;color:#1e3a8a;box-shadow:0 4px 16px #00000038,0 1px 3px #0000001a;transform:translateY(-1px);font-weight:600}.orf-type-btn.active .orf-btn-icon{color:#1e3a8a}.orf-type-btn--pdf.active,.orf-type-btn--pdf.active .orf-btn-icon{color:#dc2626}.orf-type-btn--excel.active,.orf-type-btn--excel.active .orf-btn-icon{color:#16a34a}.orf-type-btn--grid.active,.orf-type-btn--grid.active .orf-btn-icon{color:#2563eb}.orf-section-divider{display:flex;align-items:center;gap:12px;padding:0 24px;margin:18px 0 0}.orf-section-divider__line{flex:1;height:1px;background:linear-gradient(90deg,transparent,#e2e8f0,#c7d2fe,#e2e8f0,transparent)}.orf-section-divider__label{display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:20px;background:#f0f4ff;border:1px solid #c7d2fe;color:#4f46e5;font-size:11px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;white-space:nowrap;flex-shrink:0}.orf-section-divider__label svg{width:12px;height:12px;color:#6366f1}.orf-body{padding:16px 20px 4px}.orf-footer{padding:12px 20px 20px;display:flex;justify-content:flex-end;align-items:center;gap:10px}.orf-generate-btn{display:inline-flex;align-items:center;gap:8px;padding:11px 24px;border-radius:10px;border:none;background:linear-gradient(135deg,#1e3a8a,#2563eb);color:#fff;font-size:14px;font-weight:600;cursor:pointer;transition:all .25s ease;box-shadow:0 4px 18px #2563eb59;letter-spacing:.01em;font-family:inherit;position:relative;overflow:hidden}.orf-generate-btn:after{content:\"\";position:absolute;inset:0;background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.18) 50%,transparent 100%);transform:translate(-100%) skew(-12deg);pointer-events:none}.orf-generate-btn .orf-btn-icon{width:18px;height:18px}.orf-generate-btn__arrow{width:18px;height:18px;transition:transform .22s ease;flex-shrink:0}.orf-generate-btn:hover:not([disabled]){background:linear-gradient(135deg,#1e40af,#1d4ed8);box-shadow:0 6px 24px #2563eb80;transform:translateY(-1px)}.orf-generate-btn:hover:not([disabled]):after{animation:orf-shimmer .55s ease forwards}.orf-generate-btn:hover:not([disabled]) .orf-generate-btn__arrow{transform:translate(5px)}.orf-generate-btn:active:not([disabled]){transform:translateY(0);box-shadow:0 2px 8px #2563eb38}.orf-generate-btn[disabled]{opacity:.72;cursor:not-allowed;box-shadow:none}.orf-generate-btn__spinner{width:16px;height:16px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;display:inline-block;animation:orf-spin .7s linear infinite;flex-shrink:0}.orf-more-btn{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:10px;border:1.5px solid #e5e7eb;background:#fff;cursor:pointer;transition:background .15s,border-color .15s,transform .15s,box-shadow .15s;padding:0;flex-shrink:0}.orf-more-btn svg{width:15px;height:15px;fill:#9ca3af;transition:fill .15s}.orf-more-btn:hover{background:#f5f3ff;border-color:#6366f1;box-shadow:0 2px 8px #0000001a;transform:translateY(-1px)}.orf-more-btn:hover svg{fill:#6366f1}.orf-more-menu{position:fixed;z-index:9999;min-width:196px;background:#fff;border:1px solid #eaecf0;border-radius:12px;box-shadow:0 12px 32px #10182824,0 4px 12px #1018280f;overflow:hidden;animation:orf-menu-pop .16s cubic-bezier(.16,1,.3,1)}.orf-more-menu__header{padding:10px 14px 7px;font-size:10px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#9ca3af;border-bottom:1px solid #f2f4f7;-webkit-user-select:none;user-select:none}.orf-more-menu__item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 14px;background:transparent;border:none;border-left:3px solid transparent;border-bottom:1px solid #f9fafb;text-align:left;font-size:13px;font-weight:500;font-family:inherit;color:#374151;cursor:pointer;white-space:nowrap;transition:background .12s,color .12s,border-left-color .12s}.orf-more-menu__item:last-child{border-bottom:none}.orf-more-menu__item:hover{background:#f5f3ff;color:#6366f1;border-left-color:#6366f1}.orf-more-menu__item:hover .orf-more-menu__dot{background:#6366f1;box-shadow:0 0 0 3px #6366f129}.orf-more-menu__dot{flex-shrink:0;width:6px;height:6px;border-radius:50%;background:#d1d5db;transition:background .12s,box-shadow .12s}@keyframes orf-menu-pop{0%{opacity:0;transform:translateY(-8px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}.orf-results-view{position:fixed;inset:0;z-index:200;display:flex;flex-direction:column;background:#f1f5f9;animation:orf-fade-in .25s ease}.orf-results-header{flex-shrink:0;height:56px;background:linear-gradient(135deg,#1e3a8a,#2563eb 60%,#3b82f6);display:flex;align-items:center;justify-content:space-between;padding:0 16px;gap:12px;position:relative;overflow:hidden;box-shadow:0 2px 14px #2563eb73}.orf-results-header:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(255,255,255,.09) 1px,transparent 1px);background-size:16px 16px;pointer-events:none}.orf-results-header:after{content:\"\";position:absolute;width:200px;height:200px;border-radius:50%;background:radial-gradient(circle,rgba(255,255,255,.08) 0%,transparent 70%);top:-90px;right:40px;pointer-events:none}.orf-results-header__left{display:flex;align-items:center;gap:10px;position:relative;z-index:1;min-width:0;flex:1}.orf-results-header__sep{width:1px;height:22px;background:#ffffff40;flex-shrink:0}.orf-results-header__report-icon{width:20px;height:20px;color:#ffffffd9;flex-shrink:0}.orf-results-header__title{font-size:15px;font-weight:700;color:#fff;letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-shadow:0 1px 2px rgba(0,0,0,.12)}.orf-results-badge{display:inline-flex;align-items:center;gap:5px;padding:3px 10px;border-radius:20px;background:#ffffff26;border:1px solid rgba(255,255,255,.22);color:#ffffffeb;font-size:11px;font-weight:600;white-space:nowrap;flex-shrink:0;animation:orf-badge-pop .35s cubic-bezier(.34,1.56,.64,1) both}.orf-results-badge .orf-btn-icon{width:13px;height:13px}.orf-back-btn{display:inline-flex;align-items:center;gap:5px;padding:6px 14px;border-radius:8px;border:1px solid rgba(255,255,255,.3);background:#ffffff21;color:#fff;font-size:13px;font-weight:500;cursor:pointer;transition:all .18s ease;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:inherit;flex-shrink:0;position:relative;z-index:1}.orf-back-btn .orf-btn-icon{width:16px;height:16px;transition:transform .18s ease}.orf-back-btn:hover{background:#ffffff3d;border-color:#ffffff80}.orf-back-btn:hover .orf-btn-icon{transform:translate(-3px)}.orf-back-btn:active{transform:scale(.97)}.orf-results-header__right{display:flex;align-items:center;gap:8px;position:relative;z-index:1;flex-shrink:0}.orf-reexport-btn{display:inline-flex;align-items:center;gap:5px;padding:6px 13px;border-radius:8px;border:1px solid rgba(255,255,255,.28);background:#ffffff1f;color:#ffffffd9;font-size:12px;font-weight:500;cursor:pointer;transition:all .18s ease;-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);font-family:inherit;white-space:nowrap}.orf-reexport-btn .orf-btn-icon{width:15px;height:15px}.orf-reexport-btn:hover{background:#ffffff38;border-color:#ffffff73;color:#fff;transform:translateY(-1px)}.orf-reexport-btn--excel:hover{background:#16a34a52;border-color:#16a34a8c}.orf-reexport-btn--pdf:hover{background:#dc262652;border-color:#dc26268c}.orf-results-body{flex:1;overflow:hidden;padding:12px 12px 0;display:flex;flex-direction:column}.orf-results-body osl-report-grid{flex:1;display:block}\n"] }]
|
|
5868
5900
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { title: [{
|
|
5869
5901
|
type: Input
|
|
5870
5902
|
}], formElements: [{
|
|
@@ -5887,9 +5919,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
5887
5919
|
type: Input
|
|
5888
5920
|
}], pdfConfig: [{
|
|
5889
5921
|
type: Input
|
|
5922
|
+
}], moreMenuActions: [{
|
|
5923
|
+
type: Input,
|
|
5924
|
+
args: ['moreMenuActions']
|
|
5890
5925
|
}], _grid: [{
|
|
5891
5926
|
type: ViewChild,
|
|
5892
5927
|
args: [OslReportGrid]
|
|
5928
|
+
}], onDocumentClick: [{
|
|
5929
|
+
type: HostListener,
|
|
5930
|
+
args: ['document:click']
|
|
5893
5931
|
}] } });
|
|
5894
5932
|
|
|
5895
5933
|
class OslUserLog {
|