spiderly 19.11.7 → 19.11.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/docs/angular-customization/index.md +34 -1
- package/agent/docs/angular-customization/references/helper-functions.generated.md +1 -0
- package/agent/docs/entity-design/references/attributes.generated.md +1 -0
- package/fesm2022/spiderly.mjs +398 -91
- package/fesm2022/spiderly.mjs.map +1 -1
- package/lib/components/spiderly-data-table/spiderly-data-table.component.d.ts +160 -14
- package/lib/components/spiderly-data-view/spiderly-data-view.component.d.ts +8 -1
- package/lib/services/helper-functions.d.ts +6 -0
- package/package.json +1 -1
- package/public-api.d.ts +0 -1
- package/styles/components/spiderly-data-table/spiderly-data-table.component.scss +34 -0
- package/styles/components/spiderly-data-view/spiderly-data-view.component.scss +16 -0
- package/styles/styles/layout/_content.scss +1 -2
- package/styles/styles/layout/_main.scss +15 -1
- package/styles/styles/layout/_menu.scss +1 -1
- package/styles/styles/layout/_mixins.scss +33 -0
- package/styles/styles/layout/_topbar.scss +4 -4
- package/styles/styles/layout/layout.scss +0 -1
- package/lib/components/footer/footer.component.d.ts +0 -9
- package/styles/styles/layout/_footer.scss +0 -11
package/fesm2022/spiderly.mjs
CHANGED
|
@@ -723,6 +723,31 @@ function getImageDimensions(file) {
|
|
|
723
723
|
img.src = URL.createObjectURL(file);
|
|
724
724
|
});
|
|
725
725
|
}
|
|
726
|
+
/**
|
|
727
|
+
* Scrolls `element` back under the top of the viewport, but only when it has already scrolled
|
|
728
|
+
* above it. Set the element's `scroll-margin-top` to whatever fixed chrome must stay clear.
|
|
729
|
+
* Assumes the element's nearest scroll container is the viewport itself.
|
|
730
|
+
*/
|
|
731
|
+
// This is the one full telling of the three decisions below; paging is the caller (see
|
|
732
|
+
// spiderly-data-table/CLAUDE.md → "Scroll back to the table on page change").
|
|
733
|
+
// - The offset is READ from CSS rather than passed in, so the amount of fixed chrome to clear
|
|
734
|
+
// stays declared in one place per component and no TypeScript knows the shell's dimensions.
|
|
735
|
+
// That read is also why the viewport assumption above matters: getBoundingClientRect is
|
|
736
|
+
// viewport-relative, so inside a consumer's own overflow container it measures the wrong box.
|
|
737
|
+
// - The early return is what stops a list sitting BELOW other page content (a table inside a
|
|
738
|
+
// details form) from shoving that content off-screen when it was already visible.
|
|
739
|
+
// - `behavior: 'instant'` overrides the element's CSS `scroll-behavior`; `'auto'` defers to it,
|
|
740
|
+
// so an app setting `html { scroll-behavior: smooth }` would animate every call.
|
|
741
|
+
function scrollElementIntoViewIfAboveViewport(element) {
|
|
742
|
+
const offset = parseFloat(getComputedStyle(element).scrollMarginTop) || 0;
|
|
743
|
+
if (element.getBoundingClientRect().top >= offset)
|
|
744
|
+
return;
|
|
745
|
+
element.scrollIntoView({
|
|
746
|
+
block: 'start',
|
|
747
|
+
inline: 'nearest',
|
|
748
|
+
behavior: 'instant',
|
|
749
|
+
});
|
|
750
|
+
}
|
|
726
751
|
|
|
727
752
|
class SpiderlyCalendarComponent extends BaseControl {
|
|
728
753
|
constructor(translocoService) {
|
|
@@ -3080,19 +3105,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
|
|
|
3080
3105
|
type: Input
|
|
3081
3106
|
}] } });
|
|
3082
3107
|
|
|
3083
|
-
class FooterComponent {
|
|
3084
|
-
constructor(config) {
|
|
3085
|
-
this.config = config;
|
|
3086
|
-
this.companyName = this.config.companyName;
|
|
3087
|
-
}
|
|
3088
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: FooterComponent, deps: [{ token: ConfigServiceBase }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3089
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: FooterComponent, isStandalone: true, selector: "footer", ngImport: i0, template: "<div class=\"layout-footer\">\n <span class=\"font-medium ml-2\">{{ companyName }}</span>\n</div>\n" }); }
|
|
3090
|
-
}
|
|
3091
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: FooterComponent, decorators: [{
|
|
3092
|
-
type: Component,
|
|
3093
|
-
args: [{ selector: 'footer', standalone: true, template: "<div class=\"layout-footer\">\n <span class=\"font-medium ml-2\">{{ companyName }}</span>\n</div>\n" }]
|
|
3094
|
-
}], ctorParameters: () => [{ type: ConfigServiceBase }] });
|
|
3095
|
-
|
|
3096
3108
|
class IndexCardComponent {
|
|
3097
3109
|
constructor(formBuilder) {
|
|
3098
3110
|
this.formBuilder = formBuilder;
|
|
@@ -3829,7 +3841,7 @@ class SpiderlyLayoutComponent {
|
|
|
3829
3841
|
this.onAfterNgDestroy();
|
|
3830
3842
|
}
|
|
3831
3843
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyLayoutComponent, deps: [{ token: LayoutServiceBase }, { token: i0.Renderer2 }, { token: i3$2.Router }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3832
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: SpiderlyLayoutComponent, isStandalone: true, selector: "spiderly-layout", inputs: { menu: "menu", isSideMenuLayout: "isSideMenuLayout", showHoverBgOnRootTopMenuItems: "showHoverBgOnRootTopMenuItems", maxWidth: "maxWidth" }, viewQueries: [{ propertyName: "appSidebar", first: true, predicate: AppSidebarComponent, descendants: true }, { propertyName: "sidemenuTopbar", first: true, predicate: SideMenuTopBarComponent, descendants: true }, { propertyName: "topbarmenu", first: true, predicate: ["topbarmenu"], descendants: true }], ngImport: i0, template: "<div *ngIf=\"isSideMenuLayout\" class=\"layout-wrapper\" [ngClass]=\"containerClass\">\n <spiderly-sidemenu-topbar></spiderly-sidemenu-topbar>\n <div class=\"layout-sidebar\">\n <sidebar [menu]=\"sideMenu\"></sidebar>\n </div>\n <div class=\"layout-main-container\">\n <div class=\"layout-main\">\n <router-outlet></router-outlet>\n </div>\n
|
|
3844
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: SpiderlyLayoutComponent, isStandalone: true, selector: "spiderly-layout", inputs: { menu: "menu", isSideMenuLayout: "isSideMenuLayout", showHoverBgOnRootTopMenuItems: "showHoverBgOnRootTopMenuItems", maxWidth: "maxWidth" }, viewQueries: [{ propertyName: "appSidebar", first: true, predicate: AppSidebarComponent, descendants: true }, { propertyName: "sidemenuTopbar", first: true, predicate: SideMenuTopBarComponent, descendants: true }, { propertyName: "topbarmenu", first: true, predicate: ["topbarmenu"], descendants: true }], ngImport: i0, template: "<div *ngIf=\"isSideMenuLayout\" class=\"layout-wrapper\" [ngClass]=\"containerClass\">\n <spiderly-sidemenu-topbar></spiderly-sidemenu-topbar>\n <div class=\"layout-sidebar\">\n <sidebar [menu]=\"sideMenu\"></sidebar>\n </div>\n <div class=\"layout-main-container\">\n <div class=\"layout-main\">\n <router-outlet></router-outlet>\n </div>\n </div>\n <div class=\"layout-mask\"></div>\n</div>\n\n<div *ngIf=\"isSideMenuLayout === false\">\n <spiderly-topbar\n [menu]=\"topMenu\"\n [showHoverBgOnRootItems]=\"showHoverBgOnRootTopMenuItems\"\n >\n <ng-content ACTIONS select=\"[ACTIONS]\"></ng-content>\n </spiderly-topbar>\n <main [style.max-width]=\"maxWidth\" style=\"margin: auto; padding: 18px\">\n <router-outlet></router-outlet>\n </main>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i3$2.RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "component", type: AppSidebarComponent, selector: "sidebar", inputs: ["menu"] }, { kind: "component", type: SideMenuTopBarComponent, selector: "spiderly-sidemenu-topbar" }, { kind: "component", type: TopBarComponent, selector: "spiderly-topbar", inputs: ["menu", "showHoverBgOnRootItems"] }] }); }
|
|
3833
3845
|
}
|
|
3834
3846
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyLayoutComponent, decorators: [{
|
|
3835
3847
|
type: Component,
|
|
@@ -3837,10 +3849,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
|
|
|
3837
3849
|
CommonModule,
|
|
3838
3850
|
RouterModule,
|
|
3839
3851
|
AppSidebarComponent,
|
|
3840
|
-
FooterComponent,
|
|
3841
3852
|
SideMenuTopBarComponent,
|
|
3842
3853
|
TopBarComponent,
|
|
3843
|
-
], template: "<div *ngIf=\"isSideMenuLayout\" class=\"layout-wrapper\" [ngClass]=\"containerClass\">\n <spiderly-sidemenu-topbar></spiderly-sidemenu-topbar>\n <div class=\"layout-sidebar\">\n <sidebar [menu]=\"sideMenu\"></sidebar>\n </div>\n <div class=\"layout-main-container\">\n <div class=\"layout-main\">\n <router-outlet></router-outlet>\n </div>\n
|
|
3854
|
+
], template: "<div *ngIf=\"isSideMenuLayout\" class=\"layout-wrapper\" [ngClass]=\"containerClass\">\n <spiderly-sidemenu-topbar></spiderly-sidemenu-topbar>\n <div class=\"layout-sidebar\">\n <sidebar [menu]=\"sideMenu\"></sidebar>\n </div>\n <div class=\"layout-main-container\">\n <div class=\"layout-main\">\n <router-outlet></router-outlet>\n </div>\n </div>\n <div class=\"layout-mask\"></div>\n</div>\n\n<div *ngIf=\"isSideMenuLayout === false\">\n <spiderly-topbar\n [menu]=\"topMenu\"\n [showHoverBgOnRootItems]=\"showHoverBgOnRootTopMenuItems\"\n >\n <ng-content ACTIONS select=\"[ACTIONS]\"></ng-content>\n </spiderly-topbar>\n <main [style.max-width]=\"maxWidth\" style=\"margin: auto; padding: 18px\">\n <router-outlet></router-outlet>\n </main>\n</div>\n" }]
|
|
3844
3855
|
}], ctorParameters: () => [{ type: LayoutServiceBase }, { type: i0.Renderer2 }, { type: i3$2.Router }], propDecorators: { menu: [{
|
|
3845
3856
|
type: Input
|
|
3846
3857
|
}], isSideMenuLayout: [{
|
|
@@ -3990,14 +4001,32 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
|
|
|
3990
4001
|
args: [{ selector: 'spiderly-delete-confirmation', imports: [SpiderlyButtonComponent, TranslocoDirective, ConfirmDialogModule], providers: [ConfirmationService], template: "<ng-container *transloco=\"let t\">\n <p-confirmDialog #cd></p-confirmDialog>\n <div\n class=\"border-b surface-border text-lg\"\n style=\"\n display: flex;\n align-items: center;\n padding-bottom: 10px;\n width: 100%;\n gap: 10px;\n \"\n >\n <i class=\"pi pi-exclamation-circle text-4xl\"></i>\n <p>{{ config.data.message }}</p>\n </div>\n\n <div\n style=\"display: flex; gap: 12px; justify-content: right; margin-top: 28px\"\n >\n <spiderly-button\n (onClick)=\"reject()\"\n icon=\"pi pi-times\"\n [label]=\"t('Cancel')\"\n severity=\"secondary\"\n ></spiderly-button>\n <spiderly-button\n (onClick)=\"accept()\"\n icon=\"pi pi-check\"\n [label]=\"t('Confirm')\"\n styleClass=\"p-button-danger\"\n ></spiderly-button>\n </div>\n</ng-container>\n" }]
|
|
3991
4002
|
}], ctorParameters: () => [{ type: i1$5.DynamicDialogRef }, { type: i1$5.DynamicDialogConfig }] });
|
|
3992
4003
|
|
|
4004
|
+
/**
|
|
4005
|
+
* Default column width per filter type, in rem. Sized for the HEADER — the filter input plus its
|
|
4006
|
+
* match-mode dropdown — so a column of short values reserves more than its content needs; that is
|
|
4007
|
+
* deliberate, and {@link Column.width} is the per-column override.
|
|
4008
|
+
*
|
|
4009
|
+
* A table, not a switch, so the mapping is exhaustive: a new `filterType` fails the build here
|
|
4010
|
+
* instead of silently inheriting the actions-column reservation below.
|
|
4011
|
+
*/
|
|
4012
|
+
const DEFAULT_COLUMN_WIDTH_REM = {
|
|
4013
|
+
text: 12,
|
|
4014
|
+
date: 10,
|
|
4015
|
+
multiselect: 12,
|
|
4016
|
+
boolean: 8,
|
|
4017
|
+
numeric: 12,
|
|
4018
|
+
// Fits the 45px thumbnail `#defaultCell` renders for a blob.
|
|
4019
|
+
blob: 5,
|
|
4020
|
+
};
|
|
3993
4021
|
class SpiderlyDataTableComponent {
|
|
3994
|
-
constructor(router, dialogService, route, messageService, translocoService, configService, locale) {
|
|
4022
|
+
constructor(router, dialogService, route, messageService, translocoService, configService, errorHandler, locale) {
|
|
3995
4023
|
this.router = router;
|
|
3996
4024
|
this.dialogService = dialogService;
|
|
3997
4025
|
this.route = route;
|
|
3998
4026
|
this.messageService = messageService;
|
|
3999
4027
|
this.translocoService = translocoService;
|
|
4000
4028
|
this.configService = configService;
|
|
4029
|
+
this.errorHandler = errorHandler;
|
|
4001
4030
|
this.locale = locale;
|
|
4002
4031
|
this.destroy$ = new Subject();
|
|
4003
4032
|
this.tableIcon = 'pi pi-list';
|
|
@@ -4009,6 +4038,17 @@ class SpiderlyDataTableComponent {
|
|
|
4009
4038
|
this.showCardWrapper = false;
|
|
4010
4039
|
this.readonly = false;
|
|
4011
4040
|
this.idField = 'id';
|
|
4041
|
+
/**
|
|
4042
|
+
* Sizes the columns from their declared widths instead of from the rendered rows.
|
|
4043
|
+
*
|
|
4044
|
+
* The browser default is `table-layout: auto`, which computes every column from the content
|
|
4045
|
+
* of the rows currently on screen — so a column is one width on this page and another on the
|
|
4046
|
+
* next, and a filter (which narrows the variety of values in the column it filters) moves the
|
|
4047
|
+
* whole grid. PrimeNG sets no `table-layout` of its own, so without this the table inherits
|
|
4048
|
+
* that default. Bound to a field, never an object literal in the template: a literal is a new
|
|
4049
|
+
* reference on every change-detection pass.
|
|
4050
|
+
*/
|
|
4051
|
+
this.tableStyle = { 'table-layout': 'fixed' };
|
|
4012
4052
|
this.onTotalRecordsChange = new EventEmitter();
|
|
4013
4053
|
this.loading = true;
|
|
4014
4054
|
this.newlySelectedItems = [];
|
|
@@ -4026,6 +4066,7 @@ class SpiderlyDataTableComponent {
|
|
|
4026
4066
|
this.onIsAllSelectedChange = new EventEmitter();
|
|
4027
4067
|
this.matchModeDateOptions = [];
|
|
4028
4068
|
this.matchModeNumberOptions = [];
|
|
4069
|
+
this.matchModeTextOptions = [];
|
|
4029
4070
|
/** Whether the "Add" button is shown. Defaults to `true`. */
|
|
4030
4071
|
this.showAddButton = true;
|
|
4031
4072
|
/** Whether the "Export to Excel" button is shown. Defaults to `true`. */
|
|
@@ -4068,7 +4109,25 @@ class SpiderlyDataTableComponent {
|
|
|
4068
4109
|
* transient safety reveal into a durable choice.
|
|
4069
4110
|
*/
|
|
4070
4111
|
this.revealedByConstraint = new Set();
|
|
4112
|
+
/**
|
|
4113
|
+
* The filter keys the rows on screen were actually narrowed by. Kept apart from
|
|
4114
|
+
* `table.filters` because those are not the same question: PrimeNG's `onModelChange`
|
|
4115
|
+
* writes each keystroke of a text/numeric filter straight into the meta and only calls
|
|
4116
|
+
* `_filter()` for the auto-applying types, so `table.filters` holds PENDING edits too.
|
|
4117
|
+
* Reading it for the header icon marked a column filtered the moment the operator typed
|
|
4118
|
+
* a character, before Apply — claiming the grid was narrowed when it was not.
|
|
4119
|
+
*/
|
|
4120
|
+
this.appliedFilterKeys = new Set();
|
|
4071
4121
|
this.clientFilterCount = 0;
|
|
4122
|
+
/**
|
|
4123
|
+
* Memo for the resolution below. Two jobs: the narrowing maps to a NEW array and a
|
|
4124
|
+
* template binding must not hand PrimeNG a fresh reference every CD pass, and both
|
|
4125
|
+
* halves (offered list, default mode) must come from ONE computation so they cannot
|
|
4126
|
+
* disagree. Keyed on the declared array's identity, so reassigning `col.matchModes`
|
|
4127
|
+
* recomputes — note PrimeNG itself reads `matchModeOptions` only in its own `ngOnInit`,
|
|
4128
|
+
* so a post-init change still never reaches the rendered dropdown.
|
|
4129
|
+
*/
|
|
4130
|
+
this.matchModeResolutions = new WeakMap();
|
|
4072
4131
|
}
|
|
4073
4132
|
ngAfterViewInit() {
|
|
4074
4133
|
this.setupRemovableSort();
|
|
@@ -4170,6 +4229,24 @@ class SpiderlyDataTableComponent {
|
|
|
4170
4229
|
value: MatchModeCodes.GreaterThan,
|
|
4171
4230
|
},
|
|
4172
4231
|
];
|
|
4232
|
+
// Only the three the generated paginator implements for strings
|
|
4233
|
+
// (PaginatedResultGenerator.GetCaseForString). Declared rather than left to PrimeNG,
|
|
4234
|
+
// whose own text list adds notContains/endsWith/notEquals — modes the backend answers
|
|
4235
|
+
// with InvalidMatchMode, i.e. a 400 on every load once the user picks one.
|
|
4236
|
+
this.matchModeTextOptions = [
|
|
4237
|
+
{
|
|
4238
|
+
label: this.translocoService.translate('StartsWith'),
|
|
4239
|
+
value: MatchModeCodes.StartsWith,
|
|
4240
|
+
},
|
|
4241
|
+
{
|
|
4242
|
+
label: this.translocoService.translate('Contains'),
|
|
4243
|
+
value: MatchModeCodes.Contains,
|
|
4244
|
+
},
|
|
4245
|
+
{
|
|
4246
|
+
label: this.translocoService.translate('Equals'),
|
|
4247
|
+
value: MatchModeCodes.Equals,
|
|
4248
|
+
},
|
|
4249
|
+
];
|
|
4173
4250
|
this.matchModeNumberOptions = [
|
|
4174
4251
|
{
|
|
4175
4252
|
label: this.translocoService.translate('Equals'),
|
|
@@ -4197,6 +4274,10 @@ class SpiderlyDataTableComponent {
|
|
|
4197
4274
|
this.mergeActivePageSizesIntoOptions();
|
|
4198
4275
|
this.restoreColumnVisibility();
|
|
4199
4276
|
this.reconcileVisibilityWithPersistedConstraints();
|
|
4277
|
+
this.reconcilePersistedMatchModes();
|
|
4278
|
+
// Restored filters are applied by definition — mark them before the first paint rather
|
|
4279
|
+
// than waiting for the first response, which is the reload case the icon exists for.
|
|
4280
|
+
this.snapshotAppliedFilters(this.persistedTableState()?.filters);
|
|
4200
4281
|
this.chooserCols = this.cols.filter(SpiderlyDataTableComponent.isDataColumn);
|
|
4201
4282
|
this.refreshVisibleCols();
|
|
4202
4283
|
// Bound to p-table's [multiSortMeta] so the FIRST request already carries the right
|
|
@@ -4276,9 +4357,8 @@ class SpiderlyDataTableComponent {
|
|
|
4276
4357
|
return;
|
|
4277
4358
|
let cleared = false;
|
|
4278
4359
|
for (const col of cols) {
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
delete this.table.filters[filterKey];
|
|
4360
|
+
if (this.columnHasConstraint(this.table, col)) {
|
|
4361
|
+
delete this.table.filters[this.filterKey(col)];
|
|
4282
4362
|
cleared = true;
|
|
4283
4363
|
}
|
|
4284
4364
|
if (this.table._multiSortMeta?.some((m) => m.field === col.field)) {
|
|
@@ -4296,6 +4376,40 @@ class SpiderlyDataTableComponent {
|
|
|
4296
4376
|
m.value !== '' &&
|
|
4297
4377
|
(!Array.isArray(m.value) || m.value.length > 0));
|
|
4298
4378
|
}
|
|
4379
|
+
/**
|
|
4380
|
+
* The one home of the filter-state key rule (sort meta is keyed by `field` instead).
|
|
4381
|
+
* Instance rather than static so the template's `[field]` binding can share it.
|
|
4382
|
+
*/
|
|
4383
|
+
filterKey(col) {
|
|
4384
|
+
return col.filterField ?? col.field;
|
|
4385
|
+
}
|
|
4386
|
+
/**
|
|
4387
|
+
* Whether the rows on screen are narrowed by this column — feeds the projected
|
|
4388
|
+
* `filtericon` template. Why not the template's `let-hasFilter`, and why applied rather
|
|
4389
|
+
* than pending state: CLAUDE.md → "Active-filter header icon".
|
|
4390
|
+
*/
|
|
4391
|
+
isColumnFiltered(col) {
|
|
4392
|
+
return this.appliedFilterKeys.has(this.filterKey(col));
|
|
4393
|
+
}
|
|
4394
|
+
/** Whether the column carries a constraint at all, applied or still being typed. */
|
|
4395
|
+
columnHasConstraint(table, col) {
|
|
4396
|
+
return SpiderlyDataTableComponent.isActiveFilterMeta(table.filters?.[this.filterKey(col)]);
|
|
4397
|
+
}
|
|
4398
|
+
/**
|
|
4399
|
+
* Records what a just-applied filter set narrows by. Called from every path that commits
|
|
4400
|
+
* one: `(onFilter)` (Apply, auto-apply, a per-column Clear), `lazyLoad` (which also covers
|
|
4401
|
+
* `table.clear()` — it re-queries WITHOUT emitting onFilter), the caption's Clear filters,
|
|
4402
|
+
* and once from `ngOnInit` off persisted state, so a restored filter is marked on first
|
|
4403
|
+
* paint rather than after the first response.
|
|
4404
|
+
*/
|
|
4405
|
+
snapshotAppliedFilters(filters) {
|
|
4406
|
+
const applied = new Set();
|
|
4407
|
+
for (const [field, meta] of Object.entries(filters ?? {})) {
|
|
4408
|
+
if (SpiderlyDataTableComponent.isActiveFilterMeta(meta))
|
|
4409
|
+
applied.add(field);
|
|
4410
|
+
}
|
|
4411
|
+
this.appliedFilterKeys = applied;
|
|
4412
|
+
}
|
|
4299
4413
|
persistColumnVisibility() {
|
|
4300
4414
|
if (!this.columnsStateKey)
|
|
4301
4415
|
return;
|
|
@@ -4345,7 +4459,7 @@ class SpiderlyDataTableComponent {
|
|
|
4345
4459
|
continue;
|
|
4346
4460
|
if (this.isColumnVisible(col))
|
|
4347
4461
|
continue;
|
|
4348
|
-
if (constrained.has(col
|
|
4462
|
+
if (constrained.has(this.filterKey(col)) || constrained.has(col.field)) {
|
|
4349
4463
|
this.revealedByConstraint.add(col.field);
|
|
4350
4464
|
}
|
|
4351
4465
|
}
|
|
@@ -4388,6 +4502,47 @@ class SpiderlyDataTableComponent {
|
|
|
4388
4502
|
return col == null || this.isColumnSortable(col);
|
|
4389
4503
|
});
|
|
4390
4504
|
}
|
|
4505
|
+
/**
|
|
4506
|
+
* The filter twin of `keepSortableMeta` — persisted state outlives the rule that produced
|
|
4507
|
+
* it, here a match mode stored before the column declared `matchModes`. It has to REWRITE
|
|
4508
|
+
* storage rather than filter a value on the way past: `ColumnFilter.ngOnInit` skips
|
|
4509
|
+
* `initFieldFilterConstraint()` whenever the field already carries a constraint, so our
|
|
4510
|
+
* `[matchMode]` never applies to a restored one. Left alone, the column keeps filtering by
|
|
4511
|
+
* a mode it no longer offers while its match-mode `<p-select>` renders blank (the stored
|
|
4512
|
+
* value is not among the options). Runs before PrimeNG's `restoreState()`, which reads this
|
|
4513
|
+
* same key on the first `[value]` change.
|
|
4514
|
+
*
|
|
4515
|
+
* Deliberately narrow: it touches only constraints whose mode the declaring column no
|
|
4516
|
+
* longer offers, so every other byte of the persisted blob survives.
|
|
4517
|
+
*/
|
|
4518
|
+
reconcilePersistedMatchModes() {
|
|
4519
|
+
if (!this.resolvedStateKey)
|
|
4520
|
+
return;
|
|
4521
|
+
const state = this.persistedTableState();
|
|
4522
|
+
if (!state?.filters)
|
|
4523
|
+
return;
|
|
4524
|
+
let changed = false;
|
|
4525
|
+
for (const col of this.cols ?? []) {
|
|
4526
|
+
if (!col.matchModes?.length)
|
|
4527
|
+
continue;
|
|
4528
|
+
const offered = this.getColMatchModeOptions(col);
|
|
4529
|
+
const meta = state.filters[this.filterKey(col)];
|
|
4530
|
+
if (!offered?.length || !Array.isArray(meta))
|
|
4531
|
+
continue;
|
|
4532
|
+
for (const constraint of meta) {
|
|
4533
|
+
if (constraint?.matchMode == null)
|
|
4534
|
+
continue;
|
|
4535
|
+
if (offered.some((option) => option.value === constraint.matchMode))
|
|
4536
|
+
continue;
|
|
4537
|
+
constraint.matchMode = this.getColMatchMode(col);
|
|
4538
|
+
changed = true;
|
|
4539
|
+
}
|
|
4540
|
+
}
|
|
4541
|
+
if (!changed)
|
|
4542
|
+
return;
|
|
4543
|
+
const storage = this.stateStorage === 'local' ? localStorage : sessionStorage;
|
|
4544
|
+
storage.setItem(this.resolvedStateKey, JSON.stringify(state));
|
|
4545
|
+
}
|
|
4391
4546
|
// Safety net: any lazy load still leaving unsorted (Clear filters — PrimeNG's clear()
|
|
4392
4547
|
// nulls the sort and emits in one call — or stale persisted state) falls back to the
|
|
4393
4548
|
// declared default. Patching the event before it becomes the backend Filter avoids a
|
|
@@ -4406,58 +4561,112 @@ class SpiderlyDataTableComponent {
|
|
|
4406
4561
|
this.table.tableService.onSort(defaultSort);
|
|
4407
4562
|
}
|
|
4408
4563
|
}
|
|
4564
|
+
onPageChange(container) {
|
|
4565
|
+
scrollElementIntoViewIfAboveViewport(container);
|
|
4566
|
+
}
|
|
4567
|
+
/**
|
|
4568
|
+
* The one pending predicate: PrimeNG's overlay and the container's `aria-busy` must never
|
|
4569
|
+
* disagree about whether a fetch is in flight.
|
|
4570
|
+
*
|
|
4571
|
+
* It used to also test `items === undefined`, for the first load. That clause is dead now
|
|
4572
|
+
* that `loading` initialises true and every lazyLoad raises it — except in the one state
|
|
4573
|
+
* where it was actively wrong: a FAILED first load lowers the flag with `items` still
|
|
4574
|
+
* undefined, which pinned the overlay up forever. Same stranded overlay this component was
|
|
4575
|
+
* fixed for once already, through the other door.
|
|
4576
|
+
*/
|
|
4577
|
+
get isPending() {
|
|
4578
|
+
return this.loading;
|
|
4579
|
+
}
|
|
4409
4580
|
lazyLoad(event) {
|
|
4581
|
+
// Every refetch is pending, not just the first load and reload(). `items` is deliberately
|
|
4582
|
+
// left alone: the previous page stays on screen under the overlay (stale-while-revalidate)
|
|
4583
|
+
// rather than blanking. Raising the flag is also what stops PrimeNG's empty message, gated
|
|
4584
|
+
// on `isEmpty() && !loading`, from claiming "no records" for the length of the request.
|
|
4585
|
+
this.loading = true;
|
|
4410
4586
|
this.applyDefaultSortIfUnsorted(event);
|
|
4411
4587
|
this.lastLazyLoadEvent = event;
|
|
4412
4588
|
this.rangeAnchorId = null;
|
|
4589
|
+
this.snapshotAppliedFilters(event.filters);
|
|
4413
4590
|
let tableFilter = event;
|
|
4414
4591
|
tableFilter.additionalFilterIdLong = this.additionalFilterIdLong;
|
|
4415
4592
|
this.onLazyLoad.next(tableFilter);
|
|
4593
|
+
// Issued NOW rather than after the list lands: it needs only `tableFilter`, so serialising
|
|
4594
|
+
// it behind the list response cost a second round trip on every page of every
|
|
4595
|
+
// selection-enabled table — and the overlay is held up for both. The `.catch` at creation
|
|
4596
|
+
// is mandatory, not defensive: when the list request errors, `next` never runs, and a
|
|
4597
|
+
// rejected promise nobody awaits is exactly the unowned rejection this handler exists to
|
|
4598
|
+
// prevent. The rejection is re-thrown out of `awaitSelectedIds` instead, where it has one.
|
|
4599
|
+
const selectedIds = this.selectedLazyLoadObservableMethod != null
|
|
4600
|
+
? firstValueFrom(this.selectedLazyLoadObservableMethod(tableFilter)).catch((error) => error)
|
|
4601
|
+
: null;
|
|
4416
4602
|
this.getPaginatedListObservableMethod(tableFilter).subscribe({
|
|
4417
4603
|
next: async (res) => {
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
this.
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
if (this.isFirstTimeLazyLoad == true) {
|
|
4427
|
-
this.rowsSelectedNumber =
|
|
4428
|
-
selectedRowsMethodResult.totalRecordsSelected;
|
|
4429
|
-
this.setFakeIsAllSelected();
|
|
4430
|
-
this.isFirstTimeLazyLoad = false;
|
|
4431
|
-
}
|
|
4432
|
-
if (this.isAllSelected == true) {
|
|
4433
|
-
let idsToInsert = [...this.items.map((x) => x[this.idField])];
|
|
4434
|
-
idsToInsert = idsToInsert.filter((x) => this.unselectedItems.includes(x) == false);
|
|
4435
|
-
this.fakeSelectedItems = [...idsToInsert]; // Only for showing checkboxes, we will not send this to the backend
|
|
4436
|
-
}
|
|
4437
|
-
else if (this.isAllSelected == false) {
|
|
4438
|
-
this.fakeSelectedItems = [...this.newlySelectedItems]; // Only for showing checkboxes, we will not send this to the backend
|
|
4439
|
-
}
|
|
4440
|
-
else if (this.isAllSelected == null) {
|
|
4441
|
-
let idsToInsert = [
|
|
4442
|
-
...selectedRowsMethodResult.selectedIds,
|
|
4443
|
-
...this.newlySelectedItems,
|
|
4444
|
-
];
|
|
4445
|
-
idsToInsert = idsToInsert.filter((x) => this.unselectedItems.includes(x) == false);
|
|
4446
|
-
this.fakeSelectedItems = [...idsToInsert];
|
|
4447
|
-
}
|
|
4604
|
+
// The await below is INSIDE an async subscriber, so a rejection there settles nothing:
|
|
4605
|
+
// `error:` belongs to the paginated-list observable and never runs. That used to strand
|
|
4606
|
+
// the overlay forever, hence the finally.
|
|
4607
|
+
try {
|
|
4608
|
+
this.items = res.data;
|
|
4609
|
+
this.totalRecords = res.totalRecords;
|
|
4610
|
+
this.onTotalRecordsChange.next(res.totalRecords);
|
|
4611
|
+
await this.reconcileSelectionForLoadedPage(selectedIds);
|
|
4448
4612
|
}
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4613
|
+
catch (error) {
|
|
4614
|
+
// The library's own owner: it toasts, it skips HttpErrorResponse (the interceptor
|
|
4615
|
+
// already reported those), and a consumer's ErrorHandler wrapper forwards to their
|
|
4616
|
+
// tracker. console.error here would have dropped both.
|
|
4617
|
+
this.errorHandler.handleError(error);
|
|
4618
|
+
}
|
|
4619
|
+
finally {
|
|
4620
|
+
this.loading = false;
|
|
4453
4621
|
}
|
|
4454
|
-
this.loading = false;
|
|
4455
4622
|
},
|
|
4456
4623
|
error: () => {
|
|
4457
4624
|
this.loading = false;
|
|
4458
4625
|
},
|
|
4459
4626
|
});
|
|
4460
4627
|
}
|
|
4628
|
+
/**
|
|
4629
|
+
* Repaints the checkbox column for the page that just landed. Split out of the subscriber so
|
|
4630
|
+
* the try/finally that guarantees the pending flag wraps an await, not fifty lines of
|
|
4631
|
+
* selection algebra.
|
|
4632
|
+
*/
|
|
4633
|
+
async reconcileSelectionForLoadedPage(selectedIds) {
|
|
4634
|
+
if (selectedIds == null) {
|
|
4635
|
+
if (this.deleteListFromTableObservableMethod) {
|
|
4636
|
+
this.fakeSelectedItems = this.items
|
|
4637
|
+
.map((x) => x[this.idField])
|
|
4638
|
+
.filter((id) => this.newlySelectedItems.includes(id));
|
|
4639
|
+
}
|
|
4640
|
+
return;
|
|
4641
|
+
}
|
|
4642
|
+
const selectedRowsMethodResult = await selectedIds;
|
|
4643
|
+
if (selectedRowsMethodResult instanceof Error)
|
|
4644
|
+
throw selectedRowsMethodResult;
|
|
4645
|
+
this.currentPageSelectedItemsFromDb = [
|
|
4646
|
+
...selectedRowsMethodResult.selectedIds,
|
|
4647
|
+
];
|
|
4648
|
+
if (this.isFirstTimeLazyLoad == true) {
|
|
4649
|
+
this.rowsSelectedNumber = selectedRowsMethodResult.totalRecordsSelected;
|
|
4650
|
+
this.setFakeIsAllSelected();
|
|
4651
|
+
this.isFirstTimeLazyLoad = false;
|
|
4652
|
+
}
|
|
4653
|
+
if (this.isAllSelected == true) {
|
|
4654
|
+
let idsToInsert = [...this.items.map((x) => x[this.idField])];
|
|
4655
|
+
idsToInsert = idsToInsert.filter((x) => this.unselectedItems.includes(x) == false);
|
|
4656
|
+
this.fakeSelectedItems = [...idsToInsert]; // Only for showing checkboxes, we will not send this to the backend
|
|
4657
|
+
}
|
|
4658
|
+
else if (this.isAllSelected == false) {
|
|
4659
|
+
this.fakeSelectedItems = [...this.newlySelectedItems]; // Only for showing checkboxes, we will not send this to the backend
|
|
4660
|
+
}
|
|
4661
|
+
else if (this.isAllSelected == null) {
|
|
4662
|
+
let idsToInsert = [
|
|
4663
|
+
...selectedRowsMethodResult.selectedIds,
|
|
4664
|
+
...this.newlySelectedItems,
|
|
4665
|
+
];
|
|
4666
|
+
idsToInsert = idsToInsert.filter((x) => this.unselectedItems.includes(x) == false);
|
|
4667
|
+
this.fakeSelectedItems = [...idsToInsert];
|
|
4668
|
+
}
|
|
4669
|
+
}
|
|
4461
4670
|
clientLoad() {
|
|
4462
4671
|
this.loading = false;
|
|
4463
4672
|
this.loadFormArrayItems();
|
|
@@ -4473,6 +4682,8 @@ class SpiderlyDataTableComponent {
|
|
|
4473
4682
|
this.setFakeIsAllSelected();
|
|
4474
4683
|
}
|
|
4475
4684
|
filter(event) {
|
|
4685
|
+
// Fires from _filter(), i.e. only once a filter is COMMITTED — never on a keystroke.
|
|
4686
|
+
this.snapshotAppliedFilters(event.filters ?? this.table?.filters);
|
|
4476
4687
|
if (this.hasLazyLoad && this.selectionMode === 'multiple')
|
|
4477
4688
|
this.selectAll(false); // We need to do it like this because: totalRecords: 1 -> selectedRecords from earlyer selection 2 -> unselect current -> all checkbox is set to true
|
|
4478
4689
|
if (this.hasLazyLoad === false && this.selectionMode === 'multiple') {
|
|
@@ -4504,29 +4715,76 @@ class SpiderlyDataTableComponent {
|
|
|
4504
4715
|
!!col.field &&
|
|
4505
4716
|
!col.field.endsWith('CommaSeparated'));
|
|
4506
4717
|
}
|
|
4507
|
-
/**
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
}
|
|
4718
|
+
/**
|
|
4719
|
+
* The column's declared {@link Column.width}, or the default for its filter type.
|
|
4720
|
+
*
|
|
4721
|
+
* A width, not a minimum: under the fixed layout {@link tableStyle} establishes, this is what
|
|
4722
|
+
* the column is sized from, and the table shares its surplus in PROPORTION to these numbers.
|
|
4723
|
+
* Columns declaring none would otherwise take an equal share, which throws away what the
|
|
4724
|
+
* per-type defaults say — a boolean holds "Da"/"Ne", a text column holds a name.
|
|
4725
|
+
*/
|
|
4726
|
+
getColWidth(col) {
|
|
4727
|
+
if (col.width != null)
|
|
4728
|
+
return col.width;
|
|
4729
|
+
if (col.filterType)
|
|
4730
|
+
return `${DEFAULT_COLUMN_WIDTH_REM[col.filterType]}rem`;
|
|
4731
|
+
// What is left declares no filterType: an actions column. It cannot shrink to fit any more,
|
|
4732
|
+
// so the icons need a reservation — each sits in a flex row whose gap is set beside them in
|
|
4733
|
+
// the template, inside the cell's own padding. 2.5, not 2.2, so the arithmetic stays exact in
|
|
4734
|
+
// binary and the string never reads `8.6000000001rem`.
|
|
4735
|
+
return `${2 + (col.actions?.length ?? 0) * 2.5}rem`;
|
|
4736
|
+
}
|
|
4737
|
+
getColMatchModeOptions(col) {
|
|
4738
|
+
return this.resolveMatchModes(col).options;
|
|
4739
|
+
}
|
|
4740
|
+
getColMatchMode(col) {
|
|
4741
|
+
return this.resolveMatchModes(col).defaultMode;
|
|
4742
|
+
}
|
|
4743
|
+
resolveMatchModes(col) {
|
|
4744
|
+
const cached = this.matchModeResolutions.get(col);
|
|
4745
|
+
if (cached && cached.source === col.matchModes)
|
|
4746
|
+
return cached.resolution;
|
|
4747
|
+
const resolution = this.computeMatchModes(col);
|
|
4748
|
+
this.matchModeResolutions.set(col, {
|
|
4749
|
+
source: col.matchModes,
|
|
4750
|
+
resolution,
|
|
4751
|
+
});
|
|
4752
|
+
return resolution;
|
|
4525
4753
|
}
|
|
4526
|
-
|
|
4754
|
+
/**
|
|
4755
|
+
* Applies `Column.matchModes`, and refuses to half-apply it. Both ways a declaration can
|
|
4756
|
+
* be wrong are consumer mistakes that PrimeNG would otherwise turn into a broken filter
|
|
4757
|
+
* rather than an error: it reads `matchModeOptions || <type defaults>`, so handing it
|
|
4758
|
+
* `[]` renders an EMPTY dropdown (an empty array is truthy) while the default mode still
|
|
4759
|
+
* seeds the constraint, and handing it `null` silently restores PrimeNG's own list. So an
|
|
4760
|
+
* unusable narrowing falls back to the full list and says so, instead of shipping a
|
|
4761
|
+
* dropdown the user cannot pick from.
|
|
4762
|
+
*/
|
|
4763
|
+
computeMatchModes(col) {
|
|
4764
|
+
const options = this.matchModeOptionsForType(col.filterType);
|
|
4765
|
+
const typeDefault = this.defaultMatchModeForType(col.filterType);
|
|
4766
|
+
const declared = col.matchModes;
|
|
4767
|
+
if (!declared?.length)
|
|
4768
|
+
return { options, defaultMode: typeDefault };
|
|
4769
|
+
if (!options?.length) {
|
|
4770
|
+
console.error(`spiderly-data-table: column "${col.field}" declares matchModes, but filterType "${col.filterType}" has no match modes to narrow — ignoring.`);
|
|
4771
|
+
return { options, defaultMode: typeDefault };
|
|
4772
|
+
}
|
|
4773
|
+
const narrowed = declared
|
|
4774
|
+
.map((code) => options.find((option) => option.value === code))
|
|
4775
|
+
.filter((option) => option != null);
|
|
4776
|
+
if (narrowed.length !== declared.length) {
|
|
4777
|
+
const unsupported = declared.filter((code) => !options.some((option) => option.value === code));
|
|
4778
|
+
console.error(`spiderly-data-table: column "${col.field}" declares match mode(s) [${unsupported.join(', ')}] that filterType "${col.filterType}" does not support — ignoring them.`);
|
|
4779
|
+
}
|
|
4780
|
+
if (!narrowed.length)
|
|
4781
|
+
return { options, defaultMode: typeDefault };
|
|
4782
|
+
return { options: narrowed, defaultMode: narrowed[0].value };
|
|
4783
|
+
}
|
|
4784
|
+
matchModeOptionsForType(filterType) {
|
|
4527
4785
|
switch (filterType) {
|
|
4528
4786
|
case 'text':
|
|
4529
|
-
return
|
|
4787
|
+
return this.matchModeTextOptions;
|
|
4530
4788
|
case 'date':
|
|
4531
4789
|
return this.matchModeDateOptions;
|
|
4532
4790
|
case 'multiselect':
|
|
@@ -4539,7 +4797,7 @@ class SpiderlyDataTableComponent {
|
|
|
4539
4797
|
return null;
|
|
4540
4798
|
}
|
|
4541
4799
|
}
|
|
4542
|
-
|
|
4800
|
+
defaultMatchModeForType(filterType) {
|
|
4543
4801
|
switch (filterType) {
|
|
4544
4802
|
case 'text':
|
|
4545
4803
|
return MatchModeCodes.Contains;
|
|
@@ -4563,6 +4821,30 @@ class SpiderlyDataTableComponent {
|
|
|
4563
4821
|
return false;
|
|
4564
4822
|
}
|
|
4565
4823
|
}
|
|
4824
|
+
/**
|
|
4825
|
+
* Whether the filter type applies on every value change, which hides the menu's Apply
|
|
4826
|
+
* button — an Apply there would promise a pending state that cannot exist. Boolean
|
|
4827
|
+
* auto-applies through PrimeNG's own `onModelChange`; date and dropdown/multiselect
|
|
4828
|
+
* through the filter templates THIS component projects, which call `filterCallback`
|
|
4829
|
+
* directly (PrimeNG's built-in element, and with it its `onModelChange`, is only
|
|
4830
|
+
* rendered when no template is projected — so the date path is ours, not PrimeNG's).
|
|
4831
|
+
* With showApplyButton off, PrimeNG also auto-applies match-mode, operator and
|
|
4832
|
+
* constraint-removal changes, so the whole menu stays consistent. Typed input
|
|
4833
|
+
* (text/numeric) is deliberately NOT here — it commits on Enter/Apply, since applying
|
|
4834
|
+
* per keystroke would fire a lazy load per key. Listing the auto types (not the typed
|
|
4835
|
+
* ones) is the safe polarity: an unlisted future type keeps its Apply button rather
|
|
4836
|
+
* than silently losing its commit.
|
|
4837
|
+
*
|
|
4838
|
+
* Known gap this does not close, and it predates the flag: the projected date template
|
|
4839
|
+
* binds `[ngModel]` one-way and commits on `(onSelect)`, which the datepicker raises for
|
|
4840
|
+
* a calendar pick but not for `onUserInput` — so a date TYPED into the field never
|
|
4841
|
+
* reaches the constraint at all, with or without an Apply button.
|
|
4842
|
+
*/
|
|
4843
|
+
filterAppliesOnChange(filterType) {
|
|
4844
|
+
return (filterType === 'boolean' ||
|
|
4845
|
+
filterType === 'date' ||
|
|
4846
|
+
this.isDropOrMulti(filterType));
|
|
4847
|
+
}
|
|
4566
4848
|
/*
|
|
4567
4849
|
* Navigate to details page based on rowId and rowNavigationPath.
|
|
4568
4850
|
* If rowNavigationPath is provided, it will navigate to that path with the rowId.
|
|
@@ -4675,8 +4957,10 @@ class SpiderlyDataTableComponent {
|
|
|
4675
4957
|
this.fakeIsAllSelected = false;
|
|
4676
4958
|
}
|
|
4677
4959
|
reload() {
|
|
4678
|
-
|
|
4679
|
-
this.
|
|
4960
|
+
// Nothing to replay before the table's own initial load, which is already on its way.
|
|
4961
|
+
if (this.lastLazyLoadEvent == null)
|
|
4962
|
+
return;
|
|
4963
|
+
// Not special: same refetch as a page flip, so lazyLoad owns both the flag and `items`.
|
|
4680
4964
|
this.lazyLoad(this.lastLazyLoadEvent);
|
|
4681
4965
|
}
|
|
4682
4966
|
showActions() {
|
|
@@ -4775,6 +5059,9 @@ class SpiderlyDataTableComponent {
|
|
|
4775
5059
|
clear(table) {
|
|
4776
5060
|
table.clear();
|
|
4777
5061
|
table.clearState();
|
|
5062
|
+
// clear() re-queries without emitting (onFilter), so the icons would keep their fill
|
|
5063
|
+
// on a client-side table; on a lazy one lazyLoad already covers it.
|
|
5064
|
+
this.snapshotAppliedFilters(table.filters);
|
|
4778
5065
|
}
|
|
4779
5066
|
//#region Selection
|
|
4780
5067
|
setFakeIsAllSelected() {
|
|
@@ -4947,8 +5234,8 @@ class SpiderlyDataTableComponent {
|
|
|
4947
5234
|
return null;
|
|
4948
5235
|
}
|
|
4949
5236
|
}
|
|
4950
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyDataTableComponent, deps: [{ token: i3$2.Router }, { token: i1$5.DialogService }, { token: i3$2.ActivatedRoute }, { token: SpiderlyMessageService }, { token: i1.TranslocoService }, { token: ConfigServiceBase }, { token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
4951
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: SpiderlyDataTableComponent, isStandalone: true, selector: "spiderly-data-table", inputs: { tableTitle: "tableTitle", tableIcon: "tableIcon", items: "items", rows: "rows", rowsPerPageOptions: "rowsPerPageOptions", cols: "cols", showPaginator: "showPaginator", showCardWrapper: "showCardWrapper", readonly: "readonly", idField: "idField", getPaginatedListObservableMethod: "getPaginatedListObservableMethod", exportListToExcelObservableMethod: "exportListToExcelObservableMethod", deleteItemFromTableObservableMethod: "deleteItemFromTableObservableMethod", deleteListFromTableObservableMethod: "deleteListFromTableObservableMethod", newlySelectedItems: "newlySelectedItems", unselectedItems: "unselectedItems", selectionMode: "selectionMode", selectedLazyLoadObservableMethod: "selectedLazyLoadObservableMethod", additionalFilterIdLong: "additionalFilterIdLong", showAddButton: "showAddButton", showExportToExcelButton: "showExportToExcelButton", showReloadTableButton: "showReloadTableButton", getFormArrayItems: "getFormArrayItems", hasLazyLoad: "hasLazyLoad", stateKey: "stateKey", stateStorage: "stateStorage", defaultSortField: "defaultSortField", defaultSortOrder: "defaultSortOrder", getAlreadySelectedItemIds: "getAlreadySelectedItemIds", getAlreadySelectedItems: "getAlreadySelectedItems", getFormControl: "getFormControl", additionalIndexes: "additionalIndexes", navigateOnRowClick: "navigateOnRowClick", rowNavigationPath: "rowNavigationPath" }, outputs: { onTotalRecordsChange: "onTotalRecordsChange", onLazyLoad: "onLazyLoad", onIsAllSelectedChange: "onIsAllSelectedChange", onRowSelect: "onRowSelect", onRowUnselect: "onRowUnselect" }, queries: [{ propertyName: "actionsTemplate", first: true, predicate: SpiderlyDataTableActionsDirective, descendants: true, read: TemplateRef }, { propertyName: "cellTemplates", predicate: SpiderlyCellTemplateDirective }], viewQueries: [{ propertyName: "table", first: true, predicate: ["dt"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n showCardWrapper ? 'card responsive-card-padding overflow-auto' : ''\n \"\n >\n <p-table\n #dt\n [value]=\"items\"\n [rows]=\"rows\"\n [rowsPerPageOptions]=\"rowsPerPageOptions\"\n [rowHover]=\"true\"\n [paginator]=\"showPaginator\"\n responsiveLayout=\"scroll\"\n [lazy]=\"hasLazyLoad\"\n (onLazyLoad)=\"lazyLoad($event)\"\n [totalRecords]=\"totalRecords\"\n class=\"spiderly-table\"\n [loading]=\"items === undefined || loading === true\"\n [selectionMode]=\"selectionMode\"\n dataKey=\"id\"\n (onFilter)=\"filter($event)\"\n sortMode=\"multiple\"\n [multiSortMeta]=\"initialMultiSortMeta\"\n [stateKey]=\"resolvedStateKey\"\n [stateStorage]=\"stateStorage\"\n >\n <ng-template pTemplate=\"caption\">\n <div class=\"table-header overflow-auto\">\n <div style=\"display: flex; align-items: center; gap: 8px\">\n <i class=\"{{ tableIcon }}\" style=\"font-size: 20px\"></i>\n <div style=\"margin: 0px; font-size: 17.5px\">{{ tableTitle }}</div>\n </div>\n <div style=\"display: flex; gap: 8px\">\n <ng-container\n *ngIf=\"actionsTemplate\"\n [ngTemplateOutlet]=\"actionsTemplate\"\n ></ng-container>\n <button\n pButton\n *ngIf=\"hasLazyLoad\"\n data-testid=\"column-chooser-button\"\n [label]=\"t('Columns')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-sliders-h\"\n (click)=\"columnChooser.toggle($event)\"\n ></button>\n <button\n pButton\n [label]=\"t('ClearFilters')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-filter-slash\"\n (click)=\"clear(dt)\"\n ></button>\n <button\n pButton\n *ngIf=\"showExportToExcelButton\"\n [label]=\"t('ExportToExcel')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-download\"\n (click)=\"exportListToExcel()\"\n ></button>\n <button\n pButton\n *ngIf=\"showReloadTableButton\"\n [label]=\"t('Reload')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-refresh\"\n (click)=\"reload()\"\n ></button>\n <button\n pButton\n *ngIf=\"deleteListFromTableObservableMethod && rowsSelectedNumber > 0\"\n [label]=\"t('DeleteSelected') + ' (' + rowsSelectedNumber + ')'\"\n class=\"p-button-danger\"\n style=\"flex: none\"\n icon=\"pi pi-trash\"\n (click)=\"deleteSelectedObjects()\"\n ></button>\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <th style=\"width: 0rem\" *ngIf=\"selectionMode == 'multiple'\">\n <div style=\"display: flex; gap: 8px\">\n <p-checkbox\n *ngIf=\"showSelectAllCheckbox\"\n [disabled]=\"readonly\"\n (onChange)=\"selectAll($event.checked)\"\n [(ngModel)]=\"fakeIsAllSelected\"\n [binary]=\"true\"\n ></p-checkbox>\n ({{ rowsSelectedNumber }})\n </div>\n </th>\n <ng-container *ngFor=\"let col of visibleCols; trackBy: colTrackByFn\">\n <th\n [pSortableColumn]=\"col.field\"\n [pSortableColumnDisabled]=\"!isColumnSortable(col)\"\n [style]=\"getColHeaderWidth(col)\"\n >\n <div\n style=\"\n display: flex;\n justify-content: space-between;\n align-items: center;\n \"\n >\n <span style=\"display: flex; align-items: center; gap: 4px\">\n {{ col.name }}\n <p-sortIcon\n *ngIf=\"isColumnSortable(col)\"\n [field]=\"col.field!\"\n ></p-sortIcon>\n </span>\n <p-columnFilter\n *ngIf=\"col.filterType != null && col.filterType !== 'blob'\"\n [type]=\"col.filterType\"\n [field]=\"col.filterField ?? col.field\"\n display=\"menu\"\n [placeholder]=\"col.filterPlaceholder\"\n [showOperator]=\"false\"\n [showMatchModes]=\"col.showMatchModes\"\n [showAddButton]=\"col.showAddButton\"\n [matchModeOptions]=\"getColMatchModeOptions(col.filterType)\"\n [matchMode]=\"getColMatchMode(col.filterType)\"\n >\n <ng-template\n *ngIf=\"isDropOrMulti(col.filterType)\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-multiSelect\n [ngModel]=\"value\"\n [options]=\"col.dropdownOrMultiselectValues\"\n [placeholder]=\"t('All')\"\n (onChange)=\"filter($event.value)\"\n optionLabel=\"label\"\n optionValue=\"code\"\n [style]=\"{ width: '240px' }\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"p-multiselect-representative-option\">\n <span class=\"ml-2\">{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-template>\n <ng-template\n *ngIf=\"col.filterType == 'date'\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-datepicker\n [ngModel]=\"value\"\n [showTime]=\"col.showTime\"\n (onSelect)=\"filter($event)\"\n ></p-datepicker>\n </ng-template>\n </p-columnFilter>\n </div>\n </th>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template\n pTemplate=\"body\"\n let-rowData\n let-index=\"rowIndex\"\n let-editing=\"editing\"\n >\n <!-- The cell rendering every column gets unless it projects a spiderlyCellTemplate. Declared\n once here rather than inside the column loop, and fed the same context the projected\n template gets, so getRowData is evaluated exactly once per cell either way. -->\n <ng-template #defaultCell let-col=\"col\" let-displayValue=\"displayValue\">\n <ng-container *ngIf=\"col.filterType === 'blob'\">\n <img width=\"45\" [src]=\"displayValue\" alt=\"\" />\n </ng-container>\n <ng-container *ngIf=\"col.filterType !== 'blob'\">\n {{ displayValue }}\n </ng-container>\n </ng-template>\n <tr\n [class.clickable]=\"navigateOnRowClick\"\n (click)=\"onRowClick(rowData, $event)\"\n >\n <!-- row-interactive: a selection gesture must never double as row navigation (onRowClick). -->\n <td\n *ngIf=\"selectionMode == 'multiple'\"\n class=\"selection-cell row-interactive\"\n (mousedown)=\"onSelectionCellMouseDown($event, rowData[idField])\"\n >\n <p-checkbox\n [disabled]=\"readonly\"\n (onChange)=\"selectRow(rowData[idField], rowData.index)\"\n [ngModel]=\"isRowSelected(rowData[idField])\"\n [binary]=\"true\"\n ></p-checkbox>\n </td>\n <ng-container *ngFor=\"let col of visibleCols; trackBy: colTrackByFn\">\n <td\n [style]=\"getStyleForBodyColumn(col)\"\n [class.clickable]=\"col.onCellClick\"\n [class.row-interactive]=\"col.onCellClick\"\n (click)=\"onCellClick(col, rowData, $event)\"\n *ngIf=\"!col.editable\"\n >\n <div\n style=\"\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 18px;\n \"\n >\n <ng-container\n *ngFor=\"let action of col.actions; trackBy: actionTrackByFn\"\n >\n <span\n [pTooltip]=\"action.name\"\n [class]=\"getClassForAction(action) + ' row-interactive'\"\n [style]=\"getStyleForAction(action)\"\n (click)=\"getMethodForAction(action, rowData, $event)\"\n ></span>\n </ng-container>\n </div>\n <ng-container\n [ngTemplateOutlet]=\"getCellTemplate(col) ?? defaultCell\"\n [ngTemplateOutletContext]=\"{\n $implicit: rowData,\n value: rowData[col.field],\n displayValue: getRowData(rowData, col),\n col: col\n }\"\n ></ng-container>\n </td>\n <td *ngIf=\"col.editable\" class=\"row-interactive\">\n <spiderly-number\n [control]=\"getFormArrayControlByIndex(col.field, rowData.index)\"\n [showLabel]=\"false\"\n ></spiderly-number>\n </td>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n <tr>\n <td\n [attr.colspan]=\"\n visibleCols.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"NoRecordsFound\") }}\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"loadingbody\">\n <tr>\n <td\n [attr.colspan]=\"\n visibleCols.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"Loading\") }}...\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"paginatorleft\">\n {{ t(\"TotalRecords\") }}: {{ totalRecords }}\n </ng-template>\n <ng-template pTemplate=\"paginatorright\">\n <div style=\"display: flex; justify-content: end; gap: 10px\">\n <spiderly-button\n *ngIf=\"showAddButton\"\n [label]=\"t('AddNew')\"\n icon=\"pi pi-plus\"\n (onClick)=\"navigateToDetails(0)\"\n ></spiderly-button>\n </div>\n </ng-template>\n </p-table>\n <p-popover #columnChooser>\n <div class=\"column-chooser\">\n <label\n *ngFor=\"let col of chooserCols; trackBy: colTrackByFn\"\n data-testid=\"column-chooser-option\"\n class=\"column-chooser-option\"\n >\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"isColumnVisible(col)\"\n [disabled]=\"!canToggleColumn(col)\"\n (onChange)=\"toggleColumn(col, $event.checked)\"\n ></p-checkbox>\n <span>{{ col.name }}</span>\n </label>\n <button\n type=\"button\"\n data-testid=\"column-chooser-reset\"\n class=\"column-chooser-reset\"\n (click)=\"resetColumnVisibility()\"\n >\n {{ t('ResetToDefault') }}\n </button>\n </div>\n </p-popover>\n </div>\n</ng-container>\n", styles: [".table-header{display:flex;justify-content:space-between;align-items:center}@media (max-width: 640px){.table-header{display:flex;flex-direction:column;align-items:start;gap:14px}}.spiderly-table .p-paginator{padding:1rem}@media (min-width: 1400px){.spiderly-table .p-paginator-left-content{position:absolute;padding:14px;left:0}}@media (min-width: 1400px){.spiderly-table .p-paginator-right-content{position:absolute;padding:14px;right:0}}.column-chooser{display:flex;flex-direction:column;gap:8px}.column-chooser-option{display:flex;align-items:center;gap:8px;cursor:pointer}.column-chooser-reset{margin-top:4px;padding:0;border:none;background:none;color:var(--p-primary-color);cursor:pointer;align-self:flex-start;font-size:.875rem}.column-chooser-reset:hover{text-decoration:underline}:host ::ng-deep .clickable{cursor:pointer}:host ::ng-deep td.clickable:hover{text-decoration:underline}:host td.selection-cell{-webkit-user-select:none;user-select:none}:host ::ng-deep .p-datatable-thead{position:unset!important}:host ::ng-deep .p-datatable-header{border-radius:6px 6px 0 0}:host ::ng-deep .p-datatable{border-radius:var(--p-content-border-radius);border:1px solid var(--p-datatable-border-color)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: SpiderlyControlsModule }, { kind: "component", type: SpiderlyButtonComponent, selector: "spiderly-button", inputs: ["type"] }, { kind: "component", type: SpiderlyNumberComponent, selector: "spiderly-number", inputs: ["prefix", "showButtons", "decimal", "maxFractionDigits"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i10.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "virtualRowHeight", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i10.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i10.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "component", type: i10.ColumnFilter, selector: "p-columnFilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "useGrouping", "showButtons", "ariaLabel", "filterButtonProps"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i12.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i4$5.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "fluid", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "size", "showClear", "autofocus", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i14.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i4$1.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "fluid", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "size", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$2.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }] }); }
|
|
5237
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyDataTableComponent, deps: [{ token: i3$2.Router }, { token: i1$5.DialogService }, { token: i3$2.ActivatedRoute }, { token: SpiderlyMessageService }, { token: i1.TranslocoService }, { token: ConfigServiceBase }, { token: i0.ErrorHandler }, { token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5238
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: SpiderlyDataTableComponent, isStandalone: true, selector: "spiderly-data-table", inputs: { tableTitle: "tableTitle", tableIcon: "tableIcon", items: "items", rows: "rows", rowsPerPageOptions: "rowsPerPageOptions", cols: "cols", showPaginator: "showPaginator", showCardWrapper: "showCardWrapper", readonly: "readonly", idField: "idField", getPaginatedListObservableMethod: "getPaginatedListObservableMethod", exportListToExcelObservableMethod: "exportListToExcelObservableMethod", deleteItemFromTableObservableMethod: "deleteItemFromTableObservableMethod", deleteListFromTableObservableMethod: "deleteListFromTableObservableMethod", newlySelectedItems: "newlySelectedItems", unselectedItems: "unselectedItems", selectionMode: "selectionMode", selectedLazyLoadObservableMethod: "selectedLazyLoadObservableMethod", additionalFilterIdLong: "additionalFilterIdLong", showAddButton: "showAddButton", showExportToExcelButton: "showExportToExcelButton", showReloadTableButton: "showReloadTableButton", getFormArrayItems: "getFormArrayItems", hasLazyLoad: "hasLazyLoad", stateKey: "stateKey", stateStorage: "stateStorage", defaultSortField: "defaultSortField", defaultSortOrder: "defaultSortOrder", getAlreadySelectedItemIds: "getAlreadySelectedItemIds", getAlreadySelectedItems: "getAlreadySelectedItems", getFormControl: "getFormControl", additionalIndexes: "additionalIndexes", navigateOnRowClick: "navigateOnRowClick", rowNavigationPath: "rowNavigationPath" }, outputs: { onTotalRecordsChange: "onTotalRecordsChange", onLazyLoad: "onLazyLoad", onIsAllSelectedChange: "onIsAllSelectedChange", onRowSelect: "onRowSelect", onRowUnselect: "onRowUnselect" }, queries: [{ propertyName: "actionsTemplate", first: true, predicate: SpiderlyDataTableActionsDirective, descendants: true, read: TemplateRef }, { propertyName: "cellTemplates", predicate: SpiderlyCellTemplateDirective }], viewQueries: [{ propertyName: "table", first: true, predicate: ["dt"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div\n #tableContainer\n class=\"spiderly-table-container\"\n [attr.aria-busy]=\"isPending\"\n [ngClass]=\"{\n 'card responsive-card-padding overflow-auto': showCardWrapper\n }\"\n >\n <p-table\n #dt\n [value]=\"items\"\n [rows]=\"rows\"\n [rowsPerPageOptions]=\"rowsPerPageOptions\"\n [rowHover]=\"true\"\n [paginator]=\"showPaginator\"\n responsiveLayout=\"scroll\"\n [lazy]=\"hasLazyLoad\"\n (onLazyLoad)=\"lazyLoad($event)\"\n (onPage)=\"onPageChange(tableContainer)\"\n [totalRecords]=\"totalRecords\"\n class=\"spiderly-table\"\n [loading]=\"isPending\"\n [selectionMode]=\"selectionMode\"\n dataKey=\"id\"\n (onFilter)=\"filter($event)\"\n sortMode=\"multiple\"\n [multiSortMeta]=\"initialMultiSortMeta\"\n [stateKey]=\"resolvedStateKey\"\n [stateStorage]=\"stateStorage\"\n [tableStyle]=\"tableStyle\"\n >\n <ng-template pTemplate=\"caption\">\n <div class=\"table-header overflow-auto\">\n <div style=\"display: flex; align-items: center; gap: 8px\">\n <i class=\"{{ tableIcon }}\" style=\"font-size: 20px\"></i>\n <div style=\"margin: 0px; font-size: 17.5px\">{{ tableTitle }}</div>\n </div>\n <div style=\"display: flex; gap: 8px\">\n <ng-container\n *ngIf=\"actionsTemplate\"\n [ngTemplateOutlet]=\"actionsTemplate\"\n ></ng-container>\n <button\n pButton\n *ngIf=\"hasLazyLoad\"\n data-testid=\"column-chooser-button\"\n [label]=\"t('Columns')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-sliders-h\"\n (click)=\"columnChooser.toggle($event)\"\n ></button>\n <button\n pButton\n [label]=\"t('ClearFilters')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-filter-slash\"\n (click)=\"clear(dt)\"\n ></button>\n <button\n pButton\n *ngIf=\"showExportToExcelButton\"\n [label]=\"t('ExportToExcel')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-download\"\n (click)=\"exportListToExcel()\"\n ></button>\n <button\n pButton\n *ngIf=\"showReloadTableButton\"\n [label]=\"t('Reload')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-refresh\"\n (click)=\"reload()\"\n ></button>\n <button\n pButton\n *ngIf=\"deleteListFromTableObservableMethod && rowsSelectedNumber > 0\"\n [label]=\"t('DeleteSelected') + ' (' + rowsSelectedNumber + ')'\"\n class=\"p-button-danger\"\n style=\"flex: none\"\n icon=\"pi pi-trash\"\n (click)=\"deleteSelectedObjects()\"\n ></button>\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <!-- Sized for the checkbox plus the counter beside it, which shows `totalRecords` on a\n select-all and so reaches five digits on a large table. Not `0rem` (see CLAUDE.md \u2192\n Column widths), and not routed through getColWidth: this column has no `Column`. -->\n <th style=\"width: 6rem\" *ngIf=\"selectionMode == 'multiple'\">\n <div style=\"display: flex; gap: 8px\">\n <p-checkbox\n *ngIf=\"showSelectAllCheckbox\"\n [disabled]=\"readonly\"\n (onChange)=\"selectAll($event.checked)\"\n [(ngModel)]=\"fakeIsAllSelected\"\n [binary]=\"true\"\n ></p-checkbox>\n ({{ rowsSelectedNumber }})\n </div>\n </th>\n <ng-container *ngFor=\"let col of visibleCols; trackBy: colTrackByFn\">\n <th\n [pSortableColumn]=\"col.field\"\n [pSortableColumnDisabled]=\"!isColumnSortable(col)\"\n [style.width]=\"getColWidth(col)\"\n >\n <div\n style=\"\n display: flex;\n justify-content: space-between;\n align-items: center;\n \"\n >\n <span style=\"display: flex; align-items: center; gap: 4px\">\n {{ col.name }}\n <p-sortIcon\n *ngIf=\"isColumnSortable(col)\"\n [field]=\"col.field!\"\n ></p-sortIcon>\n </span>\n <!-- hideOnClear: Apply is what closes this menu in PrimeNG (applyFilter =\n _filter + hide), so wherever Apply is hidden Clear is the only button\n left and would otherwise clear-and-linger. Committing a VALUE still\n leaves the menu open deliberately \u2014 a multiselect gets ticked several\n times per visit; dismissal is the popover's own (outside click, Esc). -->\n <p-columnFilter\n *ngIf=\"col.filterType != null && col.filterType !== 'blob'\"\n [type]=\"col.filterType\"\n [field]=\"filterKey(col)\"\n display=\"menu\"\n [placeholder]=\"col.filterPlaceholder\"\n [showOperator]=\"false\"\n [showMatchModes]=\"col.showMatchModes\"\n [showAddButton]=\"col.showAddButton\"\n [showApplyButton]=\"!filterAppliesOnChange(col.filterType)\"\n [hideOnClear]=\"true\"\n [matchModeOptions]=\"getColMatchModeOptions(col)\"\n [matchMode]=\"getColMatchMode(col)\"\n >\n <!-- Marks a filtered column \u2014 PrimeNG's own <FilterIcon> cannot\n (CLAUDE.md \u2192 \"Active-filter header icon\"). Colour: the SCSS. -->\n <ng-template pTemplate=\"filtericon\">\n <i\n [class]=\"\n isColumnFiltered(col) ? 'pi pi-filter-fill' : 'pi pi-filter'\n \"\n ></i>\n </ng-template>\n <ng-template\n *ngIf=\"isDropOrMulti(col.filterType)\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-multiSelect\n [ngModel]=\"value\"\n [options]=\"col.dropdownOrMultiselectValues\"\n [placeholder]=\"t('All')\"\n (onChange)=\"filter($event.value)\"\n optionLabel=\"label\"\n optionValue=\"code\"\n [style]=\"{ width: '240px' }\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"p-multiselect-representative-option\">\n <span class=\"ml-2\">{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-template>\n <ng-template\n *ngIf=\"col.filterType == 'date'\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-datepicker\n [ngModel]=\"value\"\n [showTime]=\"col.showTime\"\n (onSelect)=\"filter($event)\"\n ></p-datepicker>\n </ng-template>\n </p-columnFilter>\n </div>\n </th>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template\n pTemplate=\"body\"\n let-rowData\n let-index=\"rowIndex\"\n let-editing=\"editing\"\n >\n <!-- The cell rendering every column gets unless it projects a spiderlyCellTemplate. Declared\n once here rather than inside the column loop, and fed the same context the projected\n template gets, so getRowData is evaluated exactly once per cell either way. -->\n <ng-template #defaultCell let-col=\"col\" let-displayValue=\"displayValue\">\n <ng-container *ngIf=\"col.filterType === 'blob'\">\n <img width=\"45\" [src]=\"displayValue\" alt=\"\" />\n </ng-container>\n <ng-container *ngIf=\"col.filterType !== 'blob'\">\n <span class=\"cell-text\">{{ displayValue }}</span>\n </ng-container>\n </ng-template>\n <tr\n [class.clickable]=\"navigateOnRowClick\"\n (click)=\"onRowClick(rowData, $event)\"\n >\n <!-- row-interactive: a selection gesture must never double as row navigation (onRowClick). -->\n <td\n *ngIf=\"selectionMode == 'multiple'\"\n class=\"selection-cell row-interactive\"\n (mousedown)=\"onSelectionCellMouseDown($event, rowData[idField])\"\n >\n <p-checkbox\n [disabled]=\"readonly\"\n (onChange)=\"selectRow(rowData[idField], rowData.index)\"\n [ngModel]=\"isRowSelected(rowData[idField])\"\n [binary]=\"true\"\n ></p-checkbox>\n </td>\n <ng-container *ngFor=\"let col of visibleCols; trackBy: colTrackByFn\">\n <td\n [style]=\"getStyleForBodyColumn(col)\"\n [class.clickable]=\"col.onCellClick\"\n [class.row-interactive]=\"col.onCellClick\"\n (click)=\"onCellClick(col, rowData, $event)\"\n *ngIf=\"!col.editable\"\n >\n <!-- getColWidth reserves this column's width from `actions.length`; this gap is\n the geometry it reserves for, so changing it belongs in the same edit. -->\n <div\n style=\"\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 18px;\n \"\n >\n <ng-container\n *ngFor=\"let action of col.actions; trackBy: actionTrackByFn\"\n >\n <span\n [pTooltip]=\"action.name\"\n [class]=\"getClassForAction(action) + ' row-interactive'\"\n [style]=\"getStyleForAction(action)\"\n (click)=\"getMethodForAction(action, rowData, $event)\"\n ></span>\n </ng-container>\n </div>\n <ng-container\n [ngTemplateOutlet]=\"getCellTemplate(col) ?? defaultCell\"\n [ngTemplateOutletContext]=\"{\n $implicit: rowData,\n value: rowData[col.field],\n displayValue: getRowData(rowData, col),\n col: col\n }\"\n ></ng-container>\n </td>\n <td *ngIf=\"col.editable\" class=\"row-interactive\">\n <spiderly-number\n [control]=\"getFormArrayControlByIndex(col.field, rowData.index)\"\n [showLabel]=\"false\"\n ></spiderly-number>\n </td>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n <tr>\n <td\n [attr.colspan]=\"\n visibleCols.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"NoRecordsFound\") }}\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"paginatorleft\">\n {{ t(\"TotalRecords\") }}: {{ totalRecords }}\n </ng-template>\n <ng-template pTemplate=\"paginatorright\">\n <div style=\"display: flex; justify-content: end; gap: 10px\">\n <spiderly-button\n *ngIf=\"showAddButton\"\n [label]=\"t('AddNew')\"\n icon=\"pi pi-plus\"\n (onClick)=\"navigateToDetails(0)\"\n ></spiderly-button>\n </div>\n </ng-template>\n </p-table>\n <p-popover #columnChooser>\n <div class=\"column-chooser\">\n <label\n *ngFor=\"let col of chooserCols; trackBy: colTrackByFn\"\n data-testid=\"column-chooser-option\"\n class=\"column-chooser-option\"\n >\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"isColumnVisible(col)\"\n [disabled]=\"!canToggleColumn(col)\"\n (onChange)=\"toggleColumn(col, $event.checked)\"\n ></p-checkbox>\n <span>{{ col.name }}</span>\n </label>\n <button\n type=\"button\"\n data-testid=\"column-chooser-reset\"\n class=\"column-chooser-reset\"\n (click)=\"resetColumnVisibility()\"\n >\n {{ t('ResetToDefault') }}\n </button>\n </div>\n </p-popover>\n </div>\n</ng-container>\n", styles: [".table-header{display:flex;justify-content:space-between;align-items:center}.pi-filter-fill{color:var(--p-primary-color)}@media (max-width: 640px){.table-header{display:flex;flex-direction:column;align-items:start;gap:14px}}.spiderly-table .p-paginator{padding:1rem}@media (min-width: 1400px){.spiderly-table .p-paginator-left-content{position:absolute;padding:14px;left:0}}@media (min-width: 1400px){.spiderly-table .p-paginator-right-content{position:absolute;padding:14px;right:0}}.column-chooser{display:flex;flex-direction:column;gap:8px}.column-chooser-option{display:flex;align-items:center;gap:8px;cursor:pointer}.column-chooser-reset{margin-top:4px;padding:0;border:none;background:none;color:var(--p-primary-color);cursor:pointer;align-self:flex-start;font-size:.875rem}.column-chooser-reset:hover{text-decoration:underline}:host .spiderly-table-container{scroll-margin-top:calc(var(--spiderly-viewport-top-inset, 0px) + 1rem)}:host ::ng-deep .clickable{cursor:pointer}:host ::ng-deep td.clickable:hover{text-decoration:underline}:host td.selection-cell{-webkit-user-select:none;user-select:none}:host .cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ::ng-deep .p-datatable-mask{background:color-mix(in srgb,var(--p-content-background, #ffffff) 65%,transparent);color:var(--p-primary-color, #334155);align-items:flex-start;padding-top:6rem}:host ::ng-deep .p-datatable-thead{position:unset!important}:host ::ng-deep .p-datatable-header{border-radius:6px 6px 0 0}:host ::ng-deep .p-datatable{border-radius:var(--p-content-border-radius);border:1px solid var(--p-datatable-border-color)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: SpiderlyControlsModule }, { kind: "component", type: SpiderlyButtonComponent, selector: "spiderly-button", inputs: ["type"] }, { kind: "component", type: SpiderlyNumberComponent, selector: "spiderly-number", inputs: ["prefix", "showButtons", "decimal", "maxFractionDigits"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i10.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "virtualRowHeight", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "directive", type: i10.SortableColumn, selector: "[pSortableColumn]", inputs: ["pSortableColumn", "pSortableColumnDisabled"] }, { kind: "component", type: i10.SortIcon, selector: "p-sortIcon", inputs: ["field"] }, { kind: "component", type: i10.ColumnFilter, selector: "p-columnFilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "useGrouping", "showButtons", "ariaLabel", "filterButtonProps"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i12.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i4$5.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "fluid", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "size", "showClear", "autofocus", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i14.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: DatePickerModule }, { kind: "component", type: i4$1.DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "style", "styleClass", "inputStyle", "inputId", "name", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "disabled", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "fluid", "icon", "appendTo", "readonlyInput", "shortYearCutoff", "monthNavigator", "yearNavigator", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "required", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "variant", "size", "minDate", "maxDate", "disabledDates", "disabledDays", "yearRange", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "locale", "view", "defaultDate"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$2.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }] }); }
|
|
4952
5239
|
}
|
|
4953
5240
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyDataTableComponent, decorators: [{
|
|
4954
5241
|
type: Component,
|
|
@@ -4964,8 +5251,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
|
|
|
4964
5251
|
DatePickerModule,
|
|
4965
5252
|
CheckboxModule,
|
|
4966
5253
|
TooltipModule,
|
|
4967
|
-
], template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n showCardWrapper ? 'card responsive-card-padding overflow-auto' : ''\n \"\n >\n <p-table\n #dt\n [value]=\"items\"\n [rows]=\"rows\"\n [rowsPerPageOptions]=\"rowsPerPageOptions\"\n [rowHover]=\"true\"\n [paginator]=\"showPaginator\"\n responsiveLayout=\"scroll\"\n [lazy]=\"hasLazyLoad\"\n (onLazyLoad)=\"lazyLoad($event)\"\n [totalRecords]=\"totalRecords\"\n class=\"spiderly-table\"\n [loading]=\"items === undefined || loading === true\"\n [selectionMode]=\"selectionMode\"\n dataKey=\"id\"\n (onFilter)=\"filter($event)\"\n sortMode=\"multiple\"\n [multiSortMeta]=\"initialMultiSortMeta\"\n [stateKey]=\"resolvedStateKey\"\n [stateStorage]=\"stateStorage\"\n >\n <ng-template pTemplate=\"caption\">\n <div class=\"table-header overflow-auto\">\n <div style=\"display: flex; align-items: center; gap: 8px\">\n <i class=\"{{ tableIcon }}\" style=\"font-size: 20px\"></i>\n <div style=\"margin: 0px; font-size: 17.5px\">{{ tableTitle }}</div>\n </div>\n <div style=\"display: flex; gap: 8px\">\n <ng-container\n *ngIf=\"actionsTemplate\"\n [ngTemplateOutlet]=\"actionsTemplate\"\n ></ng-container>\n <button\n pButton\n *ngIf=\"hasLazyLoad\"\n data-testid=\"column-chooser-button\"\n [label]=\"t('Columns')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-sliders-h\"\n (click)=\"columnChooser.toggle($event)\"\n ></button>\n <button\n pButton\n [label]=\"t('ClearFilters')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-filter-slash\"\n (click)=\"clear(dt)\"\n ></button>\n <button\n pButton\n *ngIf=\"showExportToExcelButton\"\n [label]=\"t('ExportToExcel')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-download\"\n (click)=\"exportListToExcel()\"\n ></button>\n <button\n pButton\n *ngIf=\"showReloadTableButton\"\n [label]=\"t('Reload')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-refresh\"\n (click)=\"reload()\"\n ></button>\n <button\n pButton\n *ngIf=\"deleteListFromTableObservableMethod && rowsSelectedNumber > 0\"\n [label]=\"t('DeleteSelected') + ' (' + rowsSelectedNumber + ')'\"\n class=\"p-button-danger\"\n style=\"flex: none\"\n icon=\"pi pi-trash\"\n (click)=\"deleteSelectedObjects()\"\n ></button>\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <th style=\"width: 0rem\" *ngIf=\"selectionMode == 'multiple'\">\n <div style=\"display: flex; gap: 8px\">\n <p-checkbox\n *ngIf=\"showSelectAllCheckbox\"\n [disabled]=\"readonly\"\n (onChange)=\"selectAll($event.checked)\"\n [(ngModel)]=\"fakeIsAllSelected\"\n [binary]=\"true\"\n ></p-checkbox>\n ({{ rowsSelectedNumber }})\n </div>\n </th>\n <ng-container *ngFor=\"let col of visibleCols; trackBy: colTrackByFn\">\n <th\n [pSortableColumn]=\"col.field\"\n [pSortableColumnDisabled]=\"!isColumnSortable(col)\"\n [style]=\"getColHeaderWidth(col)\"\n >\n <div\n style=\"\n display: flex;\n justify-content: space-between;\n align-items: center;\n \"\n >\n <span style=\"display: flex; align-items: center; gap: 4px\">\n {{ col.name }}\n <p-sortIcon\n *ngIf=\"isColumnSortable(col)\"\n [field]=\"col.field!\"\n ></p-sortIcon>\n </span>\n <p-columnFilter\n *ngIf=\"col.filterType != null && col.filterType !== 'blob'\"\n [type]=\"col.filterType\"\n [field]=\"col.filterField ?? col.field\"\n display=\"menu\"\n [placeholder]=\"col.filterPlaceholder\"\n [showOperator]=\"false\"\n [showMatchModes]=\"col.showMatchModes\"\n [showAddButton]=\"col.showAddButton\"\n [matchModeOptions]=\"getColMatchModeOptions(col.filterType)\"\n [matchMode]=\"getColMatchMode(col.filterType)\"\n >\n <ng-template\n *ngIf=\"isDropOrMulti(col.filterType)\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-multiSelect\n [ngModel]=\"value\"\n [options]=\"col.dropdownOrMultiselectValues\"\n [placeholder]=\"t('All')\"\n (onChange)=\"filter($event.value)\"\n optionLabel=\"label\"\n optionValue=\"code\"\n [style]=\"{ width: '240px' }\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"p-multiselect-representative-option\">\n <span class=\"ml-2\">{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-template>\n <ng-template\n *ngIf=\"col.filterType == 'date'\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-datepicker\n [ngModel]=\"value\"\n [showTime]=\"col.showTime\"\n (onSelect)=\"filter($event)\"\n ></p-datepicker>\n </ng-template>\n </p-columnFilter>\n </div>\n </th>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template\n pTemplate=\"body\"\n let-rowData\n let-index=\"rowIndex\"\n let-editing=\"editing\"\n >\n <!-- The cell rendering every column gets unless it projects a spiderlyCellTemplate. Declared\n once here rather than inside the column loop, and fed the same context the projected\n template gets, so getRowData is evaluated exactly once per cell either way. -->\n <ng-template #defaultCell let-col=\"col\" let-displayValue=\"displayValue\">\n <ng-container *ngIf=\"col.filterType === 'blob'\">\n <img width=\"45\" [src]=\"displayValue\" alt=\"\" />\n </ng-container>\n <ng-container *ngIf=\"col.filterType !== 'blob'\">\n {{ displayValue }}\n </ng-container>\n </ng-template>\n <tr\n [class.clickable]=\"navigateOnRowClick\"\n (click)=\"onRowClick(rowData, $event)\"\n >\n <!-- row-interactive: a selection gesture must never double as row navigation (onRowClick). -->\n <td\n *ngIf=\"selectionMode == 'multiple'\"\n class=\"selection-cell row-interactive\"\n (mousedown)=\"onSelectionCellMouseDown($event, rowData[idField])\"\n >\n <p-checkbox\n [disabled]=\"readonly\"\n (onChange)=\"selectRow(rowData[idField], rowData.index)\"\n [ngModel]=\"isRowSelected(rowData[idField])\"\n [binary]=\"true\"\n ></p-checkbox>\n </td>\n <ng-container *ngFor=\"let col of visibleCols; trackBy: colTrackByFn\">\n <td\n [style]=\"getStyleForBodyColumn(col)\"\n [class.clickable]=\"col.onCellClick\"\n [class.row-interactive]=\"col.onCellClick\"\n (click)=\"onCellClick(col, rowData, $event)\"\n *ngIf=\"!col.editable\"\n >\n <div\n style=\"\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 18px;\n \"\n >\n <ng-container\n *ngFor=\"let action of col.actions; trackBy: actionTrackByFn\"\n >\n <span\n [pTooltip]=\"action.name\"\n [class]=\"getClassForAction(action) + ' row-interactive'\"\n [style]=\"getStyleForAction(action)\"\n (click)=\"getMethodForAction(action, rowData, $event)\"\n ></span>\n </ng-container>\n </div>\n <ng-container\n [ngTemplateOutlet]=\"getCellTemplate(col) ?? defaultCell\"\n [ngTemplateOutletContext]=\"{\n $implicit: rowData,\n value: rowData[col.field],\n displayValue: getRowData(rowData, col),\n col: col\n }\"\n ></ng-container>\n </td>\n <td *ngIf=\"col.editable\" class=\"row-interactive\">\n <spiderly-number\n [control]=\"getFormArrayControlByIndex(col.field, rowData.index)\"\n [showLabel]=\"false\"\n ></spiderly-number>\n </td>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n <tr>\n <td\n [attr.colspan]=\"\n visibleCols.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"NoRecordsFound\") }}\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"loadingbody\">\n <tr>\n <td\n [attr.colspan]=\"\n visibleCols.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"Loading\") }}...\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"paginatorleft\">\n {{ t(\"TotalRecords\") }}: {{ totalRecords }}\n </ng-template>\n <ng-template pTemplate=\"paginatorright\">\n <div style=\"display: flex; justify-content: end; gap: 10px\">\n <spiderly-button\n *ngIf=\"showAddButton\"\n [label]=\"t('AddNew')\"\n icon=\"pi pi-plus\"\n (onClick)=\"navigateToDetails(0)\"\n ></spiderly-button>\n </div>\n </ng-template>\n </p-table>\n <p-popover #columnChooser>\n <div class=\"column-chooser\">\n <label\n *ngFor=\"let col of chooserCols; trackBy: colTrackByFn\"\n data-testid=\"column-chooser-option\"\n class=\"column-chooser-option\"\n >\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"isColumnVisible(col)\"\n [disabled]=\"!canToggleColumn(col)\"\n (onChange)=\"toggleColumn(col, $event.checked)\"\n ></p-checkbox>\n <span>{{ col.name }}</span>\n </label>\n <button\n type=\"button\"\n data-testid=\"column-chooser-reset\"\n class=\"column-chooser-reset\"\n (click)=\"resetColumnVisibility()\"\n >\n {{ t('ResetToDefault') }}\n </button>\n </div>\n </p-popover>\n </div>\n</ng-container>\n", styles: [".table-header{display:flex;justify-content:space-between;align-items:center}@media (max-width: 640px){.table-header{display:flex;flex-direction:column;align-items:start;gap:14px}}.spiderly-table .p-paginator{padding:1rem}@media (min-width: 1400px){.spiderly-table .p-paginator-left-content{position:absolute;padding:14px;left:0}}@media (min-width: 1400px){.spiderly-table .p-paginator-right-content{position:absolute;padding:14px;right:0}}.column-chooser{display:flex;flex-direction:column;gap:8px}.column-chooser-option{display:flex;align-items:center;gap:8px;cursor:pointer}.column-chooser-reset{margin-top:4px;padding:0;border:none;background:none;color:var(--p-primary-color);cursor:pointer;align-self:flex-start;font-size:.875rem}.column-chooser-reset:hover{text-decoration:underline}:host ::ng-deep .clickable{cursor:pointer}:host ::ng-deep td.clickable:hover{text-decoration:underline}:host td.selection-cell{-webkit-user-select:none;user-select:none}:host ::ng-deep .p-datatable-thead{position:unset!important}:host ::ng-deep .p-datatable-header{border-radius:6px 6px 0 0}:host ::ng-deep .p-datatable{border-radius:var(--p-content-border-radius);border:1px solid var(--p-datatable-border-color)}\n"] }]
|
|
4968
|
-
}], ctorParameters: () => [{ type: i3$2.Router }, { type: i1$5.DialogService }, { type: i3$2.ActivatedRoute }, { type: SpiderlyMessageService }, { type: i1.TranslocoService }, { type: ConfigServiceBase }, { type: undefined, decorators: [{
|
|
5254
|
+
], template: "<ng-container *transloco=\"let t\">\n <div\n #tableContainer\n class=\"spiderly-table-container\"\n [attr.aria-busy]=\"isPending\"\n [ngClass]=\"{\n 'card responsive-card-padding overflow-auto': showCardWrapper\n }\"\n >\n <p-table\n #dt\n [value]=\"items\"\n [rows]=\"rows\"\n [rowsPerPageOptions]=\"rowsPerPageOptions\"\n [rowHover]=\"true\"\n [paginator]=\"showPaginator\"\n responsiveLayout=\"scroll\"\n [lazy]=\"hasLazyLoad\"\n (onLazyLoad)=\"lazyLoad($event)\"\n (onPage)=\"onPageChange(tableContainer)\"\n [totalRecords]=\"totalRecords\"\n class=\"spiderly-table\"\n [loading]=\"isPending\"\n [selectionMode]=\"selectionMode\"\n dataKey=\"id\"\n (onFilter)=\"filter($event)\"\n sortMode=\"multiple\"\n [multiSortMeta]=\"initialMultiSortMeta\"\n [stateKey]=\"resolvedStateKey\"\n [stateStorage]=\"stateStorage\"\n [tableStyle]=\"tableStyle\"\n >\n <ng-template pTemplate=\"caption\">\n <div class=\"table-header overflow-auto\">\n <div style=\"display: flex; align-items: center; gap: 8px\">\n <i class=\"{{ tableIcon }}\" style=\"font-size: 20px\"></i>\n <div style=\"margin: 0px; font-size: 17.5px\">{{ tableTitle }}</div>\n </div>\n <div style=\"display: flex; gap: 8px\">\n <ng-container\n *ngIf=\"actionsTemplate\"\n [ngTemplateOutlet]=\"actionsTemplate\"\n ></ng-container>\n <button\n pButton\n *ngIf=\"hasLazyLoad\"\n data-testid=\"column-chooser-button\"\n [label]=\"t('Columns')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-sliders-h\"\n (click)=\"columnChooser.toggle($event)\"\n ></button>\n <button\n pButton\n [label]=\"t('ClearFilters')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-filter-slash\"\n (click)=\"clear(dt)\"\n ></button>\n <button\n pButton\n *ngIf=\"showExportToExcelButton\"\n [label]=\"t('ExportToExcel')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-download\"\n (click)=\"exportListToExcel()\"\n ></button>\n <button\n pButton\n *ngIf=\"showReloadTableButton\"\n [label]=\"t('Reload')\"\n class=\"p-button-outlined\"\n style=\"flex: none\"\n icon=\"pi pi-refresh\"\n (click)=\"reload()\"\n ></button>\n <button\n pButton\n *ngIf=\"deleteListFromTableObservableMethod && rowsSelectedNumber > 0\"\n [label]=\"t('DeleteSelected') + ' (' + rowsSelectedNumber + ')'\"\n class=\"p-button-danger\"\n style=\"flex: none\"\n icon=\"pi pi-trash\"\n (click)=\"deleteSelectedObjects()\"\n ></button>\n </div>\n </div>\n </ng-template>\n <ng-template pTemplate=\"header\">\n <tr>\n <!-- Sized for the checkbox plus the counter beside it, which shows `totalRecords` on a\n select-all and so reaches five digits on a large table. Not `0rem` (see CLAUDE.md \u2192\n Column widths), and not routed through getColWidth: this column has no `Column`. -->\n <th style=\"width: 6rem\" *ngIf=\"selectionMode == 'multiple'\">\n <div style=\"display: flex; gap: 8px\">\n <p-checkbox\n *ngIf=\"showSelectAllCheckbox\"\n [disabled]=\"readonly\"\n (onChange)=\"selectAll($event.checked)\"\n [(ngModel)]=\"fakeIsAllSelected\"\n [binary]=\"true\"\n ></p-checkbox>\n ({{ rowsSelectedNumber }})\n </div>\n </th>\n <ng-container *ngFor=\"let col of visibleCols; trackBy: colTrackByFn\">\n <th\n [pSortableColumn]=\"col.field\"\n [pSortableColumnDisabled]=\"!isColumnSortable(col)\"\n [style.width]=\"getColWidth(col)\"\n >\n <div\n style=\"\n display: flex;\n justify-content: space-between;\n align-items: center;\n \"\n >\n <span style=\"display: flex; align-items: center; gap: 4px\">\n {{ col.name }}\n <p-sortIcon\n *ngIf=\"isColumnSortable(col)\"\n [field]=\"col.field!\"\n ></p-sortIcon>\n </span>\n <!-- hideOnClear: Apply is what closes this menu in PrimeNG (applyFilter =\n _filter + hide), so wherever Apply is hidden Clear is the only button\n left and would otherwise clear-and-linger. Committing a VALUE still\n leaves the menu open deliberately \u2014 a multiselect gets ticked several\n times per visit; dismissal is the popover's own (outside click, Esc). -->\n <p-columnFilter\n *ngIf=\"col.filterType != null && col.filterType !== 'blob'\"\n [type]=\"col.filterType\"\n [field]=\"filterKey(col)\"\n display=\"menu\"\n [placeholder]=\"col.filterPlaceholder\"\n [showOperator]=\"false\"\n [showMatchModes]=\"col.showMatchModes\"\n [showAddButton]=\"col.showAddButton\"\n [showApplyButton]=\"!filterAppliesOnChange(col.filterType)\"\n [hideOnClear]=\"true\"\n [matchModeOptions]=\"getColMatchModeOptions(col)\"\n [matchMode]=\"getColMatchMode(col)\"\n >\n <!-- Marks a filtered column \u2014 PrimeNG's own <FilterIcon> cannot\n (CLAUDE.md \u2192 \"Active-filter header icon\"). Colour: the SCSS. -->\n <ng-template pTemplate=\"filtericon\">\n <i\n [class]=\"\n isColumnFiltered(col) ? 'pi pi-filter-fill' : 'pi pi-filter'\n \"\n ></i>\n </ng-template>\n <ng-template\n *ngIf=\"isDropOrMulti(col.filterType)\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-multiSelect\n [ngModel]=\"value\"\n [options]=\"col.dropdownOrMultiselectValues\"\n [placeholder]=\"t('All')\"\n (onChange)=\"filter($event.value)\"\n optionLabel=\"label\"\n optionValue=\"code\"\n [style]=\"{ width: '240px' }\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"p-multiselect-representative-option\">\n <span class=\"ml-2\">{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-template>\n <ng-template\n *ngIf=\"col.filterType == 'date'\"\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-datepicker\n [ngModel]=\"value\"\n [showTime]=\"col.showTime\"\n (onSelect)=\"filter($event)\"\n ></p-datepicker>\n </ng-template>\n </p-columnFilter>\n </div>\n </th>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template\n pTemplate=\"body\"\n let-rowData\n let-index=\"rowIndex\"\n let-editing=\"editing\"\n >\n <!-- The cell rendering every column gets unless it projects a spiderlyCellTemplate. Declared\n once here rather than inside the column loop, and fed the same context the projected\n template gets, so getRowData is evaluated exactly once per cell either way. -->\n <ng-template #defaultCell let-col=\"col\" let-displayValue=\"displayValue\">\n <ng-container *ngIf=\"col.filterType === 'blob'\">\n <img width=\"45\" [src]=\"displayValue\" alt=\"\" />\n </ng-container>\n <ng-container *ngIf=\"col.filterType !== 'blob'\">\n <span class=\"cell-text\">{{ displayValue }}</span>\n </ng-container>\n </ng-template>\n <tr\n [class.clickable]=\"navigateOnRowClick\"\n (click)=\"onRowClick(rowData, $event)\"\n >\n <!-- row-interactive: a selection gesture must never double as row navigation (onRowClick). -->\n <td\n *ngIf=\"selectionMode == 'multiple'\"\n class=\"selection-cell row-interactive\"\n (mousedown)=\"onSelectionCellMouseDown($event, rowData[idField])\"\n >\n <p-checkbox\n [disabled]=\"readonly\"\n (onChange)=\"selectRow(rowData[idField], rowData.index)\"\n [ngModel]=\"isRowSelected(rowData[idField])\"\n [binary]=\"true\"\n ></p-checkbox>\n </td>\n <ng-container *ngFor=\"let col of visibleCols; trackBy: colTrackByFn\">\n <td\n [style]=\"getStyleForBodyColumn(col)\"\n [class.clickable]=\"col.onCellClick\"\n [class.row-interactive]=\"col.onCellClick\"\n (click)=\"onCellClick(col, rowData, $event)\"\n *ngIf=\"!col.editable\"\n >\n <!-- getColWidth reserves this column's width from `actions.length`; this gap is\n the geometry it reserves for, so changing it belongs in the same edit. -->\n <div\n style=\"\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 18px;\n \"\n >\n <ng-container\n *ngFor=\"let action of col.actions; trackBy: actionTrackByFn\"\n >\n <span\n [pTooltip]=\"action.name\"\n [class]=\"getClassForAction(action) + ' row-interactive'\"\n [style]=\"getStyleForAction(action)\"\n (click)=\"getMethodForAction(action, rowData, $event)\"\n ></span>\n </ng-container>\n </div>\n <ng-container\n [ngTemplateOutlet]=\"getCellTemplate(col) ?? defaultCell\"\n [ngTemplateOutletContext]=\"{\n $implicit: rowData,\n value: rowData[col.field],\n displayValue: getRowData(rowData, col),\n col: col\n }\"\n ></ng-container>\n </td>\n <td *ngIf=\"col.editable\" class=\"row-interactive\">\n <spiderly-number\n [control]=\"getFormArrayControlByIndex(col.field, rowData.index)\"\n [showLabel]=\"false\"\n ></spiderly-number>\n </td>\n </ng-container>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n <tr>\n <td\n [attr.colspan]=\"\n visibleCols.length + (selectionMode === 'multiple' ? 1 : 0)\n \"\n >\n {{ t(\"NoRecordsFound\") }}\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"paginatorleft\">\n {{ t(\"TotalRecords\") }}: {{ totalRecords }}\n </ng-template>\n <ng-template pTemplate=\"paginatorright\">\n <div style=\"display: flex; justify-content: end; gap: 10px\">\n <spiderly-button\n *ngIf=\"showAddButton\"\n [label]=\"t('AddNew')\"\n icon=\"pi pi-plus\"\n (onClick)=\"navigateToDetails(0)\"\n ></spiderly-button>\n </div>\n </ng-template>\n </p-table>\n <p-popover #columnChooser>\n <div class=\"column-chooser\">\n <label\n *ngFor=\"let col of chooserCols; trackBy: colTrackByFn\"\n data-testid=\"column-chooser-option\"\n class=\"column-chooser-option\"\n >\n <p-checkbox\n [binary]=\"true\"\n [ngModel]=\"isColumnVisible(col)\"\n [disabled]=\"!canToggleColumn(col)\"\n (onChange)=\"toggleColumn(col, $event.checked)\"\n ></p-checkbox>\n <span>{{ col.name }}</span>\n </label>\n <button\n type=\"button\"\n data-testid=\"column-chooser-reset\"\n class=\"column-chooser-reset\"\n (click)=\"resetColumnVisibility()\"\n >\n {{ t('ResetToDefault') }}\n </button>\n </div>\n </p-popover>\n </div>\n</ng-container>\n", styles: [".table-header{display:flex;justify-content:space-between;align-items:center}.pi-filter-fill{color:var(--p-primary-color)}@media (max-width: 640px){.table-header{display:flex;flex-direction:column;align-items:start;gap:14px}}.spiderly-table .p-paginator{padding:1rem}@media (min-width: 1400px){.spiderly-table .p-paginator-left-content{position:absolute;padding:14px;left:0}}@media (min-width: 1400px){.spiderly-table .p-paginator-right-content{position:absolute;padding:14px;right:0}}.column-chooser{display:flex;flex-direction:column;gap:8px}.column-chooser-option{display:flex;align-items:center;gap:8px;cursor:pointer}.column-chooser-reset{margin-top:4px;padding:0;border:none;background:none;color:var(--p-primary-color);cursor:pointer;align-self:flex-start;font-size:.875rem}.column-chooser-reset:hover{text-decoration:underline}:host .spiderly-table-container{scroll-margin-top:calc(var(--spiderly-viewport-top-inset, 0px) + 1rem)}:host ::ng-deep .clickable{cursor:pointer}:host ::ng-deep td.clickable:hover{text-decoration:underline}:host td.selection-cell{-webkit-user-select:none;user-select:none}:host .cell-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}:host ::ng-deep .p-datatable-mask{background:color-mix(in srgb,var(--p-content-background, #ffffff) 65%,transparent);color:var(--p-primary-color, #334155);align-items:flex-start;padding-top:6rem}:host ::ng-deep .p-datatable-thead{position:unset!important}:host ::ng-deep .p-datatable-header{border-radius:6px 6px 0 0}:host ::ng-deep .p-datatable{border-radius:var(--p-content-border-radius);border:1px solid var(--p-datatable-border-color)}\n"] }]
|
|
5255
|
+
}], ctorParameters: () => [{ type: i3$2.Router }, { type: i1$5.DialogService }, { type: i3$2.ActivatedRoute }, { type: SpiderlyMessageService }, { type: i1.TranslocoService }, { type: ConfigServiceBase }, { type: i0.ErrorHandler }, { type: undefined, decorators: [{
|
|
4969
5256
|
type: Inject,
|
|
4970
5257
|
args: [LOCALE_ID]
|
|
4971
5258
|
}] }], propDecorators: { table: [{
|
|
@@ -5067,13 +5354,14 @@ class Action {
|
|
|
5067
5354
|
}
|
|
5068
5355
|
}
|
|
5069
5356
|
class Column {
|
|
5070
|
-
constructor({ name, field, filterField, filterType, filterPlaceholder, showMatchModes, showAddButton, dropdownOrMultiselectValues, actions, editable, showTime, decimalPlaces, sortable, visible, lockVisible,
|
|
5357
|
+
constructor({ name, field, filterField, filterType, filterPlaceholder, showMatchModes, matchModes, showAddButton, dropdownOrMultiselectValues, actions, editable, showTime, decimalPlaces, sortable, visible, lockVisible, width, onCellClick, } = {}) {
|
|
5071
5358
|
this.name = name;
|
|
5072
5359
|
this.field = field;
|
|
5073
5360
|
this.filterField = filterField;
|
|
5074
5361
|
this.filterType = filterType;
|
|
5075
5362
|
this.filterPlaceholder = filterPlaceholder;
|
|
5076
5363
|
this.showMatchModes = showMatchModes;
|
|
5364
|
+
this.matchModes = matchModes;
|
|
5077
5365
|
this.showAddButton = showAddButton;
|
|
5078
5366
|
this.dropdownOrMultiselectValues = dropdownOrMultiselectValues;
|
|
5079
5367
|
this.actions = actions;
|
|
@@ -5083,7 +5371,7 @@ class Column {
|
|
|
5083
5371
|
this.sortable = sortable;
|
|
5084
5372
|
this.visible = visible;
|
|
5085
5373
|
this.lockVisible = lockVisible;
|
|
5086
|
-
this.
|
|
5374
|
+
this.width = width;
|
|
5087
5375
|
this.onCellClick = onCellClick;
|
|
5088
5376
|
}
|
|
5089
5377
|
}
|
|
@@ -5156,7 +5444,24 @@ class SpiderlyDataViewComponent {
|
|
|
5156
5444
|
},
|
|
5157
5445
|
];
|
|
5158
5446
|
}
|
|
5447
|
+
// Paging semantics and the scroll-margin contract are documented once, on the data table:
|
|
5448
|
+
// spiderly-data-table/CLAUDE.md → "Scroll back to the table on page change".
|
|
5449
|
+
onPageChange(container) {
|
|
5450
|
+
scrollElementIntoViewIfAboveViewport(container);
|
|
5451
|
+
}
|
|
5452
|
+
/**
|
|
5453
|
+
* The one pending predicate: PrimeNG's overlay and the container's `aria-busy` must never
|
|
5454
|
+
* disagree. Mirrors the data table's, including why it is not also testing `items`; the
|
|
5455
|
+
* rationale lives there.
|
|
5456
|
+
*/
|
|
5457
|
+
get isPending() {
|
|
5458
|
+
return this.loading;
|
|
5459
|
+
}
|
|
5159
5460
|
lazyLoad(event) {
|
|
5461
|
+
// Every refetch is pending, not just the first load. `items` is deliberately left alone so
|
|
5462
|
+
// the previous page stays readable under the overlay. Full reasoning: the data table's
|
|
5463
|
+
// CLAUDE.md → "Pending state".
|
|
5464
|
+
this.loading = true;
|
|
5160
5465
|
this.lastLazyLoadEvent = event;
|
|
5161
5466
|
const transformedFilter = {};
|
|
5162
5467
|
for (const key in event.filters) {
|
|
@@ -5177,7 +5482,7 @@ class SpiderlyDataViewComponent {
|
|
|
5177
5482
|
tableFilter.filters = transformedFilter;
|
|
5178
5483
|
this.onLazyLoad.next(tableFilter);
|
|
5179
5484
|
this.getPaginatedListObservableMethod(tableFilter).subscribe({
|
|
5180
|
-
next:
|
|
5485
|
+
next: (res) => {
|
|
5181
5486
|
this.items = res.data;
|
|
5182
5487
|
this.totalRecords = res.totalRecords;
|
|
5183
5488
|
this.loading = false;
|
|
@@ -5221,8 +5526,10 @@ class SpiderlyDataViewComponent {
|
|
|
5221
5526
|
}
|
|
5222
5527
|
}
|
|
5223
5528
|
reload() {
|
|
5224
|
-
|
|
5225
|
-
this.
|
|
5529
|
+
// Nothing to replay before the initial load, which is already on its way.
|
|
5530
|
+
if (this.lastLazyLoadEvent == null)
|
|
5531
|
+
return;
|
|
5532
|
+
// Not special: same refetch as a page flip, so lazyLoad owns both the flag and `items`.
|
|
5226
5533
|
this.lazyLoad(this.lastLazyLoadEvent);
|
|
5227
5534
|
}
|
|
5228
5535
|
colTrackByFn(index, item) {
|
|
@@ -5235,7 +5542,7 @@ class SpiderlyDataViewComponent {
|
|
|
5235
5542
|
this.table.clear();
|
|
5236
5543
|
}
|
|
5237
5544
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyDataViewComponent, deps: [{ token: i1.TranslocoService }, { token: LOCALE_ID }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
5238
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.13", type: SpiderlyDataViewComponent, isStandalone: true, selector: "spiderly-data-view", inputs: { items: "items", rows: "rows", filters: "filters", showCardWrapper: "showCardWrapper", showPaginatorRightData: "showPaginatorRightData", showTotalRecordsNumber: "showTotalRecordsNumber", applyFiltersIcon: "applyFiltersIcon", clearFiltersIcon: "clearFiltersIcon", getPaginatedListObservableMethod: "getPaginatedListObservableMethod" }, outputs: { onLazyLoad: "onLazyLoad" }, queries: [{ propertyName: "cardBody", first: true, predicate: ["cardBody"], descendants: true, read: TemplateRef }], viewQueries: [{ propertyName: "table", first: true, predicate: ["dt"], descendants: true }], ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div\n [class]=\"\n showCardWrapper ? 'card responsive-card-padding overflow-auto' : ''\n \"\n >\n <p-table\n #dt\n [value]=\"items\"\n [rows]=\"rows\"\n [rowHover]=\"false\"\n responsiveLayout=\"scroll\"\n [lazy]=\"true\"\n (onLazyLoad)=\"lazyLoad($event)\"\n [totalRecords]=\"totalRecords\"\n class=\"spiderly-table\"\n [loading]=\"items === undefined || loading === true\"\n dataKey=\"id\"\n (onFilter)=\"filter($event)\"\n [filterDelay]=\"500\"\n [paginator]=\"true\"\n >\n <ng-template #caption>\n <div *ngIf=\"filters.length !== 0\">\n <div class=\"data-view-grid\">\n @for (filter of filters; track $index) {\n <div>\n <div\n style=\"\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n position: relative;\n \"\n >\n <div>\n <label>\n {{ filter.label }}\n </label>\n </div>\n\n <p-columnFilter\n *ngIf=\"filter.type != null\"\n [type]=\"filter.type\"\n [field]=\"filter.filterField ?? filter.field\"\n display=\"row\"\n [placeholder]=\"filter.placeholder\"\n [showOperator]=\"false\"\n [showMenu]=\"filter.showMatchModes\"\n [showMatchModes]=\"true\"\n [showClearButton]=\"false\"\n [matchMode]=\"getDefaultMatchMode(filter.type)\"\n [matchModeOptions]=\"getMatchModeOptions(filter.type)\"\n >\n <ng-container *ngIf=\"filter.type == 'multiselect'\">\n <ng-template\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-multiSelect\n [ngModel]=\"value\"\n [options]=\"filter.dropdownOrMultiselectValues\"\n [placeholder]=\"t('All')\"\n (onChange)=\"filter($event.value)\"\n optionLabel=\"label\"\n optionValue=\"code\"\n [style]=\"{ width: '100%' }\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"p-multiselect-representative-option\">\n <span class=\"ml-2\">{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-template>\n </ng-container>\n </p-columnFilter>\n </div>\n </div>\n }\n </div>\n <div style=\"display: flex; gap: 12px\">\n <spiderly-button\n [label]=\"t('ApplyFilters')\"\n (onClick)=\"applyFilters()\"\n [icon]=\"applyFiltersIcon\"\n ></spiderly-button>\n <spiderly-button\n [label]=\"t('ClearFilters')\"\n (onClick)=\"clearFilters()\"\n [icon]=\"clearFiltersIcon\"\n ></spiderly-button>\n </div>\n </div>\n </ng-template>\n <ng-template #body let-rowData let-index=\"rowIndex\">\n <tr style=\"padding: 0; margin-bottom: 0\">\n <td style=\"display: block; border: 0; padding: 0\">\n <ng-container\n *ngTemplateOutlet=\"\n cardBody;\n context: { $implicit: rowData, item: rowData, index: index }\n \"\n >\n </ng-container>\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n {{ t(\"NoRecordsFound\") }}\n </ng-template>\n <ng-template *ngIf=\"showTotalRecordsNumber\" pTemplate=\"paginatorleft\">\n {{ t(\"TotalRecords\") }}: {{ totalRecords }}\n </ng-template>\n <ng-template *ngIf=\"showPaginatorRightData\" pTemplate=\"paginatorright\">\n <ng-content select=\"[PAGINATORRIGHT]\"></ng-content>\n </ng-template>\n </p-table>\n </div>\n</ng-container>\n", styles: ["::ng-deep .p-datatable-tbody{display:flex;flex-direction:column;gap:18px}::ng-deep .p-datatable-header{margin-bottom:36px;border:0!important;padding:0!important;background-color:transparent!important}::ng-deep .p-paginator{padding:18px 9px 0!important}::ng-deep .p-fluid{width:100%!important}::ng-deep .p-inputtext{width:100%!important}::ng-deep .p-inputnumber{width:100%!important}::ng-deep .p-datepicker{width:100%!important}.data-view-grid{display:grid;gap:18px;grid-template-columns:1fr 1fr 1fr;margin-bottom:18px}@media (max-width: 1080px){.data-view-grid{gap:12px;grid-template-columns:1fr 1fr}}@media (max-width: 500px){.data-view-grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: SpiderlyControlsModule }, { kind: "component", type: SpiderlyButtonComponent, selector: "spiderly-button", inputs: ["type"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i10.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "virtualRowHeight", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i10.ColumnFilter, selector: "p-columnFilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "useGrouping", "showButtons", "ariaLabel", "filterButtonProps"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i4$5.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "fluid", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "size", "showClear", "autofocus", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "ngmodule", type: DatePickerModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "ngmodule", type: InputNumberModule }, { kind: "ngmodule", type: SelectModule }] }); }
|
|
5545
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.13", type: SpiderlyDataViewComponent, isStandalone: true, selector: "spiderly-data-view", inputs: { items: "items", rows: "rows", filters: "filters", showCardWrapper: "showCardWrapper", showPaginatorRightData: "showPaginatorRightData", showTotalRecordsNumber: "showTotalRecordsNumber", applyFiltersIcon: "applyFiltersIcon", clearFiltersIcon: "clearFiltersIcon", getPaginatedListObservableMethod: "getPaginatedListObservableMethod" }, outputs: { onLazyLoad: "onLazyLoad" }, queries: [{ propertyName: "cardBody", first: true, predicate: ["cardBody"], descendants: true, read: TemplateRef }], viewQueries: [{ propertyName: "table", first: true, predicate: ["dt"], descendants: true }], ngImport: i0, template: "<ng-container *transloco=\"let t\">\n <div\n #tableContainer\n class=\"spiderly-table-container\"\n [attr.aria-busy]=\"isPending\"\n [ngClass]=\"{\n 'card responsive-card-padding overflow-auto': showCardWrapper\n }\"\n >\n <p-table\n #dt\n [value]=\"items\"\n [rows]=\"rows\"\n [rowHover]=\"false\"\n responsiveLayout=\"scroll\"\n [lazy]=\"true\"\n (onLazyLoad)=\"lazyLoad($event)\"\n (onPage)=\"onPageChange(tableContainer)\"\n [totalRecords]=\"totalRecords\"\n class=\"spiderly-table\"\n [loading]=\"isPending\"\n dataKey=\"id\"\n (onFilter)=\"filter($event)\"\n [filterDelay]=\"500\"\n [paginator]=\"true\"\n >\n <ng-template #caption>\n <div *ngIf=\"filters.length !== 0\">\n <div class=\"data-view-grid\">\n @for (filter of filters; track $index) {\n <div>\n <div\n style=\"\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n position: relative;\n \"\n >\n <div>\n <label>\n {{ filter.label }}\n </label>\n </div>\n\n <p-columnFilter\n *ngIf=\"filter.type != null\"\n [type]=\"filter.type\"\n [field]=\"filter.filterField ?? filter.field\"\n display=\"row\"\n [placeholder]=\"filter.placeholder\"\n [showOperator]=\"false\"\n [showMenu]=\"filter.showMatchModes\"\n [showMatchModes]=\"true\"\n [showClearButton]=\"false\"\n [matchMode]=\"getDefaultMatchMode(filter.type)\"\n [matchModeOptions]=\"getMatchModeOptions(filter.type)\"\n >\n <ng-container *ngIf=\"filter.type == 'multiselect'\">\n <ng-template\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-multiSelect\n [ngModel]=\"value\"\n [options]=\"filter.dropdownOrMultiselectValues\"\n [placeholder]=\"t('All')\"\n (onChange)=\"filter($event.value)\"\n optionLabel=\"label\"\n optionValue=\"code\"\n [style]=\"{ width: '100%' }\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"p-multiselect-representative-option\">\n <span class=\"ml-2\">{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-template>\n </ng-container>\n </p-columnFilter>\n </div>\n </div>\n }\n </div>\n <div style=\"display: flex; gap: 12px\">\n <spiderly-button\n [label]=\"t('ApplyFilters')\"\n (onClick)=\"applyFilters()\"\n [icon]=\"applyFiltersIcon\"\n ></spiderly-button>\n <spiderly-button\n [label]=\"t('ClearFilters')\"\n (onClick)=\"clearFilters()\"\n [icon]=\"clearFiltersIcon\"\n ></spiderly-button>\n </div>\n </div>\n </ng-template>\n <ng-template #body let-rowData let-index=\"rowIndex\">\n <tr style=\"padding: 0; margin-bottom: 0\">\n <td style=\"display: block; border: 0; padding: 0\">\n <ng-container\n *ngTemplateOutlet=\"\n cardBody;\n context: { $implicit: rowData, item: rowData, index: index }\n \"\n >\n </ng-container>\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n {{ t(\"NoRecordsFound\") }}\n </ng-template>\n <ng-template *ngIf=\"showTotalRecordsNumber\" pTemplate=\"paginatorleft\">\n {{ t(\"TotalRecords\") }}: {{ totalRecords }}\n </ng-template>\n <ng-template *ngIf=\"showPaginatorRightData\" pTemplate=\"paginatorright\">\n <ng-content select=\"[PAGINATORRIGHT]\"></ng-content>\n </ng-template>\n </p-table>\n </div>\n</ng-container>\n", styles: [":host .spiderly-table-container{scroll-margin-top:calc(var(--spiderly-viewport-top-inset, 0px) + 1rem)}:host ::ng-deep .p-datatable-mask{background:color-mix(in srgb,var(--p-content-background, #ffffff) 65%,transparent);color:var(--p-primary-color, #334155);align-items:flex-start;padding-top:6rem}::ng-deep .p-datatable-tbody{display:flex;flex-direction:column;gap:18px}::ng-deep .p-datatable-header{margin-bottom:36px;border:0!important;padding:0!important;background-color:transparent!important}::ng-deep .p-paginator{padding:18px 9px 0!important}::ng-deep .p-fluid{width:100%!important}::ng-deep .p-inputtext{width:100%!important}::ng-deep .p-inputnumber{width:100%!important}::ng-deep .p-datepicker{width:100%!important}.data-view-grid{display:grid;gap:18px;grid-template-columns:1fr 1fr 1fr;margin-bottom:18px}@media (max-width: 1080px){.data-view-grid{gap:12px;grid-template-columns:1fr 1fr}}@media (max-width: 500px){.data-view-grid{grid-template-columns:1fr}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "ngmodule", type: SpiderlyControlsModule }, { kind: "component", type: SpiderlyButtonComponent, selector: "spiderly-button", inputs: ["type"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i10.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "virtualRowHeight", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "directive", type: i1$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i10.ColumnFilter, selector: "p-columnFilter", inputs: ["field", "type", "display", "showMenu", "matchMode", "operator", "showOperator", "showClearButton", "showApplyButton", "showMatchModes", "showAddButton", "hideOnClear", "placeholder", "matchModeOptions", "maxConstraints", "minFractionDigits", "maxFractionDigits", "prefix", "suffix", "locale", "localeMatcher", "currency", "currencyDisplay", "useGrouping", "showButtons", "ariaLabel", "filterButtonProps"], outputs: ["onShow", "onHide"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: MultiSelectModule }, { kind: "component", type: i4$5.MultiSelect, selector: "p-multiSelect, p-multiselect, p-multi-select", inputs: ["id", "ariaLabel", "style", "styleClass", "panelStyle", "panelStyleClass", "inputId", "disabled", "fluid", "readonly", "group", "filter", "filterPlaceHolder", "filterLocale", "overlayVisible", "tabindex", "variant", "appendTo", "dataKey", "name", "ariaLabelledBy", "displaySelectedLabel", "maxSelectedLabels", "selectionLimit", "selectedItemsLabel", "showToggleAll", "emptyFilterMessage", "emptyMessage", "resetFilterOnHide", "dropdownIcon", "chipIcon", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "showHeader", "filterBy", "scrollHeight", "lazy", "virtualScroll", "loading", "virtualScrollItemSize", "loadingIcon", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "autofocusFilter", "display", "autocomplete", "size", "showClear", "autofocus", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "defaultLabel", "placeholder", "options", "filterValue", "itemSize", "selectAll", "focusOnHover", "filterFields", "selectOnFocus", "autoOptionFocus", "highlightOnSelect"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onClear", "onPanelShow", "onPanelHide", "onLazyLoad", "onRemove", "onSelectAllChange"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "ngmodule", type: DatePickerModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "ngmodule", type: InputNumberModule }, { kind: "ngmodule", type: SelectModule }] }); }
|
|
5239
5546
|
}
|
|
5240
5547
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyDataViewComponent, decorators: [{
|
|
5241
5548
|
type: Component,
|
|
@@ -5253,7 +5560,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
|
|
|
5253
5560
|
InputTextModule,
|
|
5254
5561
|
InputNumberModule,
|
|
5255
5562
|
SelectModule,
|
|
5256
|
-
], template: "<ng-container *transloco=\"let t\">\n <div\n [
|
|
5563
|
+
], template: "<ng-container *transloco=\"let t\">\n <div\n #tableContainer\n class=\"spiderly-table-container\"\n [attr.aria-busy]=\"isPending\"\n [ngClass]=\"{\n 'card responsive-card-padding overflow-auto': showCardWrapper\n }\"\n >\n <p-table\n #dt\n [value]=\"items\"\n [rows]=\"rows\"\n [rowHover]=\"false\"\n responsiveLayout=\"scroll\"\n [lazy]=\"true\"\n (onLazyLoad)=\"lazyLoad($event)\"\n (onPage)=\"onPageChange(tableContainer)\"\n [totalRecords]=\"totalRecords\"\n class=\"spiderly-table\"\n [loading]=\"isPending\"\n dataKey=\"id\"\n (onFilter)=\"filter($event)\"\n [filterDelay]=\"500\"\n [paginator]=\"true\"\n >\n <ng-template #caption>\n <div *ngIf=\"filters.length !== 0\">\n <div class=\"data-view-grid\">\n @for (filter of filters; track $index) {\n <div>\n <div\n style=\"\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n position: relative;\n \"\n >\n <div>\n <label>\n {{ filter.label }}\n </label>\n </div>\n\n <p-columnFilter\n *ngIf=\"filter.type != null\"\n [type]=\"filter.type\"\n [field]=\"filter.filterField ?? filter.field\"\n display=\"row\"\n [placeholder]=\"filter.placeholder\"\n [showOperator]=\"false\"\n [showMenu]=\"filter.showMatchModes\"\n [showMatchModes]=\"true\"\n [showClearButton]=\"false\"\n [matchMode]=\"getDefaultMatchMode(filter.type)\"\n [matchModeOptions]=\"getMatchModeOptions(filter.type)\"\n >\n <ng-container *ngIf=\"filter.type == 'multiselect'\">\n <ng-template\n pTemplate=\"filter\"\n let-value\n let-filter=\"filterCallback\"\n >\n <p-multiSelect\n [ngModel]=\"value\"\n [options]=\"filter.dropdownOrMultiselectValues\"\n [placeholder]=\"t('All')\"\n (onChange)=\"filter($event.value)\"\n optionLabel=\"label\"\n optionValue=\"code\"\n [style]=\"{ width: '100%' }\"\n >\n <ng-template let-item pTemplate=\"item\">\n <div class=\"p-multiselect-representative-option\">\n <span class=\"ml-2\">{{ item.label }}</span>\n </div>\n </ng-template>\n </p-multiSelect>\n </ng-template>\n </ng-container>\n </p-columnFilter>\n </div>\n </div>\n }\n </div>\n <div style=\"display: flex; gap: 12px\">\n <spiderly-button\n [label]=\"t('ApplyFilters')\"\n (onClick)=\"applyFilters()\"\n [icon]=\"applyFiltersIcon\"\n ></spiderly-button>\n <spiderly-button\n [label]=\"t('ClearFilters')\"\n (onClick)=\"clearFilters()\"\n [icon]=\"clearFiltersIcon\"\n ></spiderly-button>\n </div>\n </div>\n </ng-template>\n <ng-template #body let-rowData let-index=\"rowIndex\">\n <tr style=\"padding: 0; margin-bottom: 0\">\n <td style=\"display: block; border: 0; padding: 0\">\n <ng-container\n *ngTemplateOutlet=\"\n cardBody;\n context: { $implicit: rowData, item: rowData, index: index }\n \"\n >\n </ng-container>\n </td>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"emptymessage\">\n {{ t(\"NoRecordsFound\") }}\n </ng-template>\n <ng-template *ngIf=\"showTotalRecordsNumber\" pTemplate=\"paginatorleft\">\n {{ t(\"TotalRecords\") }}: {{ totalRecords }}\n </ng-template>\n <ng-template *ngIf=\"showPaginatorRightData\" pTemplate=\"paginatorright\">\n <ng-content select=\"[PAGINATORRIGHT]\"></ng-content>\n </ng-template>\n </p-table>\n </div>\n</ng-container>\n", styles: [":host .spiderly-table-container{scroll-margin-top:calc(var(--spiderly-viewport-top-inset, 0px) + 1rem)}:host ::ng-deep .p-datatable-mask{background:color-mix(in srgb,var(--p-content-background, #ffffff) 65%,transparent);color:var(--p-primary-color, #334155);align-items:flex-start;padding-top:6rem}::ng-deep .p-datatable-tbody{display:flex;flex-direction:column;gap:18px}::ng-deep .p-datatable-header{margin-bottom:36px;border:0!important;padding:0!important;background-color:transparent!important}::ng-deep .p-paginator{padding:18px 9px 0!important}::ng-deep .p-fluid{width:100%!important}::ng-deep .p-inputtext{width:100%!important}::ng-deep .p-inputnumber{width:100%!important}::ng-deep .p-datepicker{width:100%!important}.data-view-grid{display:grid;gap:18px;grid-template-columns:1fr 1fr 1fr;margin-bottom:18px}@media (max-width: 1080px){.data-view-grid{gap:12px;grid-template-columns:1fr 1fr}}@media (max-width: 500px){.data-view-grid{grid-template-columns:1fr}}\n"] }]
|
|
5257
5564
|
}], ctorParameters: () => [{ type: i1.TranslocoService }, { type: undefined, decorators: [{
|
|
5258
5565
|
type: Inject,
|
|
5259
5566
|
args: [LOCALE_ID]
|
|
@@ -5779,5 +6086,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
|
|
|
5779
6086
|
* Generated bundle index. Do not edit.
|
|
5780
6087
|
*/
|
|
5781
6088
|
|
|
5782
|
-
export { Action, AllClickEvent, ApiErrorCodes, ApiSecurityService, AppSidebarComponent, AuthCardComponent, AuthGuard, AuthResult, AuthResultWithCookies, AuthServiceBase, BaseAutocompleteControl, BaseControl, BaseDropdownControl, BaseEntity, BaseFormComponent, BaseFormService, CardSkeletonComponent, Codebook, Column, ConfigServiceBase, DEFAULT_EXTERNAL_PROVIDER_ICONS, ExternalLoginComponent, ExternalProvider, ExternalProviderPublic, Filter, FilterRule, FilterSortMeta,
|
|
6089
|
+
export { Action, AllClickEvent, ApiErrorCodes, ApiSecurityService, AppSidebarComponent, AuthCardComponent, AuthGuard, AuthResult, AuthResultWithCookies, AuthServiceBase, BaseAutocompleteControl, BaseControl, BaseDropdownControl, BaseEntity, BaseFormComponent, BaseFormService, CardSkeletonComponent, Codebook, Column, ConfigServiceBase, DEFAULT_EXTERNAL_PROVIDER_ICONS, ExternalLoginComponent, ExternalProvider, ExternalProviderPublic, Filter, FilterRule, FilterSortMeta, IndexCardComponent, InfoCardComponent, InitCompanyAuthDialogDetails, InitTopBarData, IsAuthorizedForSaveEvent, LastMenuIconIndexClicked, LayoutServiceBase, LazyLoadSelectedIdsResult, Login, LoginVerificationComponent, LoginVerificationToken, MatchModeCodes, MenuChangeEvent, MenuitemComponent, Namebook, NotAuthGuard, NotFoundComponent, PROPS_KEY, PaginatedResult, PanelBodyComponent, PanelFooterComponent, PanelHeaderComponent, PrimengOption, ProfileAvatarComponent, ReflectProp, RefreshTokenRequest, RequiredComponent, RowClickEvent, SecurityPermissionCodes, SendLoginVerificationEmailResult, SideMenuTopBarComponent, SidebarMenuComponent, SimpleSaveResult, SpiderlyAutocompleteComponent, SpiderlyButtonBaseComponent, SpiderlyButtonComponent, SpiderlyCalendarComponent, SpiderlyCardComponent, SpiderlyCellTemplateDirective, SpiderlyCheckboxComponent, SpiderlyColorPickerComponent, SpiderlyControlsModule, SpiderlyDataTableActionsDirective, SpiderlyDataTableComponent, SpiderlyDataViewComponent, SpiderlyDeleteConfirmationComponent, SpiderlyDropdownComponent, SpiderlyEditorComponent, SpiderlyErrorHandler, SpiderlyFileComponent, SpiderlyFileSelectEvent, SpiderlyFormArray, SpiderlyFormControl, SpiderlyFormGroup, SpiderlyLayoutComponent, SpiderlyLoginComponent, SpiderlyMarkdownComponent, SpiderlyMessageService, SpiderlyMultiAutocompleteComponent, SpiderlyMultiSelectComponent, SpiderlyNumberComponent, SpiderlyPanelComponent, SpiderlyPanelsModule, SpiderlyPasswordComponent, SpiderlyReturnButtonComponent, SpiderlySplitButtonComponent, SpiderlyTab, SpiderlyTemplateTypeDirective, SpiderlyTextareaComponent, SpiderlyTextboxComponent, SpiderlyTranslocoLoader, TopBarComponent, UserBase, UserRole, ValidatorAbstractService, VerificationTokenRequest, VerificationTypeCodes, VerificationWrapperComponent, adjustColor, authInitializer, capitalizeFirstChar, deleteAction, exportListToExcel, firstCharToUpper, getFileNameFromContentDisposition, getHtmlImgDisplayString64, getImageDimensions, getMimeTypeForFileName, getMonth, getParentUrl, getPrimengAutocompleteCodebookOptions, getPrimengAutocompleteNamebookOptions, getPrimengDropdownCodebookOptions, getPrimengDropdownNamebookOptions, getPrimengNamebookOptions, httpLoadingInterceptor, isExcelFileType, isFileImageType, isNullOrEmpty, jsonHttpInterceptor, jwtInterceptor, kebabToTitleCase, nameOf, nameof, parseDateOnlyLocal, pushAction, saveResponseAsFile, scrollElementIntoViewIfAboveViewport, selectedTab, singleOrDefault, splitPascalCase, toCommaSeparatedString, unauthorizedInterceptor, validatePrecisionScale };
|
|
5783
6090
|
//# sourceMappingURL=spiderly.mjs.map
|