@yuuvis/client-framework 3.23.0 → 3.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,15 +1,17 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, signal, viewChild, contentChild, input, output, computed, untracked, effect, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
2
+ import { inject, signal, viewChild, contentChild, input, output, computed, linkedSignal, untracked, effect, ChangeDetectionStrategy, Component, NgModule } from '@angular/core';
3
3
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
4
4
  import * as i1 from '@angular/common';
5
5
  import { CommonModule } from '@angular/common';
6
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
7
  import * as i3 from '@angular/material/paginator';
7
- import { MatPaginatorModule } from '@angular/material/paginator';
8
+ import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
8
9
  import { SearchService } from '@yuuvis/client-core';
9
10
  import { ClickDoubleDirective, DragSelectDirective, DragSelectItemDirective } from '@yuuvis/client-framework/common';
10
11
  import * as i2 from '@yuuvis/client-framework/list';
11
12
  import { YuvListModule } from '@yuuvis/client-framework/list';
12
13
  import { DeviceService } from '@yuuvis/material';
14
+ import { Subject, switchMap, EMPTY, defer, map, catchError, finalize } from 'rxjs';
13
15
 
14
16
  /**
15
17
  * Query-driven list component that executes a search query and renders the results
@@ -78,6 +80,14 @@ class QueryListComponent {
78
80
  #items;
79
81
  #dropInItems;
80
82
  #listItemUpdates;
83
+ // Every search the list runs — new query, page change, refresh — goes through this one stream, so
84
+ // a newer request cancels the one still in flight instead of racing it, and a late response can
85
+ // never overwrite a newer one (see `#load`). `null` cancels without a new request (see `#cancel`).
86
+ #requests;
87
+ // The request still in flight — `busy` follows it — and the one whose result is on screen: what
88
+ // `refresh()` re-runs.
89
+ #pendingRequest;
90
+ #shownRequest;
81
91
  //#endregion
82
92
  constructor() {
83
93
  //#region Dependencies
@@ -132,8 +142,8 @@ class QueryListComponent {
132
142
  *
133
143
  * Accepts either a structured `SearchQuery` object (field filters, sort orders, etc.)
134
144
  * or a raw CMIS query string. The query is re-executed reactively every time this
135
- * input changes. Set to `null` or `undefined` to clear the list without triggering
136
- * a new request.
145
+ * input changes. Set to `null` or `undefined` to stop searching: a request still in
146
+ * flight is cancelled, and the list keeps showing the result it has.
137
147
  */
138
148
  this.query = input(...(ngDevMode ? [undefined, { debugName: "query" }] : /* istanbul ignore next */ []));
139
149
  /**
@@ -375,27 +385,44 @@ class QueryListComponent {
375
385
  /**
376
386
  * Indicates whether a search request is currently in flight.
377
387
  *
378
- * Set to `true` immediately before a query or page request is dispatched to the
379
- * search service, and back to `false` once the response (or error) arrives.
388
+ * `true` from the moment a query or page request is dispatched to the search service until
389
+ * the latest request settles (response, error or cancellation). A request superseded by a
390
+ * newer one is cancelled, and `busy` stays `true` across that switch. Derived from the request
391
+ * in flight but still writable: a value set from outside holds until the loading state changes.
380
392
  * Bind this to a loading indicator or `BusyOverlayDirective` in the parent template:
381
393
  * ```html
382
394
  * <yuv-query-list #qList ...>...</yuv-query-list>
383
395
  * <yuv-busy-overlay [active]="qList.busy()" />
384
396
  * ```
385
397
  */
386
- this.busy = signal(false, ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
398
+ this.busy = linkedSignal(() => this.#pendingRequest() !== null, ...(ngDevMode ? [{ debugName: "busy" }] : /* istanbul ignore next */ []));
399
+ // Only there while the result has several pages. TS `private` because a signal query cannot sit
400
+ // on an ES-private field (NG1053).
401
+ this.paginator = viewChild(MatPaginator, ...(ngDevMode ? [{ debugName: "paginator" }] : /* istanbul ignore next */ []));
387
402
  this.#items = signal([], ...(ngDevMode ? [{ debugName: "#items" }] : /* istanbul ignore next */ []));
388
403
  this.#dropInItems = signal([], ...(ngDevMode ? [{ debugName: "#dropInItems" }] : /* istanbul ignore next */ []));
389
404
  this.#listItemUpdates = signal({}, ...(ngDevMode ? [{ debugName: "#listItemUpdates" }] : /* istanbul ignore next */ []));
405
+ // Every search the list runs — new query, page change, refresh — goes through this one stream, so
406
+ // a newer request cancels the one still in flight instead of racing it, and a late response can
407
+ // never overwrite a newer one (see `#load`). `null` cancels without a new request (see `#cancel`).
408
+ this.#requests = new Subject();
409
+ // The request still in flight — `busy` follows it — and the one whose result is on screen: what
410
+ // `refresh()` re-runs.
411
+ this.#pendingRequest = signal(null, ...(ngDevMode ? [{ debugName: "#pendingRequest" }] : /* istanbul ignore next */ []));
412
+ this.#shownRequest = signal(null, ...(ngDevMode ? [{ debugName: "#shownRequest" }] : /* istanbul ignore next */ []));
390
413
  //#endregion
391
414
  //#region Effects
392
415
  this.#executeQueryEffect = () => {
393
416
  // execute the query each time it changes
394
417
  const query = this.query();
395
- if (query)
396
- untracked(() => {
397
- this.#executeQuery(query || null);
398
- });
418
+ untracked(() => {
419
+ if (query) {
420
+ this.#request({ query, page: null });
421
+ }
422
+ else {
423
+ this.#cancel();
424
+ }
425
+ });
399
426
  };
400
427
  /**
401
428
  * Keeps the inner list's positional selection aligned with the id-based {@link selectedIds}
@@ -433,6 +460,9 @@ class QueryListComponent {
433
460
  };
434
461
  effect(this.#executeQueryEffect);
435
462
  effect(this.#resyncSelectionEffect);
463
+ this.#requests
464
+ .pipe(switchMap((request) => (request ? this.#load(request) : EMPTY)), takeUntilDestroyed())
465
+ .subscribe(({ request, result }) => this.#showResult(request, result));
436
466
  }
437
467
  //#region UI Methods
438
468
  /** Resolves the tracking key for a result item in the `@for` loop. */
@@ -591,7 +621,7 @@ class QueryListComponent {
591
621
  * appear in the current query (e.g. because the index has not been updated), drop it
592
622
  * in temporarily so the user sees it immediately without a full refresh.
593
623
  *
594
- * Drop-in items are cleared automatically when the user navigates to a different page.
624
+ * Drop-in items are cleared automatically once a page the user navigates to is shown.
595
625
  * Call `dropItems([])` to remove them programmatically.
596
626
  *
597
627
  * @param items Items to prepend. Must be of type `T` (same as query result items).
@@ -699,21 +729,24 @@ class QueryListComponent {
699
729
  /**
700
730
  * Re-executes the current query without changing the page or query parameters.
701
731
  *
702
- * If pagination is active, re-fetches the same page the user is currently on.
703
- * If no pagination is active, re-runs the original query from scratch.
732
+ * Re-runs what the list is about to show or is showing: a request still in flight, otherwise
733
+ * the page the user is currently on, or the query from scratch if it has only one page. If that
734
+ * page no longer exists — its objects were deleted — the list moves to the last page there is.
704
735
  *
705
736
  * Use this to reflect server-side changes (e.g. after a create/delete operation)
706
737
  * without navigating away from the current view.
707
738
  */
708
739
  refresh() {
709
740
  const query = this.query();
710
- if (query) {
711
- if (this.pagination()) {
712
- this.goToPage(this.pagination().page);
713
- }
714
- else
715
- this.#executeQuery(query || null);
716
- }
741
+ if (!query)
742
+ return;
743
+ // A request in flight is re-run as it is, so a page change still clears the drop-in items. The shown
744
+ // one only for its page: its drop-ins are gone already, and any there now were added since.
745
+ // Not `pagination()`: after a new query failed to load, that still describes the previous query's
746
+ // result, and its page number would be applied to the new one.
747
+ const pending = this.#pendingRequest();
748
+ const shown = this.#shownRequest();
749
+ this.#request(pending?.query === query ? pending : { query, page: shown?.query === query ? shown.page : null });
717
750
  }
718
751
  /**
719
752
  * Forces the `transformer` function to be re-applied to the current result set.
@@ -730,25 +763,33 @@ class QueryListComponent {
730
763
  /**
731
764
  * Handles a page-change event emitted by the `mat-paginator`.
732
765
  *
733
- * Converts the zero-based `pageEvent.pageIndex` to the one-based page number
734
- * expected by `goToPage()` and delegates the request.
766
+ * Loads the page for the zero-based `pageEvent.pageIndex`, like `goToPage()`, and clears the
767
+ * drop-in items once that page is shown. If it fails to load, they stay.
735
768
  *
736
769
  * Bound to `(page)` on the `mat-paginator` in the template.
737
770
  *
771
+ * Ignored while the paginator still shows the previous query's result — a new query is loading or
772
+ * failed to load — since its page numbers mean nothing for the current query.
773
+ *
738
774
  * @param pageEvent The pagination event emitted by `MatPaginatorModule`.
739
775
  */
740
776
  changePage(pageEvent) {
741
- this.#dropInItems.set([]);
742
- this.goToPage(pageEvent.pageIndex + 1);
777
+ const shown = this.#shownRequest();
778
+ if (!shown || shown.query !== this.query()) {
779
+ this.#syncPaginatorPage();
780
+ return;
781
+ }
782
+ this.#request({ query: shown.query, page: pageEvent.pageIndex + 1, clearDropIns: true });
743
783
  }
744
784
  /**
745
785
  * Fetches and displays the given page of the current query result.
746
786
  *
747
787
  * Sets `busy` to `true` for the duration of the request. On success, updates
748
788
  * the pagination state, clears all optimistic overrides (they are page-scoped),
749
- * and replaces `#items` with the new page's results.
750
- *
751
- * Also used internally by `refresh()` to reload the currently active page.
789
+ * and replaces `#items` with the new page's results. A request still in flight — an earlier
790
+ * page change or a new query — is cancelled. If loading the page fails, the list and the
791
+ * paginator stay on the page that was loaded before. A page past the last one loads the last
792
+ * page instead.
752
793
  *
753
794
  * @param page One-based page number to navigate to.
754
795
  */
@@ -756,21 +797,7 @@ class QueryListComponent {
756
797
  const query = this.query();
757
798
  if (!query)
758
799
  return;
759
- this.busy.set(true);
760
- this.#searchService.getPage(query, page, this.effectivePageSize(), this.includePermissions()).subscribe({
761
- next: (res) => {
762
- this.#setupPagination(res);
763
- this.#listItemUpdates.set({});
764
- this.#items.set(res.items);
765
- this.queryResult.emit({ totalCount: res.totalNumItems, items: res.items });
766
- this.busy.set(false);
767
- },
768
- error: (err) => {
769
- // TODO: how should errors be handles in case hat loading pages fail
770
- this.busy.set(false);
771
- console.error(err);
772
- }
773
- });
800
+ this.#request({ query, page });
774
801
  }
775
802
  //#endregion
776
803
  //#region Utilities
@@ -782,32 +809,80 @@ class QueryListComponent {
782
809
  const value = item[prop];
783
810
  return value == null ? undefined : String(value);
784
811
  }
812
+ /** Starts `request`, cancelling any request still in flight (see `#requests`). */
813
+ #request(request) {
814
+ // A copy: `#load` tells requests apart by identity, and `request` may be the one still in flight.
815
+ const started = { ...request };
816
+ // Before `next()`: `switchMap` cancels the superseded request first, and its `finalize` must already
817
+ // find a newer request pending, or it would clear `busy` while this one is still loading.
818
+ this.#pendingRequest.set(started);
819
+ this.#requests.next(started);
820
+ }
785
821
  /**
786
- * Executes a search query.
787
- * @param query The search query to execute.
788
- * This may be a SearchQuery object or a string. If it's a string, it is supposed to
789
- * be a CMIS query statement.
822
+ * Cancels the request still in flight, if any; the list keeps what it shows. With no newer request
823
+ * pending, the cancelled one's `finalize` clears `busy` and syncs the paginator.
790
824
  */
791
- #executeQuery(query) {
792
- if (query && !this.busy()) {
793
- this.busy.set(true);
794
- const size = this.effectivePageSize();
795
- (typeof query === 'string'
796
- ? this.#searchService.searchCmis(query, size, this.includePermissions())
797
- : this.#searchService.search({ ...query, size })).subscribe({
798
- next: (res) => {
799
- this.#setupPagination(res);
800
- this.#listItemUpdates.set({});
801
- this.#items.set(res.items);
802
- this.queryResult.emit({ totalCount: res.totalNumItems, items: res.items });
803
- this.busy.set(false);
804
- },
805
- error: (err) => {
806
- this.busy.set(false);
807
- console.error(err);
808
- }
809
- });
825
+ #cancel() {
826
+ this.#requests.next(null);
827
+ }
828
+ /**
829
+ * Loads one request. A failure is logged and leaves the current result in place, and it must not
830
+ * end `#requests` — every later search goes through that same stream.
831
+ */
832
+ #load(request) {
833
+ // `defer`: building the search can throw — e.g. a query that cannot be serialized — and a throw
834
+ // outside the pipe would get past `catchError`.
835
+ return defer(() => this.#search(request)).pipe(map((result) => ({ request, result })), catchError((err) => {
836
+ console.error(err);
837
+ return EMPTY;
838
+ }), finalize(() => {
839
+ // A superseded request leaves `busy` and the paginator to the one that replaced it.
840
+ if (this.#pendingRequest() !== request)
841
+ return;
842
+ this.#pendingRequest.set(null);
843
+ this.#syncPaginatorPage();
844
+ }));
845
+ }
846
+ /**
847
+ * The search call for `request`. The query may be a `SearchQuery` object or a string; a string is
848
+ * supposed to be a CMIS query statement.
849
+ */
850
+ #search({ query, page }) {
851
+ const size = this.effectivePageSize();
852
+ // `getPage` writes `from` onto a query object and pages it by the object's own `size`, so it gets a copy
853
+ // sized to this list's pages. Left on the consumer's object, `from` would open its next search — or a
854
+ // copy made for a sort change — on a later page.
855
+ const target = typeof query === 'string' ? query : { ...query, size };
856
+ return this.#searchService.getPage(target, page ?? 1, size, this.includePermissions());
857
+ }
858
+ #showResult(request, result) {
859
+ const lastPage = result.paging?.totalPages ?? 1;
860
+ if (request.page !== null && request.page > lastPage) {
861
+ // The page is gone, e.g. `refresh()` after its objects were deleted. Showing it empty would strand
862
+ // the user, and with a single page left there is not even a paginator to leave it with.
863
+ this.#request({ ...request, page: lastPage > 1 ? lastPage : null });
864
+ return;
810
865
  }
866
+ this.#shownRequest.set(request);
867
+ // Before `queryResult`: `TileListComponent` keeps as many leading items as there are drop-ins then.
868
+ if (request.clearDropIns)
869
+ this.#dropInItems.set([]);
870
+ this.#setupPagination(result);
871
+ this.#listItemUpdates.set({});
872
+ this.#items.set(result.items);
873
+ this.queryResult.emit({ totalCount: result.totalNumItems, items: result.items });
874
+ }
875
+ /**
876
+ * Puts the paginator on the page that is actually loaded. It moves to the requested page as soon as
877
+ * it is clicked, and its `[pageIndex]` binding cannot undo that when the click does not load: the
878
+ * request failed, or a newer one replaced it and loaded the page that was bound before. The bound
879
+ * value has not changed, so Angular does not re-apply it.
880
+ */
881
+ #syncPaginatorPage() {
882
+ const paginator = this.paginator();
883
+ const page = this.pagination()?.page;
884
+ if (paginator && page)
885
+ paginator.pageIndex = page - 1;
811
886
  }
812
887
  #setupPagination(searchResult) {
813
888
  this.pagination.set(searchResult.paging
@@ -835,7 +910,7 @@ class QueryListComponent {
835
910
  */
836
911
  #resyncSelectionEffect;
837
912
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: QueryListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
838
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: QueryListComponent, isStandalone: true, selector: "yuv-query-list", inputs: { query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, idProperty: { classPropertyName: "idProperty", publicName: "idProperty", isSignal: true, isRequired: false, transformFunction: null }, transformer: { classPropertyName: "transformer", publicName: "transformer", isSignal: true, isRequired: false, transformFunction: null }, preventChangeUntil: { classPropertyName: "preventChangeUntil", publicName: "preventChangeUntil", isSignal: true, isRequired: false, transformFunction: null }, autoSelect: { classPropertyName: "autoSelect", publicName: "autoSelect", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, enableDragSelect: { classPropertyName: "enableDragSelect", publicName: "enableDragSelect", isSignal: true, isRequired: false, transformFunction: null }, multiselect: { classPropertyName: "multiselect", publicName: "multiselect", isSignal: true, isRequired: false, transformFunction: null }, selfHandleSelection: { classPropertyName: "selfHandleSelection", publicName: "selfHandleSelection", isSignal: true, isRequired: false, transformFunction: null }, includePermissions: { classPropertyName: "includePermissions", publicName: "includePermissions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemSelect: "itemSelect", dragSelectChange: "dragSelectChange", itemDoubleClick: "itemDoubleClick", queryResult: "queryResult" }, host: { properties: { "class.pagination": "!!pagination()" } }, queries: [{ propertyName: "itemTemplate", first: true, predicate: ["yuvQueryListItem"], descendants: true, isSignal: true }, { propertyName: "emptyTemplate", first: true, predicate: ["yuvQueryListEmpty"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "list", first: true, predicate: ["list"], descendants: true, isSignal: true }], ngImport: i0, template: "<yuv-list\n #list\n [multiselect]=\"multiselect()\"\n [autoSelect]=\"autoSelect()\"\n [preventChangeUntil]=\"preventChangeUntil()\"\n [selfHandleClick]=\"true\"\n [selfHandleSelection]=\"selfHandleSelection()\"\n [externalSelectionVisuals]=\"idSelection()\"\n [yuvDragSelect]=\"{ disabled: !enableDragSelect() || !multiselect() || isTouchDevice }\"\n (dragSelectChange)=\"onDragSelectChange($event)\"\n (dragSelect)=\"onDragSelect($event)\"\n (itemSelect)=\"onInnerItemSelect($event)\"\n>\n @for (resultItem of resultItems(); track trackItem(resultItem, $index)) {\n <div\n yuvListItem\n yuvDragSelectItem\n [selected]=\"isRowSelected(resultItem)\"\n [class.drop-in]=\"$index < dropInSize()\"\n (click.single)=\"onItemClick($index, $event)\"\n (click.double)=\"onItemDoubleClick($index, $event)\"\n >\n <ng-container *ngTemplateOutlet=\"itemTemplate() || null; context: { $implicit: resultItem, index: $index }\" />\n </div>\n } @empty {\n <ng-container *ngTemplateOutlet=\"emptyTemplate() || null\" />\n }\n</yuv-list>\n@if (pagination(); as pag) {\n <mat-paginator\n class=\"paginator\"\n [length]=\"pag.total\"\n [pageSize]=\"effectivePageSize()\"\n (page)=\"changePage($event)\"\n hidePageSize\n />\n}\n", styles: [":host{--paging-background: var(--ymt-surface);--drop-in-item-outline: 2px dashed rgb(from var(--ymt-primary) r g b / .9);--drop-in-item-outline-offset: -3px;display:flex;flex-direction:column;max-height:100%}:host [yuvListItem].drop-in{outline:var(--drop-in-item-outline);outline-offset:var(--drop-in-item-outline-offset);overflow:hidden}:host yuv-list{flex:1;overflow-y:auto}:host mat-paginator{flex:0 0 auto;font:inherit;border-block-start:1px solid var(--ymt-outline-variant);background-color:var(--paging-background)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: YuvListModule }, { kind: "component", type: i2.ListComponent, selector: "yuv-list", inputs: ["preventChangeUntil", "multiselect", "selfHandleSelection", "externalSelectionVisuals", "selfHandleClick", "autoSelect", "disableSelection"], outputs: ["itemSelect", "itemFocus"] }, { kind: "directive", type: i2.ListItemDirective, selector: "[yuvListItem]", inputs: ["disabled", "active", "selected"] }, { kind: "directive", type: ClickDoubleDirective, selector: "[click.single],[click.double]", inputs: ["debounceTime"], outputs: ["click.double", "click.single"] }, { kind: "directive", type: DragSelectDirective, selector: "[yuvDragSelect]", inputs: ["yuvDragSelect"], outputs: ["dragSelectChange", "dragSelect"] }, { kind: "directive", type: DragSelectItemDirective, selector: "[yuvDragSelectItem]" }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
913
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.20", type: QueryListComponent, isStandalone: true, selector: "yuv-query-list", inputs: { query: { classPropertyName: "query", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, idProperty: { classPropertyName: "idProperty", publicName: "idProperty", isSignal: true, isRequired: false, transformFunction: null }, transformer: { classPropertyName: "transformer", publicName: "transformer", isSignal: true, isRequired: false, transformFunction: null }, preventChangeUntil: { classPropertyName: "preventChangeUntil", publicName: "preventChangeUntil", isSignal: true, isRequired: false, transformFunction: null }, autoSelect: { classPropertyName: "autoSelect", publicName: "autoSelect", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, enableDragSelect: { classPropertyName: "enableDragSelect", publicName: "enableDragSelect", isSignal: true, isRequired: false, transformFunction: null }, multiselect: { classPropertyName: "multiselect", publicName: "multiselect", isSignal: true, isRequired: false, transformFunction: null }, selfHandleSelection: { classPropertyName: "selfHandleSelection", publicName: "selfHandleSelection", isSignal: true, isRequired: false, transformFunction: null }, includePermissions: { classPropertyName: "includePermissions", publicName: "includePermissions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemSelect: "itemSelect", dragSelectChange: "dragSelectChange", itemDoubleClick: "itemDoubleClick", queryResult: "queryResult" }, host: { properties: { "class.pagination": "!!pagination()" } }, queries: [{ propertyName: "itemTemplate", first: true, predicate: ["yuvQueryListItem"], descendants: true, isSignal: true }, { propertyName: "emptyTemplate", first: true, predicate: ["yuvQueryListEmpty"], descendants: true, isSignal: true }], viewQueries: [{ propertyName: "list", first: true, predicate: ["list"], descendants: true, isSignal: true }, { propertyName: "paginator", first: true, predicate: MatPaginator, descendants: true, isSignal: true }], ngImport: i0, template: "<yuv-list\n #list\n [multiselect]=\"multiselect()\"\n [autoSelect]=\"autoSelect()\"\n [preventChangeUntil]=\"preventChangeUntil()\"\n [selfHandleClick]=\"true\"\n [selfHandleSelection]=\"selfHandleSelection()\"\n [externalSelectionVisuals]=\"idSelection()\"\n [yuvDragSelect]=\"{ disabled: !enableDragSelect() || !multiselect() || isTouchDevice }\"\n (dragSelectChange)=\"onDragSelectChange($event)\"\n (dragSelect)=\"onDragSelect($event)\"\n (itemSelect)=\"onInnerItemSelect($event)\"\n>\n @for (resultItem of resultItems(); track trackItem(resultItem, $index)) {\n <div\n yuvListItem\n yuvDragSelectItem\n [selected]=\"isRowSelected(resultItem)\"\n [class.drop-in]=\"$index < dropInSize()\"\n (click.single)=\"onItemClick($index, $event)\"\n (click.double)=\"onItemDoubleClick($index, $event)\"\n >\n <ng-container *ngTemplateOutlet=\"itemTemplate() || null; context: { $implicit: resultItem, index: $index }\" />\n </div>\n } @empty {\n <ng-container *ngTemplateOutlet=\"emptyTemplate() || null\" />\n }\n</yuv-list>\n@if (pagination(); as pag) {\n <mat-paginator\n class=\"paginator\"\n [length]=\"pag.total\"\n [pageIndex]=\"pag.page - 1\"\n [pageSize]=\"effectivePageSize()\"\n (page)=\"changePage($event)\"\n hidePageSize\n />\n}\n", styles: [":host{--paging-background: var(--ymt-surface);--drop-in-item-outline: 2px dashed rgb(from var(--ymt-primary) r g b / .9);--drop-in-item-outline-offset: -3px;display:flex;flex-direction:column;max-height:100%}:host [yuvListItem].drop-in{outline:var(--drop-in-item-outline);outline-offset:var(--drop-in-item-outline-offset);overflow:hidden}:host yuv-list{flex:1;overflow-y:auto}:host mat-paginator{flex:0 0 auto;font:inherit;border-block-start:1px solid var(--ymt-outline-variant);background-color:var(--paging-background)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: YuvListModule }, { kind: "component", type: i2.ListComponent, selector: "yuv-list", inputs: ["preventChangeUntil", "multiselect", "selfHandleSelection", "externalSelectionVisuals", "selfHandleClick", "autoSelect", "disableSelection"], outputs: ["itemSelect", "itemFocus"] }, { kind: "directive", type: i2.ListItemDirective, selector: "[yuvListItem]", inputs: ["disabled", "active", "selected"] }, { kind: "directive", type: ClickDoubleDirective, selector: "[click.single],[click.double]", inputs: ["debounceTime"], outputs: ["click.double", "click.single"] }, { kind: "directive", type: DragSelectDirective, selector: "[yuvDragSelect]", inputs: ["yuvDragSelect"], outputs: ["dragSelectChange", "dragSelect"] }, { kind: "directive", type: DragSelectItemDirective, selector: "[yuvDragSelectItem]" }, { kind: "ngmodule", type: MatPaginatorModule }, { kind: "component", type: i3.MatPaginator, selector: "mat-paginator", inputs: ["color", "pageIndex", "length", "pageSize", "pageSizeOptions", "hidePageSize", "showFirstLastButtons", "selectConfig", "disabled"], outputs: ["page"], exportAs: ["matPaginator"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
839
914
  }
840
915
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImport: i0, type: QueryListComponent, decorators: [{
841
916
  type: Component,
@@ -846,8 +921,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.20", ngImpo
846
921
  DragSelectDirective,
847
922
  DragSelectItemDirective,
848
923
  MatPaginatorModule
849
- ], host: { '[class.pagination]': '!!pagination()' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<yuv-list\n #list\n [multiselect]=\"multiselect()\"\n [autoSelect]=\"autoSelect()\"\n [preventChangeUntil]=\"preventChangeUntil()\"\n [selfHandleClick]=\"true\"\n [selfHandleSelection]=\"selfHandleSelection()\"\n [externalSelectionVisuals]=\"idSelection()\"\n [yuvDragSelect]=\"{ disabled: !enableDragSelect() || !multiselect() || isTouchDevice }\"\n (dragSelectChange)=\"onDragSelectChange($event)\"\n (dragSelect)=\"onDragSelect($event)\"\n (itemSelect)=\"onInnerItemSelect($event)\"\n>\n @for (resultItem of resultItems(); track trackItem(resultItem, $index)) {\n <div\n yuvListItem\n yuvDragSelectItem\n [selected]=\"isRowSelected(resultItem)\"\n [class.drop-in]=\"$index < dropInSize()\"\n (click.single)=\"onItemClick($index, $event)\"\n (click.double)=\"onItemDoubleClick($index, $event)\"\n >\n <ng-container *ngTemplateOutlet=\"itemTemplate() || null; context: { $implicit: resultItem, index: $index }\" />\n </div>\n } @empty {\n <ng-container *ngTemplateOutlet=\"emptyTemplate() || null\" />\n }\n</yuv-list>\n@if (pagination(); as pag) {\n <mat-paginator\n class=\"paginator\"\n [length]=\"pag.total\"\n [pageSize]=\"effectivePageSize()\"\n (page)=\"changePage($event)\"\n hidePageSize\n />\n}\n", styles: [":host{--paging-background: var(--ymt-surface);--drop-in-item-outline: 2px dashed rgb(from var(--ymt-primary) r g b / .9);--drop-in-item-outline-offset: -3px;display:flex;flex-direction:column;max-height:100%}:host [yuvListItem].drop-in{outline:var(--drop-in-item-outline);outline-offset:var(--drop-in-item-outline-offset);overflow:hidden}:host yuv-list{flex:1;overflow-y:auto}:host mat-paginator{flex:0 0 auto;font:inherit;border-block-start:1px solid var(--ymt-outline-variant);background-color:var(--paging-background)}\n"] }]
850
- }], ctorParameters: () => [], propDecorators: { list: [{ type: i0.ViewChild, args: ['list', { isSignal: true }] }], itemTemplate: [{ type: i0.ContentChild, args: ['yuvQueryListItem', { isSignal: true }] }], emptyTemplate: [{ type: i0.ContentChild, args: ['yuvQueryListEmpty', { isSignal: true }] }], query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], idProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "idProperty", required: false }] }], transformer: [{ type: i0.Input, args: [{ isSignal: true, alias: "transformer", required: false }] }], preventChangeUntil: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventChangeUntil", required: false }] }], autoSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoSelect", required: false }] }], itemSelect: [{ type: i0.Output, args: ["itemSelect"] }], dragSelectChange: [{ type: i0.Output, args: ["dragSelectChange"] }], itemDoubleClick: [{ type: i0.Output, args: ["itemDoubleClick"] }], queryResult: [{ type: i0.Output, args: ["queryResult"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], enableDragSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableDragSelect", required: false }] }], multiselect: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiselect", required: false }] }], selfHandleSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selfHandleSelection", required: false }] }], includePermissions: [{ type: i0.Input, args: [{ isSignal: true, alias: "includePermissions", required: false }] }] } });
924
+ ], host: { '[class.pagination]': '!!pagination()' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<yuv-list\n #list\n [multiselect]=\"multiselect()\"\n [autoSelect]=\"autoSelect()\"\n [preventChangeUntil]=\"preventChangeUntil()\"\n [selfHandleClick]=\"true\"\n [selfHandleSelection]=\"selfHandleSelection()\"\n [externalSelectionVisuals]=\"idSelection()\"\n [yuvDragSelect]=\"{ disabled: !enableDragSelect() || !multiselect() || isTouchDevice }\"\n (dragSelectChange)=\"onDragSelectChange($event)\"\n (dragSelect)=\"onDragSelect($event)\"\n (itemSelect)=\"onInnerItemSelect($event)\"\n>\n @for (resultItem of resultItems(); track trackItem(resultItem, $index)) {\n <div\n yuvListItem\n yuvDragSelectItem\n [selected]=\"isRowSelected(resultItem)\"\n [class.drop-in]=\"$index < dropInSize()\"\n (click.single)=\"onItemClick($index, $event)\"\n (click.double)=\"onItemDoubleClick($index, $event)\"\n >\n <ng-container *ngTemplateOutlet=\"itemTemplate() || null; context: { $implicit: resultItem, index: $index }\" />\n </div>\n } @empty {\n <ng-container *ngTemplateOutlet=\"emptyTemplate() || null\" />\n }\n</yuv-list>\n@if (pagination(); as pag) {\n <mat-paginator\n class=\"paginator\"\n [length]=\"pag.total\"\n [pageIndex]=\"pag.page - 1\"\n [pageSize]=\"effectivePageSize()\"\n (page)=\"changePage($event)\"\n hidePageSize\n />\n}\n", styles: [":host{--paging-background: var(--ymt-surface);--drop-in-item-outline: 2px dashed rgb(from var(--ymt-primary) r g b / .9);--drop-in-item-outline-offset: -3px;display:flex;flex-direction:column;max-height:100%}:host [yuvListItem].drop-in{outline:var(--drop-in-item-outline);outline-offset:var(--drop-in-item-outline-offset);overflow:hidden}:host yuv-list{flex:1;overflow-y:auto}:host mat-paginator{flex:0 0 auto;font:inherit;border-block-start:1px solid var(--ymt-outline-variant);background-color:var(--paging-background)}\n"] }]
925
+ }], ctorParameters: () => [], propDecorators: { list: [{ type: i0.ViewChild, args: ['list', { isSignal: true }] }], itemTemplate: [{ type: i0.ContentChild, args: ['yuvQueryListItem', { isSignal: true }] }], emptyTemplate: [{ type: i0.ContentChild, args: ['yuvQueryListEmpty', { isSignal: true }] }], query: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], idProperty: [{ type: i0.Input, args: [{ isSignal: true, alias: "idProperty", required: false }] }], transformer: [{ type: i0.Input, args: [{ isSignal: true, alias: "transformer", required: false }] }], preventChangeUntil: [{ type: i0.Input, args: [{ isSignal: true, alias: "preventChangeUntil", required: false }] }], autoSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoSelect", required: false }] }], itemSelect: [{ type: i0.Output, args: ["itemSelect"] }], dragSelectChange: [{ type: i0.Output, args: ["dragSelectChange"] }], itemDoubleClick: [{ type: i0.Output, args: ["itemDoubleClick"] }], queryResult: [{ type: i0.Output, args: ["queryResult"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], enableDragSelect: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableDragSelect", required: false }] }], multiselect: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiselect", required: false }] }], selfHandleSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selfHandleSelection", required: false }] }], includePermissions: [{ type: i0.Input, args: [{ isSignal: true, alias: "includePermissions", required: false }] }], paginator: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MatPaginator), { isSignal: true }] }] } });
851
926
 
852
927
  const cmp = [QueryListComponent];
853
928
  class YuvQueryListModule {