@sinequa/atomic-angular 1.7.1 → 1.8.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.
- package/fesm2022/sinequa-atomic-angular.mjs +888 -81
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/index.d.ts +576 -194
- package/package.json +1 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Injectable, inject, HostBinding, Component, Pipe, InjectionToken, computed, ChangeDetectorRef, DestroyRef, LOCALE_ID, Inject, Optional, input, output, signal, effect, assertInInjectionContext, runInInjectionContext, EnvironmentInjector, Injector, EventEmitter, Directive, viewChild, ElementRef, afterNextRender, untracked, linkedSignal, model, TemplateRef, HostListener, Renderer2, contentChildren, contentChild, booleanAttribute, resource, ChangeDetectionStrategy, ViewContainerRef, numberAttribute, viewChildren, afterRenderEffect, afterEveryRender } from '@angular/core';
|
|
3
|
-
import { BehaviorSubject, Subscription, Subject, of, catchError, map, tap, EMPTY, throwError, filter, shareReplay,
|
|
3
|
+
import { BehaviorSubject, Subscription, Subject, of, catchError, map, tap, EMPTY, throwError, from, filter as filter$1, shareReplay, switchMap, firstValueFrom, fromEvent, debounceTime } from 'rxjs';
|
|
4
4
|
import { TranslocoService, TranslocoPipe, provideTranslocoScope } from '@jsverse/transloco';
|
|
5
5
|
import { DropdownComponent, DropdownContentComponent, InputComponent, ButtonComponent, cn, FaIconComponent, EllipsisIcon, ChevronRightIcon, MenuComponent, MenuContentComponent, MenuItemComponent, LinkComponent, CardComponent, CardHeaderComponent, CardContentComponent, BadgeComponent, DialogComponent, DialogHeaderComponent, DialogTitleComponent, DialogContentComponent, DialogFooterComponent, ListItemComponent, SquareCheckIcon, SquareMinusIcon, SquareIcon, SwitchComponent, SelectOptionDirective, DialogService, XMarkIcon, InboxIcon, SparklesIcon, FileOutputIcon, TabsComponent, TabsListComponent, TabComponent, SidebarMenuComponent, SidebarMenuItemComponent, SidebarMenuButtonComponent, TooltipDirective, FrownIcon, ChevronLeftIcon, ChevronsLeftIcon, ChevronsRightIcon, ArrowUpAzIcon, ArrowDownZaIcon, ArrowUpRightFromSquareIcon, ToggleRightIcon, ToggleLeftIcon, Separator, SheetCloseDirective, ArrowLeftIcon, SheetService, DateRangePickerDirective, DatepickerDirective, FilterIcon, FilterXIcon, IconButtonComponent, HighlighterIcon, TagsIcon, SpinnerIcon, MagnifyingGlassIcon, LoadingCircleIcon, CircleCheckIcon, PopoverComponent, BellIcon, TrashIcon, BarsIcon, CardFooterComponent, InputGroupInput, InputGroupComponent, InputGroupAddonComponent, EyeSlashIcon, EyeIcon, BookmarkIcon, PopoverContentComponent, UserIcon, FolderIcon, DialogDescriptionComponent, VerticalDividerComponent, CommentIcon, ThumbsUpIcon, ThumbsDownIcon, DropdownDirective, SearchIcon, TriangleAlertIcon, ListFilterIcon, BreakpointObserverService, TrashCanIcon, CircleXIcon, InfoCircleIcon, HorizontalDividerComponent, HistoryIcon, StarIcon, FlagEnglishIcon, FlagFrenchIcon, FlagGermanIcon, EditIcon, UndoIcon, SaveIcon, AvatarComponent, AvatarFallbackComponent, AvatarImageComponent } from '@sinequa/ui';
|
|
6
6
|
import highlightWords from 'highlight-words';
|
|
7
7
|
import { ActivatedRoute, Router, NavigationEnd, RouterLink, RouterLinkActive, RouterModule } from '@angular/router';
|
|
8
8
|
import { withDevtools } from '@angular-architects/ngrx-toolkit';
|
|
9
9
|
import { signalStore, signalStoreFeature, withState, withMethods, patchState, getState, withComputed } from '@ngrx/signals';
|
|
10
|
-
import { EngineType, fetchApp, extraColumns, sysLang, fetchPrincipal, globalConfig, getQueryParamsFromUrl, clearSessionTokens, login, info, error, setGlobalConfig, initializeAppConfig, notify, addConcepts, queryParamsFromUrl, patchUserSettings, deleteUserSettings, fetchUserSettings, warn, buildPathsAndLevels, escapeExpr, isAuthenticated, isExpired, debug, AuditEventType, fetchSuggest, isObject, Audit, getMetadata, bisect, isNotInputEvent, fetchSponsoredLinks,
|
|
10
|
+
import { EngineType, fetchApp, extraColumns, sysLang, fetchPrincipal, globalConfig, filter, getQueryParamsFromUrl, clearSessionTokens, login, info, error, setGlobalConfig, initializeAppConfig, notify, addConcepts, queryParamsFromUrl, patchUserSettings, deleteUserSettings, fetchUserSettings, warn, buildPathsAndLevels, escapeExpr, fetchQuery, isAuthenticated, isExpired, debug, AuditEventType, fetchSuggest, isObject, Audit, getMetadata, bisect, isNotInputEvent, fetchSponsoredLinks, translateAggregationToDateOptions, aggItemRegex, parseValueAndOperatorFromItem, fetchSuggestField, fetchSimilarDocuments, logout, fetchChangePassword, fetchSendPasswordResetEmail, expiresSoon, suggestionsToTreeAggregationNodes, labels, fetchLabels, guid, getRelativeDate, createUserProfile, deleteUserProfileProperty, patchUserProfile, isJsonable, addAuditAdditionalInfo, getToken, setToken, createHeaders } from '@sinequa/atomic';
|
|
11
11
|
import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
|
|
12
12
|
import { DatePipe, DATE_PIPE_DEFAULT_TIMEZONE, DATE_PIPE_DEFAULT_OPTIONS, Location, NgTemplateOutlet, NgStyle, NgClass, NgComponentOutlet } from '@angular/common';
|
|
13
13
|
import { HttpClient, HttpParams, httpResource, HttpResponse, HttpContextToken, HttpHeaders } from '@angular/common/http';
|
|
@@ -1365,6 +1365,64 @@ class InlineWorker {
|
|
|
1365
1365
|
}
|
|
1366
1366
|
}
|
|
1367
1367
|
|
|
1368
|
+
/**
|
|
1369
|
+
* Converts a `LegacyFilter` to the structured `Filter` model, built with the
|
|
1370
|
+
* `filter.*` helpers of `@sinequa/atomic`. The result is assignable as-is to
|
|
1371
|
+
* `query.filters` / `fetchQuery({ filters })`.
|
|
1372
|
+
*
|
|
1373
|
+
* Covers the shapes produced by the aggregation components when applying
|
|
1374
|
+
* filters (`toFilter`, `getFiltersForTree`, `getFormValueFilter`,
|
|
1375
|
+
* `buildAppliedFilter`): `in` values, `and`/`or` expression trees, `between`
|
|
1376
|
+
* ranges, scalar leaves (`eq`/`contains`/`gte`/`lte`/…) and the operator-less
|
|
1377
|
+
* `{ field, value }` form (treated as `eq`).
|
|
1378
|
+
*
|
|
1379
|
+
* Returns `undefined` for an absent filter or a degenerate shape (leaf without
|
|
1380
|
+
* `field`/`value`, empty combinator) — consistent with the `filter.and`/`or`
|
|
1381
|
+
* collapsing rules and with an absent `query.filters`.
|
|
1382
|
+
*/
|
|
1383
|
+
function legacyFilterToFilter(legacy) {
|
|
1384
|
+
if (!legacy)
|
|
1385
|
+
return undefined;
|
|
1386
|
+
const { operator, field } = legacy;
|
|
1387
|
+
switch (operator) {
|
|
1388
|
+
case "and":
|
|
1389
|
+
case "or": {
|
|
1390
|
+
const children = (legacy.filters ?? []).map((f) => legacyFilterToFilter(f));
|
|
1391
|
+
return operator === "and" ? filter.and(...children) : filter.or(...children);
|
|
1392
|
+
}
|
|
1393
|
+
case "not": {
|
|
1394
|
+
const children = (legacy.filters ?? []).map((f) => legacyFilterToFilter(f));
|
|
1395
|
+
return filter.not(...children);
|
|
1396
|
+
}
|
|
1397
|
+
case "in":
|
|
1398
|
+
return field && legacy.values?.length ? filter.in(field, legacy.values) : undefined;
|
|
1399
|
+
case "between":
|
|
1400
|
+
return field && legacy.start !== undefined && legacy.end !== undefined
|
|
1401
|
+
? filter.between(field, legacy.start, legacy.end)
|
|
1402
|
+
: undefined;
|
|
1403
|
+
case "null":
|
|
1404
|
+
return field ? filter.isNull(field) : undefined;
|
|
1405
|
+
case "notnull":
|
|
1406
|
+
return field ? filter.isNotNull(field) : undefined;
|
|
1407
|
+
case "eq":
|
|
1408
|
+
case "neq":
|
|
1409
|
+
case "gt":
|
|
1410
|
+
case "gte":
|
|
1411
|
+
case "lt":
|
|
1412
|
+
case "lte":
|
|
1413
|
+
case "like":
|
|
1414
|
+
case "contains":
|
|
1415
|
+
case "regex":
|
|
1416
|
+
return field && legacy.value !== undefined ? filter[operator](field, legacy.value) : undefined;
|
|
1417
|
+
case undefined:
|
|
1418
|
+
// operator-less leaf ({ field, value }) — the `toFilter` form for
|
|
1419
|
+
// non-string values; semantically an equality
|
|
1420
|
+
return field && legacy.value !== undefined ? filter.eq(field, legacy.value) : undefined;
|
|
1421
|
+
default:
|
|
1422
|
+
return undefined;
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1368
1426
|
/**
|
|
1369
1427
|
* Retrieves the query name from the current route.
|
|
1370
1428
|
*
|
|
@@ -3209,11 +3267,13 @@ class AggregationsService {
|
|
|
3209
3267
|
queryParamsStore = inject(QueryParamsStore);
|
|
3210
3268
|
nonSearchableColumns = inject(NON_SEARCHABLE_COLUMNS);
|
|
3211
3269
|
API_URL = `${globalConfig.backendUrl}/api/v1`;
|
|
3212
|
-
loadMore(query, aggregation, audit) {
|
|
3270
|
+
loadMore(query, aggregation, audit, options) {
|
|
3213
3271
|
const { app } = globalConfig;
|
|
3214
3272
|
// skip cached items for computation
|
|
3215
3273
|
const skip = aggregation.items?.filter((i) => !i.$cached)?.length || 0;
|
|
3216
|
-
|
|
3274
|
+
// options.count carries the page size of a controlled (non-attached) query —
|
|
3275
|
+
// getAggregationCount only knows queries attached to the current app
|
|
3276
|
+
const count = options?.count ?? (this.appStore.getAggregationCount(query.name, aggregation.name) || 10);
|
|
3217
3277
|
const aggregations = { [aggregation.name]: { skip, count } };
|
|
3218
3278
|
const q = { ...query, action: "aggregate", aggregations };
|
|
3219
3279
|
// add audit record if available
|
|
@@ -3242,7 +3302,7 @@ class AggregationsService {
|
|
|
3242
3302
|
info("aggregation.loadMore success - data: ", response);
|
|
3243
3303
|
}));
|
|
3244
3304
|
}
|
|
3245
|
-
open(query, aggregation, item) {
|
|
3305
|
+
open(query, aggregation, item, options) {
|
|
3246
3306
|
const value = `/${item.$path}/*`;
|
|
3247
3307
|
const expression = `${aggregation.column}:${escapeExpr(value)}`;
|
|
3248
3308
|
const q = {
|
|
@@ -3252,7 +3312,18 @@ class AggregationsService {
|
|
|
3252
3312
|
open: [{ expression, aggregation: aggregation.name }]
|
|
3253
3313
|
};
|
|
3254
3314
|
// we dont' want to use the query params (filters) when opening nodes
|
|
3255
|
-
|
|
3315
|
+
// Guard-free mode calls fetchQuery directly: QueryService.search reads the shared
|
|
3316
|
+
// QueryParamsStore and publishes into its app-wide `result` signal, which a
|
|
3317
|
+
// controlled (per-instance) aggregation must not touch.
|
|
3318
|
+
const results$ = options?.guardFree
|
|
3319
|
+
? from(fetchQuery(q)).pipe(tap((results) => {
|
|
3320
|
+
results.aggregations?.forEach((agg) => {
|
|
3321
|
+
if (agg.isTree)
|
|
3322
|
+
buildPathsAndLevels(agg);
|
|
3323
|
+
});
|
|
3324
|
+
}))
|
|
3325
|
+
: this.queryService.search(q, false);
|
|
3326
|
+
return results$.pipe(map((results) => {
|
|
3256
3327
|
// find the node in the tree and replace it with the new one
|
|
3257
3328
|
const items = results.aggregations[0].items;
|
|
3258
3329
|
// if the open call returns no items, we need to update the node to indicate it has no children
|
|
@@ -3285,6 +3356,19 @@ class AggregationsService {
|
|
|
3285
3356
|
const agg = this.getAggregation(name, column);
|
|
3286
3357
|
if (!agg)
|
|
3287
3358
|
return null;
|
|
3359
|
+
return this.enrichAggregation(agg);
|
|
3360
|
+
}
|
|
3361
|
+
/**
|
|
3362
|
+
* Applies the app-level customization (custom JSON display/icon/hidden/searchable,
|
|
3363
|
+
* non-searchable columns) and marks the `$selected` items on the given aggregation.
|
|
3364
|
+
*
|
|
3365
|
+
* @param agg - The aggregation to enrich (mutated in place and returned).
|
|
3366
|
+
* @param selectionValues - Controlled selection: the selected values (list) or
|
|
3367
|
+
* `"/A/B/*"` paths (tree). When `undefined`, the selection is derived from the
|
|
3368
|
+
* shared `QueryParamsStore` filters (default behavior). An empty array means
|
|
3369
|
+
* "nothing selected" and skips the store read entirely.
|
|
3370
|
+
*/
|
|
3371
|
+
enrichAggregation(agg, selectionValues) {
|
|
3288
3372
|
const { items = [], display = agg.name, icon, hidden, expandedLevel, searchable = true } = this.appStore.getAggregationCustomization(agg.column, agg.name) || {};
|
|
3289
3373
|
agg.display = display;
|
|
3290
3374
|
agg.icon = icon;
|
|
@@ -3298,9 +3382,15 @@ class AggregationsService {
|
|
|
3298
3382
|
agg.searchable = false;
|
|
3299
3383
|
}
|
|
3300
3384
|
if (agg.items) {
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3385
|
+
let flattenedValues;
|
|
3386
|
+
if (selectionValues !== undefined) {
|
|
3387
|
+
flattenedValues = selectionValues;
|
|
3388
|
+
}
|
|
3389
|
+
else {
|
|
3390
|
+
// get current filters for the aggregation
|
|
3391
|
+
const filters = this.queryParamsStore.getFilter({ field: agg.column, name: agg.name });
|
|
3392
|
+
flattenedValues = filters ? this.flattenFilters([filters]) : [];
|
|
3393
|
+
}
|
|
3304
3394
|
if (agg.isTree) {
|
|
3305
3395
|
const selectedAgg = agg.items.map((item) => this.selectNode(item, flattenedValues));
|
|
3306
3396
|
const openedAgg = selectedAgg.map((item) => this.openParentNodes(item, flattenedValues));
|
|
@@ -4201,7 +4291,7 @@ class NavigationService {
|
|
|
4201
4291
|
*
|
|
4202
4292
|
* @type Observable<RouterEvent>
|
|
4203
4293
|
*/
|
|
4204
|
-
navigationEnd$ = this.router.events.pipe(map((event) => event), filter((event) => event instanceof NavigationEnd), tap((event) => {
|
|
4294
|
+
navigationEnd$ = this.router.events.pipe(map((event) => event), filter$1((event) => event instanceof NavigationEnd), tap((event) => {
|
|
4205
4295
|
const url = event.url.slice(1).split("?")[0]; // Extract route name
|
|
4206
4296
|
if (url && url !== this.urlAfterNavigation) {
|
|
4207
4297
|
this.auditService.notifyRouteChange(url);
|
|
@@ -5741,9 +5831,7 @@ class CollectionsDialog {
|
|
|
5741
5831
|
closed = output();
|
|
5742
5832
|
createInputElement = viewChild("createInput", ...(ngDevMode ? [{ debugName: "createInputElement" }] : []));
|
|
5743
5833
|
dialogElement = viewChild(DialogComponent, ...(ngDevMode ? [{ debugName: "dialogElement" }] : []));
|
|
5744
|
-
queryParamStore = inject(QueryParamsStore);
|
|
5745
5834
|
userSettingsStore = inject(UserSettingsStore);
|
|
5746
|
-
collection = signal(undefined, ...(ngDevMode ? [{ debugName: "collection" }] : []));
|
|
5747
5835
|
article = signal([], ...(ngDevMode ? [{ debugName: "article" }] : []));
|
|
5748
5836
|
newCollectionName = signal("", ...(ngDevMode ? [{ debugName: "newCollectionName" }] : []));
|
|
5749
5837
|
creating = signal(false, ...(ngDevMode ? [{ debugName: "creating" }] : []));
|
|
@@ -5760,8 +5848,6 @@ class CollectionsDialog {
|
|
|
5760
5848
|
this.article.set([article]);
|
|
5761
5849
|
else
|
|
5762
5850
|
this.article.set(article);
|
|
5763
|
-
const { basket } = getState(this.queryParamStore);
|
|
5764
|
-
this.collection.set(basket);
|
|
5765
5851
|
this.dialogElement()?.showModal();
|
|
5766
5852
|
}
|
|
5767
5853
|
containsArticleByName(collectionName) {
|
|
@@ -5810,6 +5896,7 @@ class CollectionsDialog {
|
|
|
5810
5896
|
// remove it
|
|
5811
5897
|
const articleIds = this.article().map((a) => a.id);
|
|
5812
5898
|
collection.ids = collection.ids?.filter((id) => !articleIds.includes(id)) ?? [];
|
|
5899
|
+
this.removedFromCollection.set(true);
|
|
5813
5900
|
}
|
|
5814
5901
|
else {
|
|
5815
5902
|
// add it
|
|
@@ -5821,8 +5908,6 @@ class CollectionsDialog {
|
|
|
5821
5908
|
}
|
|
5822
5909
|
try {
|
|
5823
5910
|
await this.userSettingsStore.updateBasket(collection, collectionIndex);
|
|
5824
|
-
const currentCollection = this.collection();
|
|
5825
|
-
this.removedFromCollection.set(!!currentCollection && !this.containsArticleByName(currentCollection));
|
|
5826
5911
|
}
|
|
5827
5912
|
catch (e) {
|
|
5828
5913
|
error("Error updating collection:", e);
|
|
@@ -6493,6 +6578,43 @@ function injectRouteNavigation(path, showCount) {
|
|
|
6493
6578
|
return { tabs, currentPath, searchText };
|
|
6494
6579
|
}
|
|
6495
6580
|
|
|
6581
|
+
/**
|
|
6582
|
+
* Marker directive: project a custom template for the content/label region of
|
|
6583
|
+
* each aggregation item. Put it on an `<ng-template>` inside `<aggregation-list>`
|
|
6584
|
+
* or `<aggregation-tree>`:
|
|
6585
|
+
*
|
|
6586
|
+
* ```html
|
|
6587
|
+
* <aggregation-list name="Languages" column="language" [aggregation]="agg">
|
|
6588
|
+
* <ng-template aggregationItem let-item let-count="count" let-selected="selected">
|
|
6589
|
+
* <span>{{ item.display }}</span>
|
|
6590
|
+
* <span class="badge">{{ count }}</span>
|
|
6591
|
+
* </ng-template>
|
|
6592
|
+
* </aggregation-list>
|
|
6593
|
+
* ```
|
|
6594
|
+
*
|
|
6595
|
+
* When no template is projected, the component renders its default row content.
|
|
6596
|
+
* The `let-*` variables are typed under `strictTemplates` via
|
|
6597
|
+
* {@link AggregationItemDirective.ngTemplateContextGuard}; the host must import
|
|
6598
|
+
* this directive in its component `imports`.
|
|
6599
|
+
*/
|
|
6600
|
+
class AggregationItemDirective {
|
|
6601
|
+
template = inject(TemplateRef);
|
|
6602
|
+
/**
|
|
6603
|
+
* Narrows the `let-*` of `<ng-template aggregationItem>` under `strictTemplates`.
|
|
6604
|
+
* Tree-only fields (`level`/`expanded`/`hasChildren`) are typed as optional; a tree
|
|
6605
|
+
* template can re-annotate them via {@link AggregationTreeItemContext} if desired.
|
|
6606
|
+
*/
|
|
6607
|
+
static ngTemplateContextGuard(_dir, ctx) {
|
|
6608
|
+
return true;
|
|
6609
|
+
}
|
|
6610
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationItemDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
6611
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.25", type: AggregationItemDirective, isStandalone: true, selector: "[aggregationItem]", ngImport: i0 });
|
|
6612
|
+
}
|
|
6613
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationItemDirective, decorators: [{
|
|
6614
|
+
type: Directive,
|
|
6615
|
+
args: [{ selector: "[aggregationItem]" }]
|
|
6616
|
+
}] });
|
|
6617
|
+
|
|
6496
6618
|
/**
|
|
6497
6619
|
* Directive to mark a child element for special processing.
|
|
6498
6620
|
* This can be used to apply specific styles or behaviors to child elements.
|
|
@@ -8917,12 +9039,98 @@ function injectAggregationBase(refs) {
|
|
|
8917
9039
|
const aggregationsService = inject(AggregationsService);
|
|
8918
9040
|
const injector = inject(Injector);
|
|
8919
9041
|
const destroyRef = inject(DestroyRef);
|
|
9042
|
+
/* controlled mode — each axis is controlled when its input is provided.
|
|
9043
|
+
Writes (store/URL/sessionStorage) are gated by the global `isControlled` flag. */
|
|
9044
|
+
const isQueryControlled = computed(() => refs.query?.() !== undefined, ...(ngDevMode ? [{ debugName: "isQueryControlled" }] : []));
|
|
9045
|
+
const isDataControlled = computed(() => refs.aggregation?.() !== undefined, ...(ngDevMode ? [{ debugName: "isDataControlled" }] : []));
|
|
9046
|
+
const isSelectionControlled = computed(() => refs.selection?.() !== undefined, ...(ngDevMode ? [{ debugName: "isSelectionControlled" }] : []));
|
|
9047
|
+
const isControlled = computed(() => isQueryControlled() || isDataControlled() || isSelectionControlled(), ...(ngDevMode ? [{ debugName: "isControlled" }] : []));
|
|
9048
|
+
/** The query the aggregation's own operations run against. */
|
|
9049
|
+
function resolvedQuery() {
|
|
9050
|
+
return refs.query?.() ?? queryParamsStore.getQuery();
|
|
9051
|
+
}
|
|
9052
|
+
/**
|
|
9053
|
+
* Load-more page size when the query axis is controlled: the host's query
|
|
9054
|
+
* carries its own aggregation counts (`query.aggregations[name].count`), since
|
|
9055
|
+
* `AppStore.getAggregationCount` only knows queries attached to the current app.
|
|
9056
|
+
* Returns `undefined` in default mode — the service then resolves the count
|
|
9057
|
+
* from the app configuration, as today.
|
|
9058
|
+
*/
|
|
9059
|
+
function resolvedAggregationCount(agg) {
|
|
9060
|
+
const q = refs.query?.();
|
|
9061
|
+
if (!q)
|
|
9062
|
+
return undefined;
|
|
9063
|
+
const aggregations = q.aggregations;
|
|
9064
|
+
if (aggregations && !Array.isArray(aggregations)) {
|
|
9065
|
+
return aggregations[agg.name]?.count ?? 10;
|
|
9066
|
+
}
|
|
9067
|
+
return 10;
|
|
9068
|
+
}
|
|
9069
|
+
/**
|
|
9070
|
+
* Local aggregation state in controlled mode, seeded from the `aggregation`
|
|
9071
|
+
* input (or the shared store when only the query/selection axis is controlled).
|
|
9072
|
+
* `loadMore`/`open` write their results here instead of the shared store.
|
|
9073
|
+
*
|
|
9074
|
+
* The seed is deep-cloned: items are mutated in place (`$selected`, `$opened`),
|
|
9075
|
+
* so working on the host's (or the store's) objects would leak state across
|
|
9076
|
+
* instances and mutate the host's data. Any reference change of the input
|
|
9077
|
+
* reseeds the signal and discards local enrichments (loaded pages, opened nodes).
|
|
9078
|
+
*/
|
|
9079
|
+
const controlledAggregation = linkedSignal(() => {
|
|
9080
|
+
if (!isControlled())
|
|
9081
|
+
return null;
|
|
9082
|
+
const source = refs.aggregation?.() ??
|
|
9083
|
+
(getState(aggregationsStore), aggregationsService.getAggregation(refs.name() ?? "", refs.column()));
|
|
9084
|
+
return source ? structuredClone(source) : null;
|
|
9085
|
+
}, ...(ngDevMode ? [{ debugName: "controlledAggregation" }] : []));
|
|
8920
9086
|
const debouncedSearchText = debouncedSignal(refs.searchText, 300);
|
|
8921
9087
|
const normalizedSearchText = computed(() => debouncedSearchText()
|
|
8922
9088
|
.normalize("NFD")
|
|
8923
9089
|
.replace(/[̀-ͯ]/g, ""), ...(ngDevMode ? [{ debugName: "normalizedSearchText" }] : []));
|
|
8924
9090
|
const suggests = signal([], ...(ngDevMode ? [{ debugName: "suggests" }] : []));
|
|
9091
|
+
/**
|
|
9092
|
+
* The selected values (list) or `"/A/B/*"` paths (tree) in controlled mode.
|
|
9093
|
+
* `undefined` in default mode — the selection is then derived from the shared
|
|
9094
|
+
* `QueryParamsStore` filters. In controlled mode without a `selection` input,
|
|
9095
|
+
* this is an empty array: the store filters belong to the shared query, not to
|
|
9096
|
+
* the host's, and must never leak into a controlled instance.
|
|
9097
|
+
*/
|
|
9098
|
+
const selectionValues = computed(() => {
|
|
9099
|
+
const sel = refs.selection?.();
|
|
9100
|
+
if (sel) {
|
|
9101
|
+
switch (sel.kind) {
|
|
9102
|
+
case "values":
|
|
9103
|
+
return sel.values;
|
|
9104
|
+
case "paths":
|
|
9105
|
+
return sel.paths;
|
|
9106
|
+
case "option":
|
|
9107
|
+
case "range":
|
|
9108
|
+
return [];
|
|
9109
|
+
}
|
|
9110
|
+
}
|
|
9111
|
+
return isControlled() ? [] : undefined;
|
|
9112
|
+
}, ...(ngDevMode ? [{ debugName: "selectionValues" }] : []));
|
|
9113
|
+
/** The controlled selection expressed in the shape `getFilter()` returns. */
|
|
9114
|
+
const selectionAsFilter = computed(() => {
|
|
9115
|
+
const sel = refs.selection?.();
|
|
9116
|
+
return sel?.kind === "values" && sel.values.length ? { values: sel.values } : null;
|
|
9117
|
+
}, ...(ngDevMode ? [{ debugName: "selectionAsFilter" }] : []));
|
|
8925
9118
|
const _filterCount = computed(() => {
|
|
9119
|
+
if (isControlled()) {
|
|
9120
|
+
const sel = refs.selection?.();
|
|
9121
|
+
if (!sel)
|
|
9122
|
+
return 0;
|
|
9123
|
+
switch (sel.kind) {
|
|
9124
|
+
case "values":
|
|
9125
|
+
return sel.values.length;
|
|
9126
|
+
case "paths":
|
|
9127
|
+
return sel.paths.length;
|
|
9128
|
+
case "option":
|
|
9129
|
+
return sel.option ? 1 : 0;
|
|
9130
|
+
case "range":
|
|
9131
|
+
return sel.from || sel.to ? 1 : 0;
|
|
9132
|
+
}
|
|
9133
|
+
}
|
|
8926
9134
|
getState(aggregationsStore);
|
|
8927
9135
|
const { count = 0 } = queryParamsStore.getFilter({ field: refs.column() ?? undefined, name: refs.name() ?? undefined }) || {};
|
|
8928
9136
|
return count;
|
|
@@ -8945,7 +9153,7 @@ function injectAggregationBase(refs) {
|
|
|
8945
9153
|
.pipe(takeUntilDestroyed(destroyRef), switchMap(({ text, column }) => {
|
|
8946
9154
|
if (text === "" || column === null)
|
|
8947
9155
|
return of([]);
|
|
8948
|
-
const q =
|
|
9156
|
+
const q = resolvedQuery();
|
|
8949
9157
|
return from(withFetch(() => fetchSuggestField(normalizedSearchText(), [column || ""], q), injector));
|
|
8950
9158
|
}))
|
|
8951
9159
|
.subscribe((result) => suggests.set(result || []));
|
|
@@ -8964,7 +9172,9 @@ function injectAggregationBase(refs) {
|
|
|
8964
9172
|
function addCurrentFiltersToItems(aggregation) {
|
|
8965
9173
|
const currentItems = aggregation?.items || [];
|
|
8966
9174
|
if (!aggregation?.isTree && !aggregation?.isDistribution) {
|
|
8967
|
-
const currentFilters =
|
|
9175
|
+
const currentFilters = isControlled()
|
|
9176
|
+
? selectionAsFilter()
|
|
9177
|
+
: queryParamsStore.getFilter({ field: aggregation?.column, name: aggregation?.name });
|
|
8968
9178
|
if (currentFilters) {
|
|
8969
9179
|
if (Array.isArray(currentFilters.filters) && currentFilters.filters.length) {
|
|
8970
9180
|
currentFilters.filters.forEach((filter) => {
|
|
@@ -9003,34 +9213,45 @@ function injectAggregationBase(refs) {
|
|
|
9003
9213
|
}
|
|
9004
9214
|
return currentItems;
|
|
9005
9215
|
}
|
|
9006
|
-
|
|
9216
|
+
/**
|
|
9217
|
+
* Aggregates the applied item filters into the single `LegacyFilter` that
|
|
9218
|
+
* `applyFilters` writes to the store. Also used by the controlled mode to
|
|
9219
|
+
* build the emitted structured filter, so both modes stay strictly
|
|
9220
|
+
* consistent. Returns `undefined` when nothing is applied (the clear case).
|
|
9221
|
+
*/
|
|
9222
|
+
function buildAppliedFilter(appliedFilters, agg) {
|
|
9007
9223
|
const { name: aggName, column: field } = agg;
|
|
9008
|
-
const syncUrl = refs.syncUrl?.() ?? true;
|
|
9009
9224
|
if (appliedFilters.length > 1) {
|
|
9010
9225
|
const display = appliedFilters[0].display;
|
|
9011
9226
|
if (agg.isDistribution) {
|
|
9012
|
-
|
|
9227
|
+
return {
|
|
9013
9228
|
operator: "or",
|
|
9014
9229
|
filters: appliedFilters,
|
|
9015
9230
|
name: aggName,
|
|
9016
9231
|
field,
|
|
9017
9232
|
display,
|
|
9018
|
-
}
|
|
9019
|
-
}
|
|
9020
|
-
else {
|
|
9021
|
-
const values = appliedFilters.map((f) => f.value);
|
|
9022
|
-
queryParamsStore.updateFilter({
|
|
9023
|
-
operator: "in",
|
|
9024
|
-
name: aggName,
|
|
9025
|
-
field,
|
|
9026
|
-
values,
|
|
9027
|
-
display,
|
|
9028
|
-
filters: appliedFilters,
|
|
9029
|
-
}, undefined, { syncUrl });
|
|
9233
|
+
};
|
|
9030
9234
|
}
|
|
9235
|
+
const values = appliedFilters.map((f) => f.value);
|
|
9236
|
+
return {
|
|
9237
|
+
operator: "in",
|
|
9238
|
+
name: aggName,
|
|
9239
|
+
field,
|
|
9240
|
+
values,
|
|
9241
|
+
display,
|
|
9242
|
+
filters: appliedFilters,
|
|
9243
|
+
};
|
|
9244
|
+
}
|
|
9245
|
+
if (appliedFilters.length === 1) {
|
|
9246
|
+
return appliedFilters[0];
|
|
9031
9247
|
}
|
|
9032
|
-
|
|
9033
|
-
|
|
9248
|
+
return undefined;
|
|
9249
|
+
}
|
|
9250
|
+
function applyFilters(appliedFilters, agg, clearFn) {
|
|
9251
|
+
const syncUrl = refs.syncUrl?.() ?? true;
|
|
9252
|
+
const combined = buildAppliedFilter(appliedFilters, agg);
|
|
9253
|
+
if (combined) {
|
|
9254
|
+
queryParamsStore.updateFilter(combined, undefined, { syncUrl });
|
|
9034
9255
|
}
|
|
9035
9256
|
else {
|
|
9036
9257
|
clearFn();
|
|
@@ -9043,6 +9264,14 @@ function injectAggregationBase(refs) {
|
|
|
9043
9264
|
aggregationsService,
|
|
9044
9265
|
injector,
|
|
9045
9266
|
destroyRef,
|
|
9267
|
+
isControlled,
|
|
9268
|
+
isQueryControlled,
|
|
9269
|
+
isDataControlled,
|
|
9270
|
+
isSelectionControlled,
|
|
9271
|
+
resolvedQuery,
|
|
9272
|
+
resolvedAggregationCount,
|
|
9273
|
+
controlledAggregation,
|
|
9274
|
+
selectionValues,
|
|
9046
9275
|
debouncedSearchText,
|
|
9047
9276
|
normalizedSearchText,
|
|
9048
9277
|
suggests,
|
|
@@ -9053,6 +9282,7 @@ function injectAggregationBase(refs) {
|
|
|
9053
9282
|
clearSearch,
|
|
9054
9283
|
selectItems,
|
|
9055
9284
|
addCurrentFiltersToItems,
|
|
9285
|
+
buildAppliedFilter,
|
|
9056
9286
|
applyFilters,
|
|
9057
9287
|
};
|
|
9058
9288
|
}
|
|
@@ -9100,12 +9330,56 @@ class AggregationDateComponent {
|
|
|
9100
9330
|
* @default true
|
|
9101
9331
|
*/
|
|
9102
9332
|
syncUrl = input(true, ...(ngDevMode ? [{ debugName: "syncUrl", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
9333
|
+
/**
|
|
9334
|
+
* Controlled query. When set, the component's own operations run against this
|
|
9335
|
+
* query instead of reading the shared `QueryParamsStore`. Use to target a query
|
|
9336
|
+
* not tied to the current app, without mutating the shared store or the URL.
|
|
9337
|
+
*
|
|
9338
|
+
* @default undefined — read `QueryParamsStore.getQuery()` (current behavior)
|
|
9339
|
+
*/
|
|
9340
|
+
controlledQuery = input(undefined, ...(ngDevMode ? [{ debugName: "controlledQuery", alias: "query" }] : [{ alias: "query" }]));
|
|
9341
|
+
/**
|
|
9342
|
+
* Controlled data. When set, the component renders this aggregation object
|
|
9343
|
+
* instead of reading it from the shared `AggregationsStore`.
|
|
9344
|
+
*
|
|
9345
|
+
* @default undefined — read `AggregationsStore` (current behavior)
|
|
9346
|
+
*/
|
|
9347
|
+
controlledAggregation = input(undefined, ...(ngDevMode ? [{ debugName: "controlledAggregation", alias: "aggregation" }] : [{ alias: "aggregation" }]));
|
|
9348
|
+
/**
|
|
9349
|
+
* Controlled selection. When set, the date form is derived from this value
|
|
9350
|
+
* instead of the shared `QueryParamsStore` filters:
|
|
9351
|
+
* `{ kind: "option", option }` re-selects the date option whose aggregation
|
|
9352
|
+
* item `value` matches (the real filter value, never the `display`);
|
|
9353
|
+
* `{ kind: "range", from, to }` fills the custom range from the bounds.
|
|
9354
|
+
* The two kinds are mutually exclusive — a date selection is either a preset
|
|
9355
|
+
* or a custom range.
|
|
9356
|
+
*
|
|
9357
|
+
* @default undefined — derive from `QueryParamsStore` filters (current behavior)
|
|
9358
|
+
*/
|
|
9359
|
+
controlledSelection = input(undefined, ...(ngDevMode ? [{ debugName: "controlledSelection", alias: "selection" }] : [{ alias: "selection" }]));
|
|
9103
9360
|
title = input({ label: "Date", icon: "far fa-calendar-day" }, ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
9104
9361
|
displayEmptyDistributionIntervals = input(false, ...(ngDevMode ? [{ debugName: "displayEmptyDistributionIntervals" }] : []));
|
|
9105
9362
|
/* outputs */
|
|
9106
9363
|
onSelect = output();
|
|
9107
9364
|
onApply = output();
|
|
9108
9365
|
onClear = output();
|
|
9366
|
+
/**
|
|
9367
|
+
* Emits the new selection (`{ kind: "option" }` preset or `{ kind: "range" }`
|
|
9368
|
+
* custom range) in controlled mode. The counterpart of the `[selection]` input,
|
|
9369
|
+
* emitted before `filtersChange`. Date is a radio list, so this fires **live at
|
|
9370
|
+
* selection time** — picking a preset radio, or confirming a date in the custom
|
|
9371
|
+
* range picker — not on an apply click (the custom-range radio alone, with no
|
|
9372
|
+
* date picked, emits nothing). Also emitted on clear. In controlled mode this is
|
|
9373
|
+
* the only way changes surface: nothing is written to the shared stores or the URL.
|
|
9374
|
+
*/
|
|
9375
|
+
selectionChange = output();
|
|
9376
|
+
/**
|
|
9377
|
+
* Emits the selection as a structured `Filter` (the exact filter the default
|
|
9378
|
+
* mode would write to the store), ready for `fetchQuery({ filters })`.
|
|
9379
|
+
* `undefined` on clear. Controlled mode only — emitted right after
|
|
9380
|
+
* `selectionChange` (see its live-emission timing).
|
|
9381
|
+
*/
|
|
9382
|
+
filtersChange = output();
|
|
9109
9383
|
/* collapse state */
|
|
9110
9384
|
isCollapsed = linkedSignal(() => this.collapsed(), ...(ngDevMode ? [{ debugName: "isCollapsed" }] : []));
|
|
9111
9385
|
expanded = computed(() => (this.isCollapsed() ? null : ""), ...(ngDevMode ? [{ debugName: "expanded" }] : []));
|
|
@@ -9120,8 +9394,12 @@ class AggregationDateComponent {
|
|
|
9120
9394
|
searchInput: this.searchInput,
|
|
9121
9395
|
expanded: this.expanded,
|
|
9122
9396
|
syncUrl: this.syncUrl,
|
|
9397
|
+
query: this.controlledQuery,
|
|
9398
|
+
aggregation: this.controlledAggregation,
|
|
9399
|
+
selection: this.controlledSelection,
|
|
9123
9400
|
});
|
|
9124
9401
|
/* spread from base */
|
|
9402
|
+
isControlled = this.base.isControlled;
|
|
9125
9403
|
aggregationsService = this.base.aggregationsService;
|
|
9126
9404
|
queryParamsStore = this.base.queryParamsStore;
|
|
9127
9405
|
hasFilters = this.base.hasFilters;
|
|
@@ -9132,7 +9410,24 @@ class AggregationDateComponent {
|
|
|
9132
9410
|
/* state */
|
|
9133
9411
|
selection = signal(false, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
9134
9412
|
validSelection = signal(false, ...(ngDevMode ? [{ debugName: "validSelection" }] : []));
|
|
9413
|
+
/**
|
|
9414
|
+
* Guards live emission while the form is seeded programmatically from the
|
|
9415
|
+
* `[selection]` input (see the seeding effect below). `form.setValue`/`patchValue`
|
|
9416
|
+
* emit `valueChanges` synchronously; without this flag a controlled host that
|
|
9417
|
+
* re-feeds `[selection]` from `selectionChange` would loop indefinitely.
|
|
9418
|
+
*/
|
|
9419
|
+
seedingForm = false;
|
|
9135
9420
|
aggregation = computed(() => {
|
|
9421
|
+
if (this.isControlled()) {
|
|
9422
|
+
const controlled = this.base.controlledAggregation();
|
|
9423
|
+
if (!controlled)
|
|
9424
|
+
return null;
|
|
9425
|
+
const agg = this.aggregationsService.enrichAggregation(controlled, []);
|
|
9426
|
+
return {
|
|
9427
|
+
...agg,
|
|
9428
|
+
items: agg.items?.filter((item) => item.display !== "custom-range") ?? []
|
|
9429
|
+
};
|
|
9430
|
+
}
|
|
9136
9431
|
const name = this.name();
|
|
9137
9432
|
if (name !== null) {
|
|
9138
9433
|
const agg = this.aggregationsService.processAggregation(name, this.column());
|
|
@@ -9168,28 +9463,75 @@ class AggregationDateComponent {
|
|
|
9168
9463
|
this.transloco.langChanges$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((lang) => {
|
|
9169
9464
|
this.lang.set(lang);
|
|
9170
9465
|
});
|
|
9171
|
-
// apply current date filter from queryParamsStore
|
|
9466
|
+
// apply current date filter from the selection input (controlled) or the queryParamsStore
|
|
9172
9467
|
effect(() => {
|
|
9173
9468
|
const agg = this.aggregation();
|
|
9174
|
-
if (agg)
|
|
9469
|
+
if (!agg)
|
|
9470
|
+
return;
|
|
9471
|
+
// seeding the form from an input must not re-emit through valueChanges
|
|
9472
|
+
this.seedingForm = true;
|
|
9473
|
+
try {
|
|
9474
|
+
if (this.isControlled()) {
|
|
9475
|
+
this.updateFormFromSelection(this.controlledSelection());
|
|
9476
|
+
return;
|
|
9477
|
+
}
|
|
9175
9478
|
this.updateForm(this.queryParamsStore.getFilter({
|
|
9176
9479
|
field: agg.column,
|
|
9177
9480
|
name: agg.name
|
|
9178
9481
|
}));
|
|
9179
9482
|
}
|
|
9483
|
+
finally {
|
|
9484
|
+
this.seedingForm = false;
|
|
9485
|
+
}
|
|
9180
9486
|
});
|
|
9181
9487
|
this.form.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((changes) => {
|
|
9182
9488
|
this.validSelection.set(!!changes.option &&
|
|
9183
9489
|
(changes.option !== "custom-range" || changes.customRange?.from !== null || changes.customRange?.to !== null));
|
|
9490
|
+
// controlled date is a radio list: emit the selection live, without waiting
|
|
9491
|
+
// for an apply click. Presets are complete as soon as the radio is picked;
|
|
9492
|
+
// the custom range is emitted only once a real date is chosen (see
|
|
9493
|
+
// `onRangeSelected`), never on toggling the "custom-range" radio, so a stale
|
|
9494
|
+
// range left in the form is not surfaced.
|
|
9495
|
+
if (!this.seedingForm &&
|
|
9496
|
+
this.isControlled() &&
|
|
9497
|
+
changes.option &&
|
|
9498
|
+
changes.option !== "custom-range") {
|
|
9499
|
+
try {
|
|
9500
|
+
this.emitCurrentSelection();
|
|
9501
|
+
}
|
|
9502
|
+
catch (err) {
|
|
9503
|
+
warn("Error emitting date selection:", err);
|
|
9504
|
+
}
|
|
9505
|
+
}
|
|
9184
9506
|
});
|
|
9185
9507
|
}
|
|
9508
|
+
/**
|
|
9509
|
+
* Emits the current form selection through `selectionChange` then `filtersChange`
|
|
9510
|
+
* (controlled mode). Shared by `apply()` and the live emission paths so both
|
|
9511
|
+
* produce identical payloads. Throws if the form is not a valid selection
|
|
9512
|
+
* (`getFormValueFilter`) — callers decide how to surface it.
|
|
9513
|
+
*/
|
|
9514
|
+
emitCurrentSelection() {
|
|
9515
|
+
const filter = this.getFormValueFilter();
|
|
9516
|
+
const option = this.form.value.option;
|
|
9517
|
+
// a date selection is either a preset (the item's value — the real filter
|
|
9518
|
+
// value, robust across days) or a custom range (its bounds) — never both
|
|
9519
|
+
this.selectionChange.emit(option && option !== "custom-range"
|
|
9520
|
+
? { kind: "option", option: this.optionToItemValue(option) ?? option }
|
|
9521
|
+
: this.toRangeSelection(filter));
|
|
9522
|
+
this.filtersChange.emit(legacyFilterToFilter(filter));
|
|
9523
|
+
}
|
|
9186
9524
|
select() {
|
|
9187
9525
|
this.selection.set(true);
|
|
9188
9526
|
}
|
|
9189
9527
|
apply() {
|
|
9190
9528
|
try {
|
|
9191
|
-
|
|
9192
|
-
|
|
9529
|
+
if (this.isControlled()) {
|
|
9530
|
+
this.emitCurrentSelection();
|
|
9531
|
+
}
|
|
9532
|
+
else {
|
|
9533
|
+
this.queryParamsStore.updateFilter(this.getFormValueFilter(), undefined, { syncUrl: this.syncUrl() });
|
|
9534
|
+
}
|
|
9193
9535
|
this.selection.set(false);
|
|
9194
9536
|
this.onApply.emit();
|
|
9195
9537
|
}
|
|
@@ -9210,7 +9552,13 @@ class AggregationDateComponent {
|
|
|
9210
9552
|
});
|
|
9211
9553
|
const agg = this.aggregation();
|
|
9212
9554
|
if (agg && notify) {
|
|
9213
|
-
|
|
9555
|
+
if (this.isControlled()) {
|
|
9556
|
+
this.selectionChange.emit({ kind: "range" });
|
|
9557
|
+
this.filtersChange.emit(undefined);
|
|
9558
|
+
}
|
|
9559
|
+
else {
|
|
9560
|
+
this.queryParamsStore.removeFilterByName(agg.name, undefined, { syncUrl: this.syncUrl() });
|
|
9561
|
+
}
|
|
9214
9562
|
this.onClear.emit();
|
|
9215
9563
|
}
|
|
9216
9564
|
}
|
|
@@ -9228,12 +9576,22 @@ class AggregationDateComponent {
|
|
|
9228
9576
|
this.dateRangeDialog()?.open();
|
|
9229
9577
|
}
|
|
9230
9578
|
onRangeSelected(range) {
|
|
9231
|
-
console.log("Selected range:", range);
|
|
9232
9579
|
this.form.patchValue({
|
|
9233
9580
|
option: "custom-range",
|
|
9234
9581
|
customRange: { from: range.from, to: range.to }
|
|
9235
9582
|
});
|
|
9236
9583
|
this.select();
|
|
9584
|
+
// a real date was picked in the picker: emit the custom range live in
|
|
9585
|
+
// controlled mode (the only place a custom range is emitted — never on
|
|
9586
|
+
// toggling the "custom-range" radio)
|
|
9587
|
+
if (this.isControlled()) {
|
|
9588
|
+
try {
|
|
9589
|
+
this.emitCurrentSelection();
|
|
9590
|
+
}
|
|
9591
|
+
catch (err) {
|
|
9592
|
+
warn("Error emitting date selection:", err);
|
|
9593
|
+
}
|
|
9594
|
+
}
|
|
9237
9595
|
}
|
|
9238
9596
|
updateForm(filter) {
|
|
9239
9597
|
if (!filter) {
|
|
@@ -9273,6 +9631,90 @@ class AggregationDateComponent {
|
|
|
9273
9631
|
};
|
|
9274
9632
|
this.form.setValue(formValue);
|
|
9275
9633
|
}
|
|
9634
|
+
/**
|
|
9635
|
+
* Derives the form from a controlled selection.
|
|
9636
|
+
* `{ kind: "option" }` re-selects the date option whose item `value` matches —
|
|
9637
|
+
* no operator/value matching, so presets whose bounds are computed relative to
|
|
9638
|
+
* today survive a round-trip across days; a value unknown to the aggregation
|
|
9639
|
+
* resets the form. `{ kind: "range" }` fills the custom range from the bounds.
|
|
9640
|
+
*/
|
|
9641
|
+
updateFormFromSelection(sel) {
|
|
9642
|
+
if (sel?.kind === "option") {
|
|
9643
|
+
const code = this.itemValueToOption(sel.option);
|
|
9644
|
+
if (code && code !== "custom-range") {
|
|
9645
|
+
this.form.setValue({ option: code, customRange: { from: null, to: null } });
|
|
9646
|
+
}
|
|
9647
|
+
else {
|
|
9648
|
+
this.clear(false);
|
|
9649
|
+
}
|
|
9650
|
+
return;
|
|
9651
|
+
}
|
|
9652
|
+
this.updateForm(this.rangeToLegacyFilter(sel));
|
|
9653
|
+
}
|
|
9654
|
+
/**
|
|
9655
|
+
* The date option codes (what `form.option` holds) are the items' `display`
|
|
9656
|
+
* with a fallback on their `value` (see `translateAggregationToDateOptions`).
|
|
9657
|
+
* The controlled selection persists the item `value` — the real filter value —
|
|
9658
|
+
* so these two helpers convert between the option code and the item value.
|
|
9659
|
+
*/
|
|
9660
|
+
optionToItemValue(option) {
|
|
9661
|
+
const item = this.items().find((it) => (it.display || String(it.value)) === option);
|
|
9662
|
+
return item ? String(item.value) : undefined;
|
|
9663
|
+
}
|
|
9664
|
+
itemValueToOption(value) {
|
|
9665
|
+
const item = this.items().find((it) => String(it.value) === value);
|
|
9666
|
+
return item ? item.display || String(item.value) : undefined;
|
|
9667
|
+
}
|
|
9668
|
+
/**
|
|
9669
|
+
* Converts a controlled `{ kind: "range" }` selection to the `LegacyFilter`
|
|
9670
|
+
* shape `updateForm` expects (which only reads `operator`/`value`/`filters`).
|
|
9671
|
+
* Returns `undefined` for an empty or non-range selection, which resets the form.
|
|
9672
|
+
*/
|
|
9673
|
+
rangeToLegacyFilter(sel) {
|
|
9674
|
+
if (!sel || sel.kind !== "range")
|
|
9675
|
+
return undefined;
|
|
9676
|
+
const { from, to } = sel;
|
|
9677
|
+
if (from && to) {
|
|
9678
|
+
return {
|
|
9679
|
+
operator: "and",
|
|
9680
|
+
filters: [
|
|
9681
|
+
{ operator: "gte", value: from },
|
|
9682
|
+
{ operator: "lte", value: to }
|
|
9683
|
+
]
|
|
9684
|
+
};
|
|
9685
|
+
}
|
|
9686
|
+
if (from)
|
|
9687
|
+
return { operator: "gte", value: from };
|
|
9688
|
+
if (to)
|
|
9689
|
+
return { operator: "lte", value: to };
|
|
9690
|
+
return undefined;
|
|
9691
|
+
}
|
|
9692
|
+
/**
|
|
9693
|
+
* Converts the applied `LegacyFilter` to a `{ kind: "range" }` selection
|
|
9694
|
+
* (the shape emitted through `selectionChange` in controlled mode).
|
|
9695
|
+
* Mirrors the operator handling of `updateForm`. Presets whose operator is not
|
|
9696
|
+
* a range operator (e.g. `eq`) collapse to `from = to = value`.
|
|
9697
|
+
*/
|
|
9698
|
+
toRangeSelection(filter) {
|
|
9699
|
+
switch (filter.operator) {
|
|
9700
|
+
case "gte":
|
|
9701
|
+
return { kind: "range", from: filter.value };
|
|
9702
|
+
case "lte":
|
|
9703
|
+
return { kind: "range", to: filter.value };
|
|
9704
|
+
case "between":
|
|
9705
|
+
return { kind: "range", from: filter.start, to: filter.end };
|
|
9706
|
+
case "and": {
|
|
9707
|
+
const filters = filter.filters ?? [];
|
|
9708
|
+
return {
|
|
9709
|
+
kind: "range",
|
|
9710
|
+
from: filters.find((f) => f.operator === "gte")?.value,
|
|
9711
|
+
to: filters.find((f) => f.operator === "lte")?.value
|
|
9712
|
+
};
|
|
9713
|
+
}
|
|
9714
|
+
default:
|
|
9715
|
+
return { kind: "range", from: filter.value, to: filter.value };
|
|
9716
|
+
}
|
|
9717
|
+
}
|
|
9276
9718
|
toEndOfDay(dateStr) {
|
|
9277
9719
|
return `${new Date(dateStr).toLocaleDateString("en-CA", options)} 23:59:59`;
|
|
9278
9720
|
}
|
|
@@ -9348,7 +9790,7 @@ class AggregationDateComponent {
|
|
|
9348
9790
|
throw new Error("filters.filterInvalid");
|
|
9349
9791
|
}
|
|
9350
9792
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationDateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9351
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AggregationDateComponent, isStandalone: true, selector: "aggregation-date, AggregationDate, aggregationdate", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, showFiltersCount: { classPropertyName: "showFiltersCount", publicName: "showFiltersCount", isSignal: true, isRequired: false, transformFunction: null }, syncUrl: { classPropertyName: "syncUrl", publicName: "syncUrl", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, displayEmptyDistributionIntervals: { classPropertyName: "displayEmptyDistributionIntervals", publicName: "displayEmptyDistributionIntervals", isSignal: true, isRequired: false, transformFunction: null }, searchText: { classPropertyName: "searchText", publicName: "searchText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", onApply: "onApply", onClear: "onClear", searchText: "searchTextChange" }, host: { classAttribute: "@container" }, providers: [provideTranslocoScope("filters")], viewQueries: [{ propertyName: "dateRangeDialog", first: true, predicate: AggregationDateRangeDialogComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible()\"\r\n class=\"m-0 flex h-8 w-full items-center pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @let icon = aggregation()?.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\r\n }\r\n <span class=\"grow\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (hasFilters()) {\r\n <icon-button\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"clear()\"\r\n (keydown.enter)=\"clear()\">\r\n <filter-x-icon />\r\n <span class=\"sr-only\">{{ \"filters.clearFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n\r\n @if (selection() && validSelection()) {\r\n <icon-button\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"apply()\"\r\n (keydown.enter)=\"apply()\">\r\n <filter-icon />\r\n <span class=\"sr-only\">{{ \"filters.applyFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n @if (collapsible()) {\r\n <icon-button\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n </icon-button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n <form [formGroup]=\"form\">\r\n <ul\r\n class=\"scrollbar-thin flex max-h-[var(--scroll-height,20rem)] snap-y snap-start flex-col gap-1 overflow-auto pt-2\"\r\n role=\"list\">\r\n @for (option of dateOptions(); track $index) {\r\n <li\r\n role=\"listitem\"\r\n tabindex=\"0\"\r\n (click)=\"radio.click()\"\r\n [attr.aria-label]=\"option.display | syslang | transloco\"\r\n [class]=\"\r\n cn(\r\n 'flex p-0 px-2 leading-7',\r\n form.get('option')?.value === option.display && 'bg-accent',\r\n option.hidden && 'hidden',\r\n option.disabled && 'disabled pointer-events-none text-neutral-300'\r\n )\r\n \"\r\n [attr.aria-hidden]=\"option.disabled\">\r\n <input\r\n #radio\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-{{ option.display }}\"\r\n [attr.disabled]=\"option.disabled ? true : null\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n (click)=\"select()\"\r\n value=\"{{ option.display }}\" />\r\n\r\n <label\r\n for=\"date-filter-{{ option.display }}\"\r\n class=\"grow cursor-pointer p-1\">\r\n {{ option.display | syslang | transloco }}\r\n </label>\r\n </li>\r\n }\r\n\r\n @if (allowCustomRange) {\r\n <li\r\n role=\"listitem\"\r\n aria-label=\"open date range picker\"\r\n class=\"flex px-2 leading-7\"\r\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\r\n <input\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-range-dialog\"\r\n value=\"custom-range\"\r\n (click)=\"select()\" />\r\n <div\r\n class=\"@container flex grow justify-end gap-1 p-1 @max-[340px]:flex-wrap\">\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\r\n \"filters.from\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-start\"\r\n name=\"start\"\r\n type=\"text\"\r\n readonly\r\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\r\n [value]=\"customRangeFrom()\"\r\n (click)=\"selectAndOpenDialog()\" />\r\n </div>\r\n <div class=\"flex gap-1\">\r\n <label\r\n for=\"datepicker-range-end\"\r\n class=\"min-w-10 truncate text-right\"\r\n >{{ \"filters.to\" | transloco }}</label\r\n >\r\n <input\r\n id=\"datepicker-range-end\"\r\n name=\"end\"\r\n type=\"text\"\r\n readonly\r\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\r\n [value]=\"customRangeTo()\"\r\n (click)=\"selectAndOpenDialog()\" />\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n </ul>\r\n </form>\r\n</details>\r\n\r\n<aggregation-date-range-dialog\r\n [lang]=\"lang()\"\r\n [useDateRange]=\"false\"\r\n [min]=\"form.get('customRange.from')?.value || undefined\"\r\n [max]=\"form.get('customRange.to')?.value || undefined\"\r\n (rangeSelected)=\"onRangeSelected($event)\" />\r\n", styles: [":host{display:block;min-width:200px}ul[role=list]{scrollbar-width:thin}\n"], dependencies: [{ kind: "directive", type: InputComponent, selector: "input[type=\"text\"], input[type=\"email\"], input[type=\"number\"], input[type=\"password\"], input[type=\"tel\"], input[type=\"url\"], input[type=\"time\"], input[type=\"file\"]", inputs: ["class", "variant", "decoration"] }, { kind: "directive", type: ListItemComponent, selector: "[role=\"listitem\"], [role=\"option\"]", inputs: ["class", "variant", "decoration"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon, chevron-right-icon, chevronrighticon", inputs: ["class"] }, { kind: "component", type: AggregationDateRangeDialogComponent, selector: "aggregation-date-range-dialog", inputs: ["min", "max", "lang", "useDateRange"], outputs: ["rangeSelected"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass", "class"] }, { kind: "component", type: FilterIcon, selector: "filter-icon, FilterIcon", inputs: ["class"] }, { kind: "component", type: FilterXIcon, selector: "filter-x-icon, FilterXIcon", inputs: ["class"] }, { kind: "directive", type: IconButtonComponent, selector: "button[icon-button], icon-button, IconButton", inputs: ["class", "size"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
|
|
9793
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AggregationDateComponent, isStandalone: true, selector: "aggregation-date, AggregationDate, aggregationdate", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, showFiltersCount: { classPropertyName: "showFiltersCount", publicName: "showFiltersCount", isSignal: true, isRequired: false, transformFunction: null }, syncUrl: { classPropertyName: "syncUrl", publicName: "syncUrl", isSignal: true, isRequired: false, transformFunction: null }, controlledQuery: { classPropertyName: "controlledQuery", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, controlledAggregation: { classPropertyName: "controlledAggregation", publicName: "aggregation", isSignal: true, isRequired: false, transformFunction: null }, controlledSelection: { classPropertyName: "controlledSelection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, displayEmptyDistributionIntervals: { classPropertyName: "displayEmptyDistributionIntervals", publicName: "displayEmptyDistributionIntervals", isSignal: true, isRequired: false, transformFunction: null }, searchText: { classPropertyName: "searchText", publicName: "searchText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", onApply: "onApply", onClear: "onClear", selectionChange: "selectionChange", filtersChange: "filtersChange", searchText: "searchTextChange" }, host: { classAttribute: "@container" }, providers: [provideTranslocoScope("filters")], viewQueries: [{ propertyName: "dateRangeDialog", first: true, predicate: AggregationDateRangeDialogComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible()\"\r\n class=\"m-0 flex h-8 w-full items-center pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @let icon = aggregation()?.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\r\n }\r\n <span class=\"grow\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (hasFilters()) {\r\n <icon-button\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"clear()\"\r\n (keydown.enter)=\"clear()\">\r\n <filter-x-icon />\r\n <span class=\"sr-only\">{{ \"filters.clearFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n\r\n @if (!isControlled() && selection() && validSelection()) {\r\n <icon-button\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"apply()\"\r\n (keydown.enter)=\"apply()\">\r\n <filter-icon />\r\n <span class=\"sr-only\">{{ \"filters.applyFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n @if (collapsible()) {\r\n <icon-button\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n </icon-button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n <form [formGroup]=\"form\">\r\n <ul\r\n class=\"flex max-h-[var(--scroll-height,20rem)] snap-y snap-start scrollbar-thin flex-col gap-1 overflow-auto pt-2\"\r\n role=\"list\">\r\n @for (option of dateOptions(); track $index) {\r\n <li\r\n role=\"listitem\"\r\n tabindex=\"0\"\r\n (click)=\"radio.click()\"\r\n [attr.aria-label]=\"option.display | syslang | transloco\"\r\n [class]=\"\r\n cn(\r\n 'flex p-0 px-2 leading-7',\r\n form.get('option')?.value === option.display && 'bg-accent',\r\n option.hidden && 'hidden',\r\n option.disabled && 'disabled pointer-events-none text-neutral-300'\r\n )\r\n \"\r\n [attr.aria-hidden]=\"option.disabled\">\r\n <input\r\n #radio\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-{{ option.display }}\"\r\n [attr.disabled]=\"option.disabled ? true : null\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n (click)=\"select()\"\r\n value=\"{{ option.display }}\" />\r\n\r\n <label\r\n for=\"date-filter-{{ option.display }}\"\r\n class=\"grow cursor-pointer p-1\">\r\n {{ option.display | syslang | transloco }}\r\n </label>\r\n </li>\r\n }\r\n\r\n @if (allowCustomRange) {\r\n <li\r\n role=\"listitem\"\r\n aria-label=\"open date range picker\"\r\n class=\"flex px-2 leading-7\"\r\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\r\n <input\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-range-dialog\"\r\n value=\"custom-range\"\r\n (click)=\"select()\" />\r\n <div\r\n class=\"@container flex grow justify-end gap-1 p-1 @max-[340px]:flex-wrap\">\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\r\n \"filters.from\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-start\"\r\n name=\"start\"\r\n type=\"text\"\r\n readonly\r\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\r\n [value]=\"customRangeFrom()\"\r\n (click)=\"selectAndOpenDialog()\" />\r\n </div>\r\n <div class=\"flex gap-1\">\r\n <label\r\n for=\"datepicker-range-end\"\r\n class=\"min-w-10 truncate text-right\"\r\n >{{ \"filters.to\" | transloco }}</label\r\n >\r\n <input\r\n id=\"datepicker-range-end\"\r\n name=\"end\"\r\n type=\"text\"\r\n readonly\r\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\r\n [value]=\"customRangeTo()\"\r\n (click)=\"selectAndOpenDialog()\" />\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n </ul>\r\n </form>\r\n</details>\r\n\r\n<aggregation-date-range-dialog\r\n [lang]=\"lang()\"\r\n [useDateRange]=\"false\"\r\n [min]=\"form.get('customRange.from')?.value || undefined\"\r\n [max]=\"form.get('customRange.to')?.value || undefined\"\r\n (rangeSelected)=\"onRangeSelected($event)\" />\r\n", styles: [":host{display:block;min-width:200px}ul[role=list]{scrollbar-width:thin}\n"], dependencies: [{ kind: "directive", type: InputComponent, selector: "input[type=\"text\"], input[type=\"email\"], input[type=\"number\"], input[type=\"password\"], input[type=\"tel\"], input[type=\"url\"], input[type=\"time\"], input[type=\"file\"]", inputs: ["class", "variant", "decoration"] }, { kind: "directive", type: ListItemComponent, selector: "[role=\"listitem\"], [role=\"option\"]", inputs: ["class", "variant", "decoration"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon, chevron-right-icon, chevronrighticon", inputs: ["class"] }, { kind: "component", type: AggregationDateRangeDialogComponent, selector: "aggregation-date-range-dialog", inputs: ["min", "max", "lang", "useDateRange"], outputs: ["rangeSelected"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass", "class"] }, { kind: "component", type: FilterIcon, selector: "filter-icon, FilterIcon", inputs: ["class"] }, { kind: "component", type: FilterXIcon, selector: "filter-x-icon, FilterXIcon", inputs: ["class"] }, { kind: "directive", type: IconButtonComponent, selector: "button[icon-button], icon-button, IconButton", inputs: ["class", "size"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
|
|
9352
9794
|
}
|
|
9353
9795
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationDateComponent, decorators: [{
|
|
9354
9796
|
type: Component,
|
|
@@ -9366,8 +9808,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
9366
9808
|
IconButtonComponent
|
|
9367
9809
|
], host: {
|
|
9368
9810
|
class: "@container"
|
|
9369
|
-
}, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible()\"\r\n class=\"m-0 flex h-8 w-full items-center pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @let icon = aggregation()?.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\r\n }\r\n <span class=\"grow\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (hasFilters()) {\r\n <icon-button\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"clear()\"\r\n (keydown.enter)=\"clear()\">\r\n <filter-x-icon />\r\n <span class=\"sr-only\">{{ \"filters.clearFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n\r\n @if (selection() && validSelection()) {\r\n <icon-button\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"apply()\"\r\n (keydown.enter)=\"apply()\">\r\n <filter-icon />\r\n <span class=\"sr-only\">{{ \"filters.applyFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n @if (collapsible()) {\r\n <icon-button\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n </icon-button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n <form [formGroup]=\"form\">\r\n <ul\r\n class=\"
|
|
9370
|
-
}], ctorParameters: () => [], propDecorators: { dateRangeDialog: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AggregationDateRangeDialogComponent), { isSignal: true }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], showFiltersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFiltersCount", required: false }] }], syncUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncUrl", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], displayEmptyDistributionIntervals: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayEmptyDistributionIntervals", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], onApply: [{ type: i0.Output, args: ["onApply"] }], onClear: [{ type: i0.Output, args: ["onClear"] }], searchText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchText", required: false }] }, { type: i0.Output, args: ["searchTextChange"] }] } });
|
|
9811
|
+
}, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible()\"\r\n class=\"m-0 flex h-8 w-full items-center pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @let icon = aggregation()?.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\r\n }\r\n <span class=\"grow\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (hasFilters()) {\r\n <icon-button\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"clear()\"\r\n (keydown.enter)=\"clear()\">\r\n <filter-x-icon />\r\n <span class=\"sr-only\">{{ \"filters.clearFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n\r\n @if (!isControlled() && selection() && validSelection()) {\r\n <icon-button\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"apply()\"\r\n (keydown.enter)=\"apply()\">\r\n <filter-icon />\r\n <span class=\"sr-only\">{{ \"filters.applyFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n @if (collapsible()) {\r\n <icon-button\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n </icon-button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n <form [formGroup]=\"form\">\r\n <ul\r\n class=\"flex max-h-[var(--scroll-height,20rem)] snap-y snap-start scrollbar-thin flex-col gap-1 overflow-auto pt-2\"\r\n role=\"list\">\r\n @for (option of dateOptions(); track $index) {\r\n <li\r\n role=\"listitem\"\r\n tabindex=\"0\"\r\n (click)=\"radio.click()\"\r\n [attr.aria-label]=\"option.display | syslang | transloco\"\r\n [class]=\"\r\n cn(\r\n 'flex p-0 px-2 leading-7',\r\n form.get('option')?.value === option.display && 'bg-accent',\r\n option.hidden && 'hidden',\r\n option.disabled && 'disabled pointer-events-none text-neutral-300'\r\n )\r\n \"\r\n [attr.aria-hidden]=\"option.disabled\">\r\n <input\r\n #radio\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-{{ option.display }}\"\r\n [attr.disabled]=\"option.disabled ? true : null\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n (click)=\"select()\"\r\n value=\"{{ option.display }}\" />\r\n\r\n <label\r\n for=\"date-filter-{{ option.display }}\"\r\n class=\"grow cursor-pointer p-1\">\r\n {{ option.display | syslang | transloco }}\r\n </label>\r\n </li>\r\n }\r\n\r\n @if (allowCustomRange) {\r\n <li\r\n role=\"listitem\"\r\n aria-label=\"open date range picker\"\r\n class=\"flex px-2 leading-7\"\r\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\r\n <input\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-range-dialog\"\r\n value=\"custom-range\"\r\n (click)=\"select()\" />\r\n <div\r\n class=\"@container flex grow justify-end gap-1 p-1 @max-[340px]:flex-wrap\">\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\r\n \"filters.from\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-start\"\r\n name=\"start\"\r\n type=\"text\"\r\n readonly\r\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\r\n [value]=\"customRangeFrom()\"\r\n (click)=\"selectAndOpenDialog()\" />\r\n </div>\r\n <div class=\"flex gap-1\">\r\n <label\r\n for=\"datepicker-range-end\"\r\n class=\"min-w-10 truncate text-right\"\r\n >{{ \"filters.to\" | transloco }}</label\r\n >\r\n <input\r\n id=\"datepicker-range-end\"\r\n name=\"end\"\r\n type=\"text\"\r\n readonly\r\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\r\n [value]=\"customRangeTo()\"\r\n (click)=\"selectAndOpenDialog()\" />\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n </ul>\r\n </form>\r\n</details>\r\n\r\n<aggregation-date-range-dialog\r\n [lang]=\"lang()\"\r\n [useDateRange]=\"false\"\r\n [min]=\"form.get('customRange.from')?.value || undefined\"\r\n [max]=\"form.get('customRange.to')?.value || undefined\"\r\n (rangeSelected)=\"onRangeSelected($event)\" />\r\n", styles: [":host{display:block;min-width:200px}ul[role=list]{scrollbar-width:thin}\n"] }]
|
|
9812
|
+
}], ctorParameters: () => [], propDecorators: { dateRangeDialog: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AggregationDateRangeDialogComponent), { isSignal: true }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], showFiltersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFiltersCount", required: false }] }], syncUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncUrl", required: false }] }], controlledQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], controlledAggregation: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregation", required: false }] }], controlledSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], displayEmptyDistributionIntervals: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayEmptyDistributionIntervals", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], onApply: [{ type: i0.Output, args: ["onApply"] }], onClear: [{ type: i0.Output, args: ["onClear"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], filtersChange: [{ type: i0.Output, args: ["filtersChange"] }], searchText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchText", required: false }] }, { type: i0.Output, args: ["searchTextChange"] }] } });
|
|
9371
9813
|
|
|
9372
9814
|
/**
|
|
9373
9815
|
* Component that allows users to select a date or a date range for filtering search results.
|
|
@@ -9376,7 +9818,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
9376
9818
|
*/
|
|
9377
9819
|
class DateComponent extends AggregationDateComponent {
|
|
9378
9820
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: DateComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
9379
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: DateComponent, isStandalone: true, selector: "date-filter,DateFilter", host: { classAttribute: "@container" }, providers: [provideTranslocoScope("filters")], usesInheritance: true, ngImport: i0, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible()\"\r\n class=\"m-0 flex h-8 w-full items-center pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @let icon = aggregation()?.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\r\n }\r\n <span class=\"grow\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (hasFilters()) {\r\n <icon-button\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"clear()\"\r\n (keydown.enter)=\"clear()\">\r\n <filter-x-icon />\r\n <span class=\"sr-only\">{{ \"filters.clearFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n\r\n @if (selection() && validSelection()) {\r\n <icon-button\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"apply()\"\r\n (keydown.enter)=\"apply()\">\r\n <filter-icon />\r\n <span class=\"sr-only\">{{ \"filters.applyFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n @if (collapsible()) {\r\n <icon-button\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n </icon-button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n <form [formGroup]=\"form\">\r\n <ul\r\n class=\"
|
|
9821
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: DateComponent, isStandalone: true, selector: "date-filter,DateFilter", host: { classAttribute: "@container" }, providers: [provideTranslocoScope("filters")], usesInheritance: true, ngImport: i0, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible()\"\r\n class=\"m-0 flex h-8 w-full items-center pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @let icon = aggregation()?.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\r\n }\r\n <span class=\"grow\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (hasFilters()) {\r\n <icon-button\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"clear()\"\r\n (keydown.enter)=\"clear()\">\r\n <filter-x-icon />\r\n <span class=\"sr-only\">{{ \"filters.clearFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n\r\n @if (!isControlled() && selection() && validSelection()) {\r\n <icon-button\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"apply()\"\r\n (keydown.enter)=\"apply()\">\r\n <filter-icon />\r\n <span class=\"sr-only\">{{ \"filters.applyFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n @if (collapsible()) {\r\n <icon-button\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n </icon-button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n <form [formGroup]=\"form\">\r\n <ul\r\n class=\"flex max-h-[var(--scroll-height,20rem)] snap-y snap-start scrollbar-thin flex-col gap-1 overflow-auto pt-2\"\r\n role=\"list\">\r\n @for (option of dateOptions(); track $index) {\r\n <li\r\n role=\"listitem\"\r\n tabindex=\"0\"\r\n (click)=\"radio.click()\"\r\n [attr.aria-label]=\"option.display | syslang | transloco\"\r\n [class]=\"\r\n cn(\r\n 'flex p-0 px-2 leading-7',\r\n form.get('option')?.value === option.display && 'bg-accent',\r\n option.hidden && 'hidden',\r\n option.disabled && 'disabled pointer-events-none text-neutral-300'\r\n )\r\n \"\r\n [attr.aria-hidden]=\"option.disabled\">\r\n <input\r\n #radio\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-{{ option.display }}\"\r\n [attr.disabled]=\"option.disabled ? true : null\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n (click)=\"select()\"\r\n value=\"{{ option.display }}\" />\r\n\r\n <label\r\n for=\"date-filter-{{ option.display }}\"\r\n class=\"grow cursor-pointer p-1\">\r\n {{ option.display | syslang | transloco }}\r\n </label>\r\n </li>\r\n }\r\n\r\n @if (allowCustomRange) {\r\n <li\r\n role=\"listitem\"\r\n aria-label=\"open date range picker\"\r\n class=\"flex px-2 leading-7\"\r\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\r\n <input\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-range-dialog\"\r\n value=\"custom-range\"\r\n (click)=\"select()\" />\r\n <div\r\n class=\"@container flex grow justify-end gap-1 p-1 @max-[340px]:flex-wrap\">\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\r\n \"filters.from\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-start\"\r\n name=\"start\"\r\n type=\"text\"\r\n readonly\r\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\r\n [value]=\"customRangeFrom()\"\r\n (click)=\"selectAndOpenDialog()\" />\r\n </div>\r\n <div class=\"flex gap-1\">\r\n <label\r\n for=\"datepicker-range-end\"\r\n class=\"min-w-10 truncate text-right\"\r\n >{{ \"filters.to\" | transloco }}</label\r\n >\r\n <input\r\n id=\"datepicker-range-end\"\r\n name=\"end\"\r\n type=\"text\"\r\n readonly\r\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\r\n [value]=\"customRangeTo()\"\r\n (click)=\"selectAndOpenDialog()\" />\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n </ul>\r\n </form>\r\n</details>\r\n\r\n<aggregation-date-range-dialog\r\n [lang]=\"lang()\"\r\n [useDateRange]=\"false\"\r\n [min]=\"form.get('customRange.from')?.value || undefined\"\r\n [max]=\"form.get('customRange.to')?.value || undefined\"\r\n (rangeSelected)=\"onRangeSelected($event)\" />\r\n", styles: [":host{display:block;min-width:200px}ul[role=list]{scrollbar-width:thin}\n"], dependencies: [{ kind: "directive", type: IconButtonComponent, selector: "button[icon-button], icon-button, IconButton", inputs: ["class", "size"] }, { kind: "directive", type: ListItemComponent, selector: "[role=\"listitem\"], [role=\"option\"]", inputs: ["class", "variant", "decoration"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon, chevron-right-icon, chevronrighticon", inputs: ["class"] }, { kind: "component", type: AggregationDateRangeDialogComponent, selector: "aggregation-date-range-dialog", inputs: ["min", "max", "lang", "useDateRange"], outputs: ["rangeSelected"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass", "class"] }, { kind: "component", type: FilterIcon, selector: "filter-icon, FilterIcon", inputs: ["class"] }, { kind: "component", type: FilterXIcon, selector: "filter-x-icon, FilterXIcon", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
|
|
9380
9822
|
}
|
|
9381
9823
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: DateComponent, decorators: [{
|
|
9382
9824
|
type: Component,
|
|
@@ -9393,7 +9835,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
9393
9835
|
FilterXIcon
|
|
9394
9836
|
], host: {
|
|
9395
9837
|
class: "@container"
|
|
9396
|
-
}, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible()\"\r\n class=\"m-0 flex h-8 w-full items-center pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @let icon = aggregation()?.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\r\n }\r\n <span class=\"grow\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (hasFilters()) {\r\n <icon-button\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"clear()\"\r\n (keydown.enter)=\"clear()\">\r\n <filter-x-icon />\r\n <span class=\"sr-only\">{{ \"filters.clearFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n\r\n @if (selection() && validSelection()) {\r\n <icon-button\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"apply()\"\r\n (keydown.enter)=\"apply()\">\r\n <filter-icon />\r\n <span class=\"sr-only\">{{ \"filters.applyFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n @if (collapsible()) {\r\n <icon-button\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n </icon-button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n <form [formGroup]=\"form\">\r\n <ul\r\n class=\"
|
|
9838
|
+
}, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible()\"\r\n class=\"m-0 flex h-8 w-full items-center pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @let icon = aggregation()?.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\r\n }\r\n <span class=\"grow\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (hasFilters()) {\r\n <icon-button\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"clear()\"\r\n (keydown.enter)=\"clear()\">\r\n <filter-x-icon />\r\n <span class=\"sr-only\">{{ \"filters.clearFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n\r\n @if (!isControlled() && selection() && validSelection()) {\r\n <icon-button\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"apply()\"\r\n (keydown.enter)=\"apply()\">\r\n <filter-icon />\r\n <span class=\"sr-only\">{{ \"filters.applyFilters\" | transloco }}</span>\r\n </icon-button>\r\n }\r\n @if (collapsible()) {\r\n <icon-button\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n </icon-button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n <form [formGroup]=\"form\">\r\n <ul\r\n class=\"flex max-h-[var(--scroll-height,20rem)] snap-y snap-start scrollbar-thin flex-col gap-1 overflow-auto pt-2\"\r\n role=\"list\">\r\n @for (option of dateOptions(); track $index) {\r\n <li\r\n role=\"listitem\"\r\n tabindex=\"0\"\r\n (click)=\"radio.click()\"\r\n [attr.aria-label]=\"option.display | syslang | transloco\"\r\n [class]=\"\r\n cn(\r\n 'flex p-0 px-2 leading-7',\r\n form.get('option')?.value === option.display && 'bg-accent',\r\n option.hidden && 'hidden',\r\n option.disabled && 'disabled pointer-events-none text-neutral-300'\r\n )\r\n \"\r\n [attr.aria-hidden]=\"option.disabled\">\r\n <input\r\n #radio\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-{{ option.display }}\"\r\n [attr.disabled]=\"option.disabled ? true : null\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n (click)=\"select()\"\r\n value=\"{{ option.display }}\" />\r\n\r\n <label\r\n for=\"date-filter-{{ option.display }}\"\r\n class=\"grow cursor-pointer p-1\">\r\n {{ option.display | syslang | transloco }}\r\n </label>\r\n </li>\r\n }\r\n\r\n @if (allowCustomRange) {\r\n <li\r\n role=\"listitem\"\r\n aria-label=\"open date range picker\"\r\n class=\"flex px-2 leading-7\"\r\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\r\n <input\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-range-dialog\"\r\n value=\"custom-range\"\r\n (click)=\"select()\" />\r\n <div\r\n class=\"@container flex grow justify-end gap-1 p-1 @max-[340px]:flex-wrap\">\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\r\n \"filters.from\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-start\"\r\n name=\"start\"\r\n type=\"text\"\r\n readonly\r\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\r\n [value]=\"customRangeFrom()\"\r\n (click)=\"selectAndOpenDialog()\" />\r\n </div>\r\n <div class=\"flex gap-1\">\r\n <label\r\n for=\"datepicker-range-end\"\r\n class=\"min-w-10 truncate text-right\"\r\n >{{ \"filters.to\" | transloco }}</label\r\n >\r\n <input\r\n id=\"datepicker-range-end\"\r\n name=\"end\"\r\n type=\"text\"\r\n readonly\r\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\r\n [value]=\"customRangeTo()\"\r\n (click)=\"selectAndOpenDialog()\" />\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n </ul>\r\n </form>\r\n</details>\r\n\r\n<aggregation-date-range-dialog\r\n [lang]=\"lang()\"\r\n [useDateRange]=\"false\"\r\n [min]=\"form.get('customRange.from')?.value || undefined\"\r\n [max]=\"form.get('customRange.to')?.value || undefined\"\r\n (rangeSelected)=\"onRangeSelected($event)\" />\r\n", styles: [":host{display:block;min-width:200px}ul[role=list]{scrollbar-width:thin}\n"] }]
|
|
9397
9839
|
}] });
|
|
9398
9840
|
|
|
9399
9841
|
class ArticleEntities {
|
|
@@ -13726,6 +14168,14 @@ class AggregationTreeComponent {
|
|
|
13726
14168
|
collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : []));
|
|
13727
14169
|
searchable = input(undefined, ...(ngDevMode ? [{ debugName: "searchable" }] : []));
|
|
13728
14170
|
showFiltersCount = input(false, ...(ngDevMode ? [{ debugName: "showFiltersCount", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
14171
|
+
/**
|
|
14172
|
+
* Whether to render the per-node checkbox. When `false`, the checkbox is hidden
|
|
14173
|
+
* but selection still toggles on row click — pair it with a custom `aggregationItem`
|
|
14174
|
+
* template that reflects the `selected` context to convey selection.
|
|
14175
|
+
*
|
|
14176
|
+
* @default true
|
|
14177
|
+
*/
|
|
14178
|
+
showCheckbox = input(true, ...(ngDevMode ? [{ debugName: "showCheckbox", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
13729
14179
|
expandedLevel = input(undefined, ...(ngDevMode ? [{ debugName: "expandedLevel", transform: (v) => {
|
|
13730
14180
|
const n = numberAttribute(v);
|
|
13731
14181
|
return Number.isNaN(n) ? undefined : n;
|
|
@@ -13742,13 +14192,63 @@ class AggregationTreeComponent {
|
|
|
13742
14192
|
* @default true
|
|
13743
14193
|
*/
|
|
13744
14194
|
syncUrl = input(true, ...(ngDevMode ? [{ debugName: "syncUrl", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
14195
|
+
/**
|
|
14196
|
+
* Controlled query. When set, the component's own operations (load-more,
|
|
14197
|
+
* search-within-facet, tree-expand) run against this query instead of reading
|
|
14198
|
+
* the shared `QueryParamsStore`. Use to target a query not tied to the current
|
|
14199
|
+
* app, without mutating the shared store or the URL.
|
|
14200
|
+
*
|
|
14201
|
+
* @default undefined — read `QueryParamsStore.getQuery()` (current behavior)
|
|
14202
|
+
*/
|
|
14203
|
+
controlledQuery = input(undefined, ...(ngDevMode ? [{ debugName: "controlledQuery", alias: "query" }] : [{ alias: "query" }]));
|
|
14204
|
+
/**
|
|
14205
|
+
* Controlled data. When set, the component renders this aggregation object
|
|
14206
|
+
* instead of reading it from the shared `AggregationsStore`.
|
|
14207
|
+
*
|
|
14208
|
+
* @default undefined — read `AggregationsStore` (current behavior)
|
|
14209
|
+
*/
|
|
14210
|
+
controlledAggregation = input(undefined, ...(ngDevMode ? [{ debugName: "controlledAggregation", alias: "aggregation" }] : [{ alias: "aggregation" }]));
|
|
14211
|
+
/**
|
|
14212
|
+
* Controlled selection. When set, item `$selected` state is derived from this
|
|
14213
|
+
* value (`paths` in the `"/A/B/*"` format) instead of the shared
|
|
14214
|
+
* `QueryParamsStore` filters.
|
|
14215
|
+
*
|
|
14216
|
+
* @default undefined — derive from `QueryParamsStore` filters (current behavior)
|
|
14217
|
+
*/
|
|
14218
|
+
controlledSelection = input(undefined, ...(ngDevMode ? [{ debugName: "controlledSelection", alias: "selection" }] : [{ alias: "selection" }]));
|
|
13745
14219
|
/* outputs */
|
|
13746
14220
|
onSelect = output();
|
|
13747
14221
|
onApply = output();
|
|
13748
14222
|
onClear = output();
|
|
14223
|
+
/**
|
|
14224
|
+
* Emits the new selection (`"/A/B/*"` paths) when the user applies or clears
|
|
14225
|
+
* filters in controlled mode. The counterpart of the `[selection]` input —
|
|
14226
|
+
* emitted before `filtersChange` and `onApply`/`onClear`. In controlled mode
|
|
14227
|
+
* this is the only way changes surface: nothing is written to the shared
|
|
14228
|
+
* stores or the URL.
|
|
14229
|
+
*/
|
|
14230
|
+
selectionChange = output();
|
|
14231
|
+
/**
|
|
14232
|
+
* Emits the applied selection as a structured `Filter` (the exact filter the
|
|
14233
|
+
* default mode would write to the store), ready for `fetchQuery({ filters })`.
|
|
14234
|
+
* `undefined` on clear. Controlled mode only — emitted after `selectionChange`,
|
|
14235
|
+
* before `onApply`/`onClear`.
|
|
14236
|
+
*/
|
|
14237
|
+
filtersChange = output();
|
|
13749
14238
|
/* view queries */
|
|
13750
14239
|
virtualItems = viewChildren("virtualItem", ...(ngDevMode ? [{ debugName: "virtualItems" }] : []));
|
|
13751
14240
|
scrollElement = viewChild("scrollElement", ...(ngDevMode ? [{ debugName: "scrollElement" }] : []));
|
|
14241
|
+
/* optional host-provided template for the node content/label region */
|
|
14242
|
+
itemDirective = contentChild(AggregationItemDirective, ...(ngDevMode ? [{ debugName: "itemDirective" }] : []));
|
|
14243
|
+
/**
|
|
14244
|
+
* Item content template relayed by the `<Aggregation>` wrapper (which cannot
|
|
14245
|
+
* project content into this component). Also usable directly as a `TemplateRef`
|
|
14246
|
+
* input. A directive projected straight onto this component takes precedence.
|
|
14247
|
+
*/
|
|
14248
|
+
itemTemplateInput = input(undefined, ...(ngDevMode ? [{ debugName: "itemTemplateInput", alias: "itemTemplate" }] : [{
|
|
14249
|
+
alias: "itemTemplate",
|
|
14250
|
+
}]));
|
|
14251
|
+
resolvedItemTemplate = computed(() => this.itemDirective()?.template ?? this.itemTemplateInput(), ...(ngDevMode ? [{ debugName: "resolvedItemTemplate" }] : []));
|
|
13752
14252
|
/* selection state */
|
|
13753
14253
|
selection = signal(false, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
13754
14254
|
isAllSelected = signal(false, ...(ngDevMode ? [{ debugName: "isAllSelected" }] : []));
|
|
@@ -13760,8 +14260,12 @@ class AggregationTreeComponent {
|
|
|
13760
14260
|
column: this.column,
|
|
13761
14261
|
searchText: this.searchText,
|
|
13762
14262
|
syncUrl: this.syncUrl,
|
|
14263
|
+
query: this.controlledQuery,
|
|
14264
|
+
aggregation: this.controlledAggregation,
|
|
14265
|
+
selection: this.controlledSelection,
|
|
13763
14266
|
});
|
|
13764
14267
|
/* spread from base */
|
|
14268
|
+
isControlled = this.base.isControlled;
|
|
13765
14269
|
aggregationsStore = this.base.aggregationsStore;
|
|
13766
14270
|
queryParamsStore = this.base.queryParamsStore;
|
|
13767
14271
|
appStore = this.base.appStore;
|
|
@@ -13788,6 +14292,21 @@ class AggregationTreeComponent {
|
|
|
13788
14292
|
#measureItems = effect(() => this.virtualItems().forEach((el) => this.virtualizer.measureElement(el.nativeElement)), ...(ngDevMode ? [{ debugName: "#measureItems" }] : []));
|
|
13789
14293
|
linkChildren = computed(() => this.appStore.general()?.features?.filterLinkChildren, ...(ngDevMode ? [{ debugName: "linkChildren" }] : []));
|
|
13790
14294
|
aggregation = computed(() => {
|
|
14295
|
+
if (this.isControlled()) {
|
|
14296
|
+
const agg = this.base.controlledAggregation();
|
|
14297
|
+
if (!agg)
|
|
14298
|
+
return null;
|
|
14299
|
+
if (!agg.isTree) {
|
|
14300
|
+
error("The aggregation tree component does not support list aggregations. Please use the <Aggregation /> component instead.");
|
|
14301
|
+
}
|
|
14302
|
+
const enriched = this.aggregationsService.enrichAggregation(agg, this.base.selectionValues() ?? []);
|
|
14303
|
+
const expandedLevel = this.expandedLevel() ?? enriched.expandedLevel;
|
|
14304
|
+
if (expandedLevel) {
|
|
14305
|
+
this.expandItems(enriched.items, expandedLevel);
|
|
14306
|
+
}
|
|
14307
|
+
enriched.searchable = this.searchable() ?? enriched.searchable;
|
|
14308
|
+
return enriched;
|
|
14309
|
+
}
|
|
13791
14310
|
getState(this.aggregationsStore);
|
|
13792
14311
|
const name = this.name();
|
|
13793
14312
|
const column = this.column();
|
|
@@ -13818,7 +14337,10 @@ class AggregationTreeComponent {
|
|
|
13818
14337
|
else if (agg?.items) {
|
|
13819
14338
|
res = this.addCurrentFiltersToItems();
|
|
13820
14339
|
}
|
|
13821
|
-
|
|
14340
|
+
// Controlled mode never touches sessionStorage: selection persistence is the
|
|
14341
|
+
// host's responsibility via the `selection` input, and the `agg-${column}` key
|
|
14342
|
+
// is shared with any default-mode instance on the same column.
|
|
14343
|
+
const sessionAggItem = this.isControlled() ? null : sessionStorage.getItem(`agg-${agg?.column}`);
|
|
13822
14344
|
const sessionAgg = JSON.parse(sessionAggItem || "[]");
|
|
13823
14345
|
return this.processAggregations(sessionAgg.length ? this.setSelected(res, sessionAgg) : res);
|
|
13824
14346
|
}, ...(ngDevMode ? [{ debugName: "items" }] : []));
|
|
@@ -13842,13 +14364,16 @@ class AggregationTreeComponent {
|
|
|
13842
14364
|
constructor() {
|
|
13843
14365
|
effect(() => {
|
|
13844
14366
|
getState(this.aggregationsStore);
|
|
14367
|
+
if (this.isControlled())
|
|
14368
|
+
return;
|
|
13845
14369
|
sessionStorage.removeItem(`agg-${this.column()}`);
|
|
13846
14370
|
});
|
|
13847
14371
|
effect(() => {
|
|
13848
14372
|
this.filters.set(this.getFilters());
|
|
13849
14373
|
});
|
|
13850
14374
|
this.destroyRef.onDestroy(() => {
|
|
13851
|
-
|
|
14375
|
+
if (!this.isControlled())
|
|
14376
|
+
sessionStorage.removeItem(`agg-${this.aggregation()?.column}`);
|
|
13852
14377
|
if (this.selection()) {
|
|
13853
14378
|
const unselect = (items) => {
|
|
13854
14379
|
items.forEach((item) => {
|
|
@@ -13865,7 +14390,13 @@ class AggregationTreeComponent {
|
|
|
13865
14390
|
clear() {
|
|
13866
14391
|
const agg = this.aggregation();
|
|
13867
14392
|
if (agg) {
|
|
13868
|
-
|
|
14393
|
+
if (this.isControlled()) {
|
|
14394
|
+
this.selectionChange.emit({ kind: "paths", paths: [] });
|
|
14395
|
+
this.filtersChange.emit(undefined);
|
|
14396
|
+
}
|
|
14397
|
+
else {
|
|
14398
|
+
this.queryParamsStore.removeFilterByName(agg.name, agg.column, { syncUrl: this.syncUrl() });
|
|
14399
|
+
}
|
|
13869
14400
|
const unselect = (items) => {
|
|
13870
14401
|
items.forEach((item) => {
|
|
13871
14402
|
item.$selected = false;
|
|
@@ -13875,7 +14406,8 @@ class AggregationTreeComponent {
|
|
|
13875
14406
|
});
|
|
13876
14407
|
};
|
|
13877
14408
|
unselect(this.items());
|
|
13878
|
-
|
|
14409
|
+
if (!this.isControlled())
|
|
14410
|
+
sessionStorage.removeItem(`agg-${this.aggregation()?.column}`);
|
|
13879
14411
|
this.selection.set(false);
|
|
13880
14412
|
this.isAllSelected.set(false);
|
|
13881
14413
|
this.onClear.emit();
|
|
@@ -13896,21 +14428,57 @@ class AggregationTreeComponent {
|
|
|
13896
14428
|
}
|
|
13897
14429
|
}
|
|
13898
14430
|
apply(overrideFilters) {
|
|
13899
|
-
sessionStorage.setItem(`agg-${this.aggregation()?.column}`, JSON.stringify([...this.items()]));
|
|
13900
14431
|
const filters = overrideFilters || this.getFilters();
|
|
13901
14432
|
const agg = this.aggregation();
|
|
13902
|
-
|
|
14433
|
+
if (this.isControlled()) {
|
|
14434
|
+
const paths = filters[0]?.values ?? [];
|
|
14435
|
+
this.selectionChange.emit({ kind: "paths", paths });
|
|
14436
|
+
this.filtersChange.emit(legacyFilterToFilter(this.base.buildAppliedFilter(filters, agg)));
|
|
14437
|
+
}
|
|
14438
|
+
else {
|
|
14439
|
+
sessionStorage.setItem(`agg-${agg?.column}`, JSON.stringify([...this.items()]));
|
|
14440
|
+
this.base.applyFilters(filters, agg, () => this.clear());
|
|
14441
|
+
}
|
|
13903
14442
|
this.searchText.set("");
|
|
13904
14443
|
this.selection.set(false);
|
|
13905
14444
|
this.onApply.emit();
|
|
13906
14445
|
}
|
|
13907
14446
|
loadMore() {
|
|
14447
|
+
// Controlled mode loads into the local state — never the shared AggregationsStore.
|
|
14448
|
+
if (this.isControlled()) {
|
|
14449
|
+
const agg = this.base.controlledAggregation();
|
|
14450
|
+
if (!agg)
|
|
14451
|
+
return;
|
|
14452
|
+
this.aggregationsService
|
|
14453
|
+
.loadMore(this.base.resolvedQuery(), agg, undefined, { count: this.base.resolvedAggregationCount(agg) })
|
|
14454
|
+
.subscribe((aggregation) => {
|
|
14455
|
+
this.base.controlledAggregation.set(aggregation);
|
|
14456
|
+
});
|
|
14457
|
+
return;
|
|
14458
|
+
}
|
|
13908
14459
|
const q = this.queryParamsStore.getQuery();
|
|
13909
14460
|
this.aggregationsService.loadMore(q, this.aggregation()).subscribe((aggregation) => {
|
|
13910
14461
|
this.aggregationsStore.updateAggregation(aggregation);
|
|
13911
14462
|
});
|
|
13912
14463
|
}
|
|
13913
14464
|
async open(node) {
|
|
14465
|
+
// Controlled mode expands into the local state via the guard-free fetch —
|
|
14466
|
+
// never the shared AggregationsStore, never QueryService's global result signal.
|
|
14467
|
+
if (this.isControlled()) {
|
|
14468
|
+
const controlled = this.base.controlledAggregation();
|
|
14469
|
+
if (!controlled)
|
|
14470
|
+
return;
|
|
14471
|
+
// Spread before mutating: resolvedQuery() may return the host's query object.
|
|
14472
|
+
const q = { ...this.base.resolvedQuery() };
|
|
14473
|
+
delete q.filters;
|
|
14474
|
+
const agg = await firstValueFrom(this.aggregationsService.open(q, controlled, node, {
|
|
14475
|
+
guardFree: true,
|
|
14476
|
+
}));
|
|
14477
|
+
node.$opened = true;
|
|
14478
|
+
// replaceInTree mutates and returns the same reference — spread so the signal notifies.
|
|
14479
|
+
this.base.controlledAggregation.set({ ...agg });
|
|
14480
|
+
return;
|
|
14481
|
+
}
|
|
13914
14482
|
const q = this.queryParamsStore.getQuery();
|
|
13915
14483
|
delete q.filters;
|
|
13916
14484
|
const agg = await firstValueFrom(this.aggregationsService.open(q, this.aggregation(), node));
|
|
@@ -13922,7 +14490,9 @@ class AggregationTreeComponent {
|
|
|
13922
14490
|
this.onSelect.emit(selectedItems);
|
|
13923
14491
|
this.selection.set(selectedItems.length > 0 || this.hasFilters());
|
|
13924
14492
|
this.verifySelected();
|
|
13925
|
-
|
|
14493
|
+
if (!this.isControlled()) {
|
|
14494
|
+
sessionStorage.setItem(`agg-${this.aggregation()?.column}`, JSON.stringify([...this.items()]));
|
|
14495
|
+
}
|
|
13926
14496
|
}
|
|
13927
14497
|
/* item-level methods — called from the ng-template */
|
|
13928
14498
|
treeItemName(item) {
|
|
@@ -13930,6 +14500,10 @@ class AggregationTreeComponent {
|
|
|
13930
14500
|
return typeof value === "string" ? value : `${value}`;
|
|
13931
14501
|
}
|
|
13932
14502
|
isTreeItemFiltered(item, field) {
|
|
14503
|
+
if (this.isControlled()) {
|
|
14504
|
+
const values = [item.value, `/${item.$path}/*`];
|
|
14505
|
+
return (this.base.selectionValues() ?? []).some((value) => values.some((v) => v === value));
|
|
14506
|
+
}
|
|
13933
14507
|
const filters = this.queryParamsStore.getFilter({ field: field ?? undefined, name: this.treeItemName(item) });
|
|
13934
14508
|
if (!filters)
|
|
13935
14509
|
return false;
|
|
@@ -13990,7 +14564,10 @@ class AggregationTreeComponent {
|
|
|
13990
14564
|
const notSelectedItems = this.getFlattenTreeItems()
|
|
13991
14565
|
.filter((item) => !item.$selected)
|
|
13992
14566
|
.map((item) => (item.$path ? `/${item.$path}/*` : ""));
|
|
13993
|
-
const
|
|
14567
|
+
const sel = this.controlledSelection();
|
|
14568
|
+
const currentFilters = this.isControlled()
|
|
14569
|
+
? (sel?.kind === "paths" ? sel.paths : [])
|
|
14570
|
+
: this.queryParamsStore.getFilter({ field, name })?.values || [];
|
|
13994
14571
|
if (currentFilters) {
|
|
13995
14572
|
const filteredCurrentFilters = currentFilters.filter((filter) => !notSelectedItems.includes(filter));
|
|
13996
14573
|
if (filteredCurrentFilters.length > 0) {
|
|
@@ -14116,7 +14693,7 @@ class AggregationTreeComponent {
|
|
|
14116
14693
|
return false;
|
|
14117
14694
|
}
|
|
14118
14695
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14119
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AggregationTreeComponent, isStandalone: true, selector: "AggregationTree, aggregation-tree, aggregationtree", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, showFiltersCount: { classPropertyName: "showFiltersCount", publicName: "showFiltersCount", isSignal: true, isRequired: false, transformFunction: null }, expandedLevel: { classPropertyName: "expandedLevel", publicName: "expandedLevel", isSignal: true, isRequired: false, transformFunction: null }, syncUrl: { classPropertyName: "syncUrl", publicName: "syncUrl", isSignal: true, isRequired: false, transformFunction: null }, searchText: { classPropertyName: "searchText", publicName: "searchText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", onApply: "onApply", onClear: "onClear", searchText: "searchTextChange" }, host: { properties: { "class": "cn(\"block h-[inherit] max-h-[inherit] w-[inherit]\", class())" } }, viewQueries: [{ propertyName: "virtualItems", predicate: ["virtualItem"], descendants: true, isSignal: true }, { propertyName: "scrollElement", first: true, predicate: ["scrollElement"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (!aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <triangle-alert-icon class=\"mr-1\" />\r\n The aggregationTree component does not support list aggregations. Please use\r\n the <Aggregation /> component instead.\r\n </div>\r\n}\r\n<aggregation-panel\r\n [id]=\"id()\"\r\n [collapsible]=\"collapsible()\"\r\n [collapsed]=\"collapsed()\"\r\n [isEmpty]=\"isEmpty()\"\r\n [isDate]=\"isDate()\"\r\n [aggregation]=\"aggregation()\"\r\n [showFiltersCount]=\"showFiltersCount()\"\r\n [filtersCount]=\"filtersCount()\"\r\n [hasFilters]=\"hasFilters()\"\r\n [selection]=\"selection()\"\r\n [isAllSelected]=\"isAllSelected()\"\r\n [(searchText)]=\"searchText\"\r\n [itemsLength]=\"items().length\"\r\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\r\n [searchedItemsLength]=\"searchedItems().length\"\r\n (cleared)=\"clear()\"\r\n (applied)=\"apply()\"\r\n (allSelected)=\"selectAll()\"\r\n (allUnselected)=\"unselectAll()\"\r\n (loadedMore)=\"loadMore()\">\r\n <div\r\n #scrollElement\r\n class=\"scrollbar-thin max-h-(--scroll-height,20rem) w-full overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"tree\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n <div\r\n class=\"absolute top-0 left-0 w-full\"\r\n [style.transform]=\"\r\n 'translateY(' +\r\n (virtualizer.getVirtualItems()[0]\r\n ? virtualizer.getVirtualItems()[0].start\r\n : 0) +\r\n 'px)'\r\n \">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div #virtualItem [attr.data-index]=\"vItem.index\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"treeItemTpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: item,\r\n path: [],\r\n field: aggregation()?.column,\r\n tpl: treeItemTpl,\r\n parent: null\r\n }\">\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</aggregation-panel>\r\n\r\n<ng-template\r\n #treeItemTpl\r\n let-node=\"node\"\r\n let-path=\"path\"\r\n let-field=\"field\"\r\n let-tpl=\"tpl\"\r\n let-parent=\"parent\">\r\n <div\r\n role=\"treeitem\"\r\n [style.--level]=\"treeItemLevel(node)\"\r\n [attr.aria-selected]=\"node.$selected ?? false\"\r\n [attr.aria-expanded]=\"\r\n node.hasChildren !== false ? (node.$opened ?? false) : null\r\n \"\r\n [attr.aria-disabled]=\"node.count === 0 ? 'true' : null\"\r\n [attr.disabled]=\"node.count === 0 ? 'disabled' : null\">\r\n <a\r\n [attr.aria-label]=\"treeItemName(node) | syslang\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n node.count === 0 && 'disabled pointer-events-none',\r\n (node.$selected || node.$selectedVisually) && ''\r\n )\r\n \"\r\n (click)=\"selectTreeItem(node, parent, $event, true)\">\r\n <button\r\n variant=\"none\"\r\n [iconOnly]=\"true\"\r\n (click)=\"toggleTreeNode($event, node)\"\r\n class=\"transition-transform ease-in hover:scale-125\"\r\n aria-label=\"Open\">\r\n <ChevronRight\r\n [class]=\"\r\n cn(\r\n 'size-4 translate-x-1',\r\n node.$opened && 'rotate-90',\r\n !node.hasChildren && 'hidden'\r\n )\r\n \"\r\n width=\"16\"\r\n height=\"16\" />\r\n </button>\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ node.value }}\"\r\n [attr.disabled]=\"node.count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"node.count === 0\"\r\n (keydown.enter)=\"selectTreeItem(node, parent, $event)\"\r\n [checked]=\"node.$selected || node.$selectedVisually\" />\r\n @let icon = node.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"self-center justify-self-center\" />\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isTreeItemFiltered(node, field)\r\n ? 'filters.removeFilter'\r\n : 'filters.addFilter'\r\n ) | transloco) +\r\n ': ' +\r\n (treeItemName(node) | syslang)\r\n : (treeItemName(node) | syslang)\r\n \"\r\n (click)=\"onTreeItemTextClick(node, parent, $event)\">\r\n @for (\r\n chunk of (treeItemName(node) | syslang) ?? \"\"\r\n | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && node.count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n node.count\r\n }}</span>\r\n }\r\n </a>\r\n @if (node.hasChildren && node.$opened) {\r\n <div role=\"group\">\r\n @for (child of node.items; track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: child,\r\n path: treeChildrenPath(node, path),\r\n field: field,\r\n tpl: tpl,\r\n parent: node\r\n }\">\r\n </ng-container>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: ["div[role=treeitem]:has(+div[role=treeitem]){margin-bottom:var(--agg-item-gap, 0)}div[role=treeitem]{display:block;-webkit-user-select:none;user-select:none}div[role=treeitem] a{padding-left:calc(var(--agg-tree-indent, .5rem) * var(--level, 0));line-height:var(--agg-item-height, inherit)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon, chevron-right-icon, chevronrighticon", inputs: ["class"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass", "class"] }, { kind: "component", type: TriangleAlertIcon, selector: "triangle-alert-icon, TriangleAlertIcon", inputs: ["class"] }, { kind: "component", type: AggregationPanelComponent, selector: "AggregationPanel, aggregation-panel", inputs: ["id", "collapsible", "collapsed", "isDate", "isEmpty", "aggregation", "showFiltersCount", "filtersCount", "hasFilters", "selection", "isAllSelected", "searchText", "itemsLength", "hasMore", "searchedItemsLength"], outputs: ["searchTextChange", "cleared", "applied", "allSelected", "allUnselected", "loadedMore"] }, { kind: "pipe", type: SyslangPipe, name: "syslang" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: HighlightWordPipe, name: "highlightWord" }] });
|
|
14696
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AggregationTreeComponent, isStandalone: true, selector: "AggregationTree, aggregation-tree, aggregationtree", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, showFiltersCount: { classPropertyName: "showFiltersCount", publicName: "showFiltersCount", isSignal: true, isRequired: false, transformFunction: null }, showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, expandedLevel: { classPropertyName: "expandedLevel", publicName: "expandedLevel", isSignal: true, isRequired: false, transformFunction: null }, syncUrl: { classPropertyName: "syncUrl", publicName: "syncUrl", isSignal: true, isRequired: false, transformFunction: null }, controlledQuery: { classPropertyName: "controlledQuery", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, controlledAggregation: { classPropertyName: "controlledAggregation", publicName: "aggregation", isSignal: true, isRequired: false, transformFunction: null }, controlledSelection: { classPropertyName: "controlledSelection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, itemTemplateInput: { classPropertyName: "itemTemplateInput", publicName: "itemTemplate", isSignal: true, isRequired: false, transformFunction: null }, searchText: { classPropertyName: "searchText", publicName: "searchText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", onApply: "onApply", onClear: "onClear", selectionChange: "selectionChange", filtersChange: "filtersChange", searchText: "searchTextChange" }, host: { properties: { "class": "cn(\"block h-[inherit] max-h-[inherit] w-[inherit]\", class())" } }, queries: [{ propertyName: "itemDirective", first: true, predicate: AggregationItemDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "virtualItems", predicate: ["virtualItem"], descendants: true, isSignal: true }, { propertyName: "scrollElement", first: true, predicate: ["scrollElement"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (!aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <triangle-alert-icon class=\"mr-1\" />\r\n The aggregationTree component does not support list aggregations. Please use\r\n the <Aggregation /> component instead.\r\n </div>\r\n}\r\n<aggregation-panel\r\n [id]=\"id()\"\r\n [collapsible]=\"collapsible()\"\r\n [collapsed]=\"collapsed()\"\r\n [isEmpty]=\"isEmpty()\"\r\n [isDate]=\"isDate()\"\r\n [aggregation]=\"aggregation()\"\r\n [showFiltersCount]=\"showFiltersCount()\"\r\n [filtersCount]=\"filtersCount()\"\r\n [hasFilters]=\"hasFilters()\"\r\n [selection]=\"selection()\"\r\n [isAllSelected]=\"isAllSelected()\"\r\n [(searchText)]=\"searchText\"\r\n [itemsLength]=\"items().length\"\r\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\r\n [searchedItemsLength]=\"searchedItems().length\"\r\n (cleared)=\"clear()\"\r\n (applied)=\"apply()\"\r\n (allSelected)=\"selectAll()\"\r\n (allUnselected)=\"unselectAll()\"\r\n (loadedMore)=\"loadMore()\">\r\n <div\r\n #scrollElement\r\n class=\"max-h-(--scroll-height,20rem) w-full scrollbar-thin overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"tree\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n <div\r\n class=\"absolute top-0 left-0 w-full\"\r\n [style.transform]=\"\r\n 'translateY(' +\r\n (virtualizer.getVirtualItems()[0]\r\n ? virtualizer.getVirtualItems()[0].start\r\n : 0) +\r\n 'px)'\r\n \">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div #virtualItem [attr.data-index]=\"vItem.index\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"treeItemTpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: item,\r\n path: [],\r\n field: aggregation()?.column,\r\n tpl: treeItemTpl,\r\n parent: null\r\n }\">\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</aggregation-panel>\r\n\r\n<ng-template\r\n #treeItemTpl\r\n let-node=\"node\"\r\n let-path=\"path\"\r\n let-field=\"field\"\r\n let-tpl=\"tpl\"\r\n let-parent=\"parent\">\r\n <div\r\n role=\"treeitem\"\r\n [style.--level]=\"treeItemLevel(node)\"\r\n [attr.aria-selected]=\"node.$selected ?? false\"\r\n [attr.aria-expanded]=\"\r\n node.hasChildren !== false ? (node.$opened ?? false) : null\r\n \"\r\n [attr.aria-disabled]=\"node.count === 0 ? 'true' : null\"\r\n [attr.disabled]=\"node.count === 0 ? 'disabled' : null\">\r\n <a\r\n [attr.aria-label]=\"treeItemName(node) | syslang\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n node.count === 0 && 'disabled pointer-events-none',\r\n (node.$selected || node.$selectedVisually) && ''\r\n )\r\n \"\r\n (click)=\"selectTreeItem(node, parent, $event, true)\">\r\n <button\r\n variant=\"none\"\r\n [iconOnly]=\"true\"\r\n (click)=\"toggleTreeNode($event, node)\"\r\n class=\"transition-transform ease-in hover:scale-125\"\r\n aria-label=\"Open\">\r\n <ChevronRight\r\n [class]=\"\r\n cn(\r\n 'size-4 translate-x-1',\r\n node.$opened && 'rotate-90',\r\n !node.hasChildren && 'hidden'\r\n )\r\n \"\r\n width=\"16\"\r\n height=\"16\" />\r\n </button>\r\n @if (showCheckbox()) {\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ node.value }}\"\r\n [attr.disabled]=\"node.count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"node.count === 0\"\r\n (keydown.enter)=\"selectTreeItem(node, parent, $event)\"\r\n [checked]=\"node.$selected || node.$selectedVisually\" />\r\n }\r\n @if (resolvedItemTemplate(); as tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: node,\r\n field: field,\r\n selected: node.$selected || node.$selectedVisually || false,\r\n count: node.count,\r\n name: (treeItemName(node) | syslang) ?? '',\r\n searchText: searchText(),\r\n level: treeItemLevel(node),\r\n expanded: node.$opened ?? false,\r\n hasChildren: node.hasChildren ?? false\r\n }\" />\r\n } @else {\r\n @let icon = node.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"self-center justify-self-center\" />\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isTreeItemFiltered(node, field)\r\n ? 'filters.removeFilter'\r\n : 'filters.addFilter'\r\n ) | transloco) +\r\n ': ' +\r\n (treeItemName(node) | syslang)\r\n : (treeItemName(node) | syslang)\r\n \"\r\n (click)=\"onTreeItemTextClick(node, parent, $event)\">\r\n @for (\r\n chunk of (treeItemName(node) | syslang) ?? \"\"\r\n | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && node.count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n node.count\r\n }}</span>\r\n }\r\n }\r\n </a>\r\n @if (node.hasChildren && node.$opened) {\r\n <div role=\"group\">\r\n @for (child of node.items; track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: child,\r\n path: treeChildrenPath(node, path),\r\n field: field,\r\n tpl: tpl,\r\n parent: node\r\n }\">\r\n </ng-container>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: ["div[role=treeitem]:has(+div[role=treeitem]){margin-bottom:var(--agg-item-gap, 0)}div[role=treeitem]{display:block;-webkit-user-select:none;user-select:none}div[role=treeitem] a{padding-left:calc(var(--agg-tree-indent, .5rem) * var(--level, 0));line-height:var(--agg-item-height, inherit)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon, chevron-right-icon, chevronrighticon", inputs: ["class"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass", "class"] }, { kind: "component", type: TriangleAlertIcon, selector: "triangle-alert-icon, TriangleAlertIcon", inputs: ["class"] }, { kind: "component", type: AggregationPanelComponent, selector: "AggregationPanel, aggregation-panel", inputs: ["id", "collapsible", "collapsed", "isDate", "isEmpty", "aggregation", "showFiltersCount", "filtersCount", "hasFilters", "selection", "isAllSelected", "searchText", "itemsLength", "hasMore", "searchedItemsLength"], outputs: ["searchTextChange", "cleared", "applied", "allSelected", "allUnselected", "loadedMore"] }, { kind: "pipe", type: SyslangPipe, name: "syslang" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: HighlightWordPipe, name: "highlightWord" }] });
|
|
14120
14697
|
}
|
|
14121
14698
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationTreeComponent, decorators: [{
|
|
14122
14699
|
type: Component,
|
|
@@ -14134,8 +14711,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14134
14711
|
AggregationPanelComponent,
|
|
14135
14712
|
], standalone: true, host: {
|
|
14136
14713
|
"[class]": 'cn("block h-[inherit] max-h-[inherit] w-[inherit]", class())',
|
|
14137
|
-
}, template: "@if (!aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <triangle-alert-icon class=\"mr-1\" />\r\n The aggregationTree component does not support list aggregations. Please use\r\n the <Aggregation /> component instead.\r\n </div>\r\n}\r\n<aggregation-panel\r\n [id]=\"id()\"\r\n [collapsible]=\"collapsible()\"\r\n [collapsed]=\"collapsed()\"\r\n [isEmpty]=\"isEmpty()\"\r\n [isDate]=\"isDate()\"\r\n [aggregation]=\"aggregation()\"\r\n [showFiltersCount]=\"showFiltersCount()\"\r\n [filtersCount]=\"filtersCount()\"\r\n [hasFilters]=\"hasFilters()\"\r\n [selection]=\"selection()\"\r\n [isAllSelected]=\"isAllSelected()\"\r\n [(searchText)]=\"searchText\"\r\n [itemsLength]=\"items().length\"\r\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\r\n [searchedItemsLength]=\"searchedItems().length\"\r\n (cleared)=\"clear()\"\r\n (applied)=\"apply()\"\r\n (allSelected)=\"selectAll()\"\r\n (allUnselected)=\"unselectAll()\"\r\n (loadedMore)=\"loadMore()\">\r\n <div\r\n #scrollElement\r\n class=\"
|
|
14138
|
-
}], ctorParameters: () => [], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], showFiltersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFiltersCount", required: false }] }], expandedLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedLevel", required: false }] }], syncUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncUrl", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], onApply: [{ type: i0.Output, args: ["onApply"] }], onClear: [{ type: i0.Output, args: ["onClear"] }], virtualItems: [{ type: i0.ViewChildren, args: ["virtualItem", { isSignal: true }] }], scrollElement: [{ type: i0.ViewChild, args: ["scrollElement", { isSignal: true }] }], searchText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchText", required: false }] }, { type: i0.Output, args: ["searchTextChange"] }] } });
|
|
14714
|
+
}, template: "@if (!aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <triangle-alert-icon class=\"mr-1\" />\r\n The aggregationTree component does not support list aggregations. Please use\r\n the <Aggregation /> component instead.\r\n </div>\r\n}\r\n<aggregation-panel\r\n [id]=\"id()\"\r\n [collapsible]=\"collapsible()\"\r\n [collapsed]=\"collapsed()\"\r\n [isEmpty]=\"isEmpty()\"\r\n [isDate]=\"isDate()\"\r\n [aggregation]=\"aggregation()\"\r\n [showFiltersCount]=\"showFiltersCount()\"\r\n [filtersCount]=\"filtersCount()\"\r\n [hasFilters]=\"hasFilters()\"\r\n [selection]=\"selection()\"\r\n [isAllSelected]=\"isAllSelected()\"\r\n [(searchText)]=\"searchText\"\r\n [itemsLength]=\"items().length\"\r\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\r\n [searchedItemsLength]=\"searchedItems().length\"\r\n (cleared)=\"clear()\"\r\n (applied)=\"apply()\"\r\n (allSelected)=\"selectAll()\"\r\n (allUnselected)=\"unselectAll()\"\r\n (loadedMore)=\"loadMore()\">\r\n <div\r\n #scrollElement\r\n class=\"max-h-(--scroll-height,20rem) w-full scrollbar-thin overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"tree\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n <div\r\n class=\"absolute top-0 left-0 w-full\"\r\n [style.transform]=\"\r\n 'translateY(' +\r\n (virtualizer.getVirtualItems()[0]\r\n ? virtualizer.getVirtualItems()[0].start\r\n : 0) +\r\n 'px)'\r\n \">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div #virtualItem [attr.data-index]=\"vItem.index\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"treeItemTpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: item,\r\n path: [],\r\n field: aggregation()?.column,\r\n tpl: treeItemTpl,\r\n parent: null\r\n }\">\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</aggregation-panel>\r\n\r\n<ng-template\r\n #treeItemTpl\r\n let-node=\"node\"\r\n let-path=\"path\"\r\n let-field=\"field\"\r\n let-tpl=\"tpl\"\r\n let-parent=\"parent\">\r\n <div\r\n role=\"treeitem\"\r\n [style.--level]=\"treeItemLevel(node)\"\r\n [attr.aria-selected]=\"node.$selected ?? false\"\r\n [attr.aria-expanded]=\"\r\n node.hasChildren !== false ? (node.$opened ?? false) : null\r\n \"\r\n [attr.aria-disabled]=\"node.count === 0 ? 'true' : null\"\r\n [attr.disabled]=\"node.count === 0 ? 'disabled' : null\">\r\n <a\r\n [attr.aria-label]=\"treeItemName(node) | syslang\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n node.count === 0 && 'disabled pointer-events-none',\r\n (node.$selected || node.$selectedVisually) && ''\r\n )\r\n \"\r\n (click)=\"selectTreeItem(node, parent, $event, true)\">\r\n <button\r\n variant=\"none\"\r\n [iconOnly]=\"true\"\r\n (click)=\"toggleTreeNode($event, node)\"\r\n class=\"transition-transform ease-in hover:scale-125\"\r\n aria-label=\"Open\">\r\n <ChevronRight\r\n [class]=\"\r\n cn(\r\n 'size-4 translate-x-1',\r\n node.$opened && 'rotate-90',\r\n !node.hasChildren && 'hidden'\r\n )\r\n \"\r\n width=\"16\"\r\n height=\"16\" />\r\n </button>\r\n @if (showCheckbox()) {\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ node.value }}\"\r\n [attr.disabled]=\"node.count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"node.count === 0\"\r\n (keydown.enter)=\"selectTreeItem(node, parent, $event)\"\r\n [checked]=\"node.$selected || node.$selectedVisually\" />\r\n }\r\n @if (resolvedItemTemplate(); as tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: node,\r\n field: field,\r\n selected: node.$selected || node.$selectedVisually || false,\r\n count: node.count,\r\n name: (treeItemName(node) | syslang) ?? '',\r\n searchText: searchText(),\r\n level: treeItemLevel(node),\r\n expanded: node.$opened ?? false,\r\n hasChildren: node.hasChildren ?? false\r\n }\" />\r\n } @else {\r\n @let icon = node.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"self-center justify-self-center\" />\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isTreeItemFiltered(node, field)\r\n ? 'filters.removeFilter'\r\n : 'filters.addFilter'\r\n ) | transloco) +\r\n ': ' +\r\n (treeItemName(node) | syslang)\r\n : (treeItemName(node) | syslang)\r\n \"\r\n (click)=\"onTreeItemTextClick(node, parent, $event)\">\r\n @for (\r\n chunk of (treeItemName(node) | syslang) ?? \"\"\r\n | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && node.count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n node.count\r\n }}</span>\r\n }\r\n }\r\n </a>\r\n @if (node.hasChildren && node.$opened) {\r\n <div role=\"group\">\r\n @for (child of node.items; track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: child,\r\n path: treeChildrenPath(node, path),\r\n field: field,\r\n tpl: tpl,\r\n parent: node\r\n }\">\r\n </ng-container>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: ["div[role=treeitem]:has(+div[role=treeitem]){margin-bottom:var(--agg-item-gap, 0)}div[role=treeitem]{display:block;-webkit-user-select:none;user-select:none}div[role=treeitem] a{padding-left:calc(var(--agg-tree-indent, .5rem) * var(--level, 0));line-height:var(--agg-item-height, inherit)}\n"] }]
|
|
14715
|
+
}], ctorParameters: () => [], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], showFiltersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFiltersCount", required: false }] }], showCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheckbox", required: false }] }], expandedLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedLevel", required: false }] }], syncUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncUrl", required: false }] }], controlledQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], controlledAggregation: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregation", required: false }] }], controlledSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], onApply: [{ type: i0.Output, args: ["onApply"] }], onClear: [{ type: i0.Output, args: ["onClear"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], filtersChange: [{ type: i0.Output, args: ["filtersChange"] }], virtualItems: [{ type: i0.ViewChildren, args: ["virtualItem", { isSignal: true }] }], scrollElement: [{ type: i0.ViewChild, args: ["scrollElement", { isSignal: true }] }], itemDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AggregationItemDirective), { isSignal: true }] }], itemTemplateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemTemplate", required: false }] }], searchText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchText", required: false }] }, { type: i0.Output, args: ["searchTextChange"] }] } });
|
|
14139
14716
|
|
|
14140
14717
|
class AggregationListComponent {
|
|
14141
14718
|
cn = cn;
|
|
@@ -14148,6 +14725,14 @@ class AggregationListComponent {
|
|
|
14148
14725
|
collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : []));
|
|
14149
14726
|
searchable = input(undefined, ...(ngDevMode ? [{ debugName: "searchable" }] : []));
|
|
14150
14727
|
showFiltersCount = input(null, ...(ngDevMode ? [{ debugName: "showFiltersCount", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
14728
|
+
/**
|
|
14729
|
+
* Whether to render the per-item checkbox. When `false`, the checkbox is hidden
|
|
14730
|
+
* but selection still toggles on row click — pair it with a custom `aggregationItem`
|
|
14731
|
+
* template that reflects the `selected` context to convey selection.
|
|
14732
|
+
*
|
|
14733
|
+
* @default true
|
|
14734
|
+
*/
|
|
14735
|
+
showCheckbox = input(true, ...(ngDevMode ? [{ debugName: "showCheckbox", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
14151
14736
|
/**
|
|
14152
14737
|
* When `false`, applying or clearing filters updates the store without
|
|
14153
14738
|
* synchronizing the URL (no browser history entry, not shareable/restorable).
|
|
@@ -14155,12 +14740,61 @@ class AggregationListComponent {
|
|
|
14155
14740
|
* @default true
|
|
14156
14741
|
*/
|
|
14157
14742
|
syncUrl = input(true, ...(ngDevMode ? [{ debugName: "syncUrl", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
14743
|
+
/**
|
|
14744
|
+
* Controlled query. When set, the component's own operations (load-more,
|
|
14745
|
+
* search-within-facet) run against this query instead of reading the shared
|
|
14746
|
+
* `QueryParamsStore`. Use to target a query not tied to the current app,
|
|
14747
|
+
* without mutating the shared store or the URL.
|
|
14748
|
+
*
|
|
14749
|
+
* @default undefined — read `QueryParamsStore.getQuery()` (current behavior)
|
|
14750
|
+
*/
|
|
14751
|
+
controlledQuery = input(undefined, ...(ngDevMode ? [{ debugName: "controlledQuery", alias: "query" }] : [{ alias: "query" }]));
|
|
14752
|
+
/**
|
|
14753
|
+
* Controlled data. When set, the component renders this aggregation object
|
|
14754
|
+
* instead of reading it from the shared `AggregationsStore`.
|
|
14755
|
+
*
|
|
14756
|
+
* @default undefined — read `AggregationsStore` (current behavior)
|
|
14757
|
+
*/
|
|
14758
|
+
controlledAggregation = input(undefined, ...(ngDevMode ? [{ debugName: "controlledAggregation", alias: "aggregation" }] : [{ alias: "aggregation" }]));
|
|
14759
|
+
/**
|
|
14760
|
+
* Controlled selection. When set, item `$selected` state is derived from this
|
|
14761
|
+
* value instead of the shared `QueryParamsStore` filters.
|
|
14762
|
+
*
|
|
14763
|
+
* @default undefined — derive from `QueryParamsStore` filters (current behavior)
|
|
14764
|
+
*/
|
|
14765
|
+
controlledSelection = input(undefined, ...(ngDevMode ? [{ debugName: "controlledSelection", alias: "selection" }] : [{ alias: "selection" }]));
|
|
14158
14766
|
/* outputs */
|
|
14159
14767
|
onSelect = output();
|
|
14160
14768
|
onApply = output();
|
|
14161
14769
|
onClear = output();
|
|
14770
|
+
/**
|
|
14771
|
+
* Emits the new selection (item values) when the user applies or clears
|
|
14772
|
+
* filters in controlled mode. The counterpart of the `[selection]` input —
|
|
14773
|
+
* emitted before `filtersChange` and `onApply`/`onClear`. In controlled mode
|
|
14774
|
+
* this is the only way changes surface: nothing is written to the shared
|
|
14775
|
+
* stores or the URL.
|
|
14776
|
+
*/
|
|
14777
|
+
selectionChange = output();
|
|
14778
|
+
/**
|
|
14779
|
+
* Emits the applied selection as a structured `Filter` (the exact filter the
|
|
14780
|
+
* default mode would write to the store), ready for `fetchQuery({ filters })`.
|
|
14781
|
+
* `undefined` on clear. Controlled mode only — emitted after `selectionChange`,
|
|
14782
|
+
* before `onApply`/`onClear`.
|
|
14783
|
+
*/
|
|
14784
|
+
filtersChange = output();
|
|
14162
14785
|
/* view queries */
|
|
14163
14786
|
scrollElement = viewChild("scrollElement", ...(ngDevMode ? [{ debugName: "scrollElement" }] : []));
|
|
14787
|
+
/* optional host-provided template for the item content/label region */
|
|
14788
|
+
itemDirective = contentChild(AggregationItemDirective, ...(ngDevMode ? [{ debugName: "itemDirective" }] : []));
|
|
14789
|
+
/**
|
|
14790
|
+
* Item content template relayed by the `<Aggregation>` wrapper (which cannot
|
|
14791
|
+
* project content into this component). Also usable directly as a `TemplateRef`
|
|
14792
|
+
* input. A directive projected straight onto this component takes precedence.
|
|
14793
|
+
*/
|
|
14794
|
+
itemTemplateInput = input(undefined, ...(ngDevMode ? [{ debugName: "itemTemplateInput", alias: "itemTemplate" }] : [{
|
|
14795
|
+
alias: "itemTemplate",
|
|
14796
|
+
}]));
|
|
14797
|
+
resolvedItemTemplate = computed(() => this.itemDirective()?.template ?? this.itemTemplateInput(), ...(ngDevMode ? [{ debugName: "resolvedItemTemplate" }] : []));
|
|
14164
14798
|
/* selection state */
|
|
14165
14799
|
selection = signal(false, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
14166
14800
|
isAllSelected = signal(false, ...(ngDevMode ? [{ debugName: "isAllSelected" }] : []));
|
|
@@ -14172,8 +14806,12 @@ class AggregationListComponent {
|
|
|
14172
14806
|
column: this.column,
|
|
14173
14807
|
searchText: this.searchText,
|
|
14174
14808
|
syncUrl: this.syncUrl,
|
|
14809
|
+
query: this.controlledQuery,
|
|
14810
|
+
aggregation: this.controlledAggregation,
|
|
14811
|
+
selection: this.controlledSelection,
|
|
14175
14812
|
});
|
|
14176
14813
|
/* spread from base */
|
|
14814
|
+
isControlled = this.base.isControlled;
|
|
14177
14815
|
aggregationsStore = this.base.aggregationsStore;
|
|
14178
14816
|
queryParamsStore = this.base.queryParamsStore;
|
|
14179
14817
|
appStore = this.base.appStore;
|
|
@@ -14197,6 +14835,17 @@ class AggregationListComponent {
|
|
|
14197
14835
|
scrollElement: this.scrollElement(),
|
|
14198
14836
|
}));
|
|
14199
14837
|
aggregation = computed(() => {
|
|
14838
|
+
if (this.isControlled()) {
|
|
14839
|
+
const agg = this.base.controlledAggregation();
|
|
14840
|
+
if (!agg)
|
|
14841
|
+
return null;
|
|
14842
|
+
if (agg.isTree) {
|
|
14843
|
+
error("The aggregation component does not support tree aggregations. Please use the <AggregationTree /> component instead.");
|
|
14844
|
+
}
|
|
14845
|
+
const enriched = this.aggregationsService.enrichAggregation(agg, this.base.selectionValues() ?? []);
|
|
14846
|
+
enriched.searchable = this.searchable() ?? enriched.searchable;
|
|
14847
|
+
return enriched;
|
|
14848
|
+
}
|
|
14200
14849
|
getState(this.aggregationsStore);
|
|
14201
14850
|
const name = this.name();
|
|
14202
14851
|
const column = this.column();
|
|
@@ -14258,7 +14907,13 @@ class AggregationListComponent {
|
|
|
14258
14907
|
clear() {
|
|
14259
14908
|
const agg = this.aggregation();
|
|
14260
14909
|
if (agg) {
|
|
14261
|
-
|
|
14910
|
+
if (this.isControlled()) {
|
|
14911
|
+
this.selectionChange.emit({ kind: "values", values: [] });
|
|
14912
|
+
this.filtersChange.emit(undefined);
|
|
14913
|
+
}
|
|
14914
|
+
else {
|
|
14915
|
+
this.queryParamsStore.removeFilterByName(agg.name, agg.column, { syncUrl: this.syncUrl() });
|
|
14916
|
+
}
|
|
14262
14917
|
this.selection.set(false);
|
|
14263
14918
|
this.isAllSelected.set(false);
|
|
14264
14919
|
}
|
|
@@ -14283,12 +14938,34 @@ class AggregationListComponent {
|
|
|
14283
14938
|
const agg = this.aggregation();
|
|
14284
14939
|
if (!agg)
|
|
14285
14940
|
return;
|
|
14286
|
-
|
|
14941
|
+
if (this.isControlled()) {
|
|
14942
|
+
const values = [...this.items(), ...this.searchedItems()]
|
|
14943
|
+
.filter((item) => item.$selected)
|
|
14944
|
+
.map((item) => String(item.value));
|
|
14945
|
+
this.selectionChange.emit({ kind: "values", values: Array.from(new Set(values)) });
|
|
14946
|
+
this.filtersChange.emit(legacyFilterToFilter(this.base.buildAppliedFilter(this.getFilters(), agg)));
|
|
14947
|
+
}
|
|
14948
|
+
else {
|
|
14949
|
+
this.base.applyFilters(this.getFilters(), agg, () => this.clear());
|
|
14950
|
+
}
|
|
14287
14951
|
this.searchText.set("");
|
|
14288
14952
|
this.selection.set(false);
|
|
14289
14953
|
this.onApply.emit();
|
|
14290
14954
|
}
|
|
14291
14955
|
loadMore() {
|
|
14956
|
+
// Controlled mode loads into the local state — never the shared AggregationsStore.
|
|
14957
|
+
if (this.isControlled()) {
|
|
14958
|
+
const agg = this.base.controlledAggregation();
|
|
14959
|
+
if (!agg)
|
|
14960
|
+
return;
|
|
14961
|
+
this.aggregationsService
|
|
14962
|
+
.loadMore(this.base.resolvedQuery(), agg, undefined, { count: this.base.resolvedAggregationCount(agg) })
|
|
14963
|
+
.subscribe((aggregation) => {
|
|
14964
|
+
this.base.controlledAggregation.set(aggregation);
|
|
14965
|
+
this.cdr.detectChanges();
|
|
14966
|
+
});
|
|
14967
|
+
return;
|
|
14968
|
+
}
|
|
14292
14969
|
const q = this.queryParamsStore.getQuery();
|
|
14293
14970
|
this.aggregationsService.loadMore(q, this.aggregation()).subscribe((aggregation) => {
|
|
14294
14971
|
this.aggregationsStore.updateAggregation(aggregation);
|
|
@@ -14306,6 +14983,9 @@ class AggregationListComponent {
|
|
|
14306
14983
|
return typeof value === "string" ? value : `${value}`;
|
|
14307
14984
|
}
|
|
14308
14985
|
isListItemFiltered(item, field) {
|
|
14986
|
+
if (this.isControlled()) {
|
|
14987
|
+
return (this.base.selectionValues() ?? []).some((value) => value === item.value);
|
|
14988
|
+
}
|
|
14309
14989
|
const filters = this.queryParamsStore.getFilter({ field: field ?? undefined, name: this.listItemName(item) });
|
|
14310
14990
|
if (!filters)
|
|
14311
14991
|
return false;
|
|
@@ -14335,7 +15015,7 @@ class AggregationListComponent {
|
|
|
14335
15015
|
return this.base.addCurrentFiltersToItems(this.aggregation());
|
|
14336
15016
|
}
|
|
14337
15017
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14338
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AggregationListComponent, isStandalone: true, selector: "AggregationList, aggregation-list, aggregationlist", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, showFiltersCount: { classPropertyName: "showFiltersCount", publicName: "showFiltersCount", isSignal: true, isRequired: false, transformFunction: null }, syncUrl: { classPropertyName: "syncUrl", publicName: "syncUrl", isSignal: true, isRequired: false, transformFunction: null }, searchText: { classPropertyName: "searchText", publicName: "searchText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", onApply: "onApply", onClear: "onClear", searchText: "searchTextChange" }, host: { properties: { "class": "cn(\"block h-[inherit] max-h-[inherit]\", class())" } }, viewQueries: [{ propertyName: "scrollElement", first: true, predicate: ["scrollElement"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <triangle-alert-icon class=\"mr-1\" />\r\n The aggregation component no longer supports tree aggregations. Please use\r\n the <AggregationTree /> component instead.\r\n </div>\r\n}\r\n<aggregation-panel\r\n [id]=\"id()\"\r\n [collapsible]=\"collapsible()\"\r\n [collapsed]=\"collapsed()\"\r\n [isEmpty]=\"isEmpty()\"\r\n [isDate]=\"isDate()\"\r\n [aggregation]=\"aggregation()\"\r\n [showFiltersCount]=\"showFiltersCount()\"\r\n [filtersCount]=\"filtersCount()\"\r\n [hasFilters]=\"hasFilters()\"\r\n [selection]=\"selection()\"\r\n [isAllSelected]=\"isAllSelected()\"\r\n [(searchText)]=\"searchText\"\r\n [itemsLength]=\"items().length\"\r\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\r\n [searchedItemsLength]=\"searchedItems().length\"\r\n (cleared)=\"clear()\"\r\n (applied)=\"apply()\"\r\n (allSelected)=\"selectAll()\"\r\n (allUnselected)=\"unselectAll()\"\r\n (loadedMore)=\"loadMore()\">\r\n <div\r\n #scrollElement\r\n class=\"
|
|
15018
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AggregationListComponent, isStandalone: true, selector: "AggregationList, aggregation-list, aggregationlist", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, showFiltersCount: { classPropertyName: "showFiltersCount", publicName: "showFiltersCount", isSignal: true, isRequired: false, transformFunction: null }, showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, syncUrl: { classPropertyName: "syncUrl", publicName: "syncUrl", isSignal: true, isRequired: false, transformFunction: null }, controlledQuery: { classPropertyName: "controlledQuery", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, controlledAggregation: { classPropertyName: "controlledAggregation", publicName: "aggregation", isSignal: true, isRequired: false, transformFunction: null }, controlledSelection: { classPropertyName: "controlledSelection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, itemTemplateInput: { classPropertyName: "itemTemplateInput", publicName: "itemTemplate", isSignal: true, isRequired: false, transformFunction: null }, searchText: { classPropertyName: "searchText", publicName: "searchText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", onApply: "onApply", onClear: "onClear", selectionChange: "selectionChange", filtersChange: "filtersChange", searchText: "searchTextChange" }, host: { properties: { "class": "cn(\"block h-[inherit] max-h-[inherit]\", class())" } }, queries: [{ propertyName: "itemDirective", first: true, predicate: AggregationItemDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "scrollElement", first: true, predicate: ["scrollElement"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <triangle-alert-icon class=\"mr-1\" />\r\n The aggregation component no longer supports tree aggregations. Please use\r\n the <AggregationTree /> component instead.\r\n </div>\r\n}\r\n<aggregation-panel\r\n [id]=\"id()\"\r\n [collapsible]=\"collapsible()\"\r\n [collapsed]=\"collapsed()\"\r\n [isEmpty]=\"isEmpty()\"\r\n [isDate]=\"isDate()\"\r\n [aggregation]=\"aggregation()\"\r\n [showFiltersCount]=\"showFiltersCount()\"\r\n [filtersCount]=\"filtersCount()\"\r\n [hasFilters]=\"hasFilters()\"\r\n [selection]=\"selection()\"\r\n [isAllSelected]=\"isAllSelected()\"\r\n [(searchText)]=\"searchText\"\r\n [itemsLength]=\"items().length\"\r\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\r\n [searchedItemsLength]=\"searchedItems().length\"\r\n (cleared)=\"clear()\"\r\n (applied)=\"apply()\"\r\n (allSelected)=\"selectAll()\"\r\n (allUnselected)=\"unselectAll()\"\r\n (loadedMore)=\"loadMore()\">\r\n <div\r\n #scrollElement\r\n class=\"max-h-(--scroll-height,20rem) w-full scrollbar-thin overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"listbox\"\r\n aria-multiselectable=\"true\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div\r\n class=\"absolute w-full\"\r\n [style.transform]=\"'translateY(' + vItem.start + 'px)'\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"item.$selected ?? false\"\r\n [attr.aria-disabled]=\"item.count === 0 ? 'true' : null\"\r\n [attr.disabled]=\"item.count === 0 ? 'disabled' : null\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"listItemTpl\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: item,\r\n field: aggregation()?.column\r\n }\">\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</aggregation-panel>\r\n\r\n<ng-template #listItemTpl let-item let-field=\"field\">\r\n <a\r\n [attr.aria-label]=\"listItemName(item) | syslang\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n item.count === 0 && 'disabled pointer-events-none'\r\n )\r\n \"\r\n (click)=\"selectListItem(item, $event)\">\r\n @if (showCheckbox()) {\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ item.value }}\"\r\n [attr.disabled]=\"item.count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"item.count === 0\"\r\n (keydown.enter)=\"selectListItem(item, $event)\"\r\n [checked]=\"item.$selected\" />\r\n }\r\n @if (resolvedItemTemplate(); as tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: item,\r\n field: field,\r\n selected: item.$selected ?? false,\r\n count: item.count,\r\n name: (listItemName(item) | syslang) ?? '',\r\n searchText: searchText()\r\n }\" />\r\n } @else {\r\n @let icon = item.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"self-center justify-self-center\" />\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isListItemFiltered(item, field)\r\n ? 'filters.removeFilter'\r\n : 'filters.addFilter'\r\n ) | transloco) +\r\n ': ' +\r\n (listItemName(item) | syslang)\r\n : (listItemName(item) | syslang)\r\n \"\r\n (click)=\"onListItemTextClick(item, $event)\">\r\n @for (\r\n chunk of (listItemName(item) | syslang) ?? \"\"\r\n | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && item.count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n item.count\r\n }}</span>\r\n }\r\n }\r\n </a>\r\n</ng-template>\r\n", styles: ["div[role=option]{display:block;-webkit-user-select:none;user-select:none}div[role=option] a{padding-left:var(--agg-tree-indent, .5rem);line-height:var(--agg-item-height, inherit)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass", "class"] }, { kind: "component", type: TriangleAlertIcon, selector: "triangle-alert-icon, TriangleAlertIcon", inputs: ["class"] }, { kind: "component", type: AggregationPanelComponent, selector: "AggregationPanel, aggregation-panel", inputs: ["id", "collapsible", "collapsed", "isDate", "isEmpty", "aggregation", "showFiltersCount", "filtersCount", "hasFilters", "selection", "isAllSelected", "searchText", "itemsLength", "hasMore", "searchedItemsLength"], outputs: ["searchTextChange", "cleared", "applied", "allSelected", "allUnselected", "loadedMore"] }, { kind: "pipe", type: SyslangPipe, name: "syslang" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: HighlightWordPipe, name: "highlightWord" }] });
|
|
14339
15019
|
}
|
|
14340
15020
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationListComponent, decorators: [{
|
|
14341
15021
|
type: Component,
|
|
@@ -14351,8 +15031,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14351
15031
|
AggregationPanelComponent,
|
|
14352
15032
|
], standalone: true, host: {
|
|
14353
15033
|
"[class]": 'cn("block h-[inherit] max-h-[inherit]", class())',
|
|
14354
|
-
}, template: "@if (aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <triangle-alert-icon class=\"mr-1\" />\r\n The aggregation component no longer supports tree aggregations. Please use\r\n the <AggregationTree /> component instead.\r\n </div>\r\n}\r\n<aggregation-panel\r\n [id]=\"id()\"\r\n [collapsible]=\"collapsible()\"\r\n [collapsed]=\"collapsed()\"\r\n [isEmpty]=\"isEmpty()\"\r\n [isDate]=\"isDate()\"\r\n [aggregation]=\"aggregation()\"\r\n [showFiltersCount]=\"showFiltersCount()\"\r\n [filtersCount]=\"filtersCount()\"\r\n [hasFilters]=\"hasFilters()\"\r\n [selection]=\"selection()\"\r\n [isAllSelected]=\"isAllSelected()\"\r\n [(searchText)]=\"searchText\"\r\n [itemsLength]=\"items().length\"\r\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\r\n [searchedItemsLength]=\"searchedItems().length\"\r\n (cleared)=\"clear()\"\r\n (applied)=\"apply()\"\r\n (allSelected)=\"selectAll()\"\r\n (allUnselected)=\"unselectAll()\"\r\n (loadedMore)=\"loadMore()\">\r\n <div\r\n #scrollElement\r\n class=\"
|
|
14355
|
-
}], ctorParameters: () => [], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], showFiltersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFiltersCount", required: false }] }], syncUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncUrl", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], onApply: [{ type: i0.Output, args: ["onApply"] }], onClear: [{ type: i0.Output, args: ["onClear"] }], scrollElement: [{ type: i0.ViewChild, args: ["scrollElement", { isSignal: true }] }], searchText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchText", required: false }] }, { type: i0.Output, args: ["searchTextChange"] }] } });
|
|
15034
|
+
}, template: "@if (aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <triangle-alert-icon class=\"mr-1\" />\r\n The aggregation component no longer supports tree aggregations. Please use\r\n the <AggregationTree /> component instead.\r\n </div>\r\n}\r\n<aggregation-panel\r\n [id]=\"id()\"\r\n [collapsible]=\"collapsible()\"\r\n [collapsed]=\"collapsed()\"\r\n [isEmpty]=\"isEmpty()\"\r\n [isDate]=\"isDate()\"\r\n [aggregation]=\"aggregation()\"\r\n [showFiltersCount]=\"showFiltersCount()\"\r\n [filtersCount]=\"filtersCount()\"\r\n [hasFilters]=\"hasFilters()\"\r\n [selection]=\"selection()\"\r\n [isAllSelected]=\"isAllSelected()\"\r\n [(searchText)]=\"searchText\"\r\n [itemsLength]=\"items().length\"\r\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\r\n [searchedItemsLength]=\"searchedItems().length\"\r\n (cleared)=\"clear()\"\r\n (applied)=\"apply()\"\r\n (allSelected)=\"selectAll()\"\r\n (allUnselected)=\"unselectAll()\"\r\n (loadedMore)=\"loadMore()\">\r\n <div\r\n #scrollElement\r\n class=\"max-h-(--scroll-height,20rem) w-full scrollbar-thin overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"listbox\"\r\n aria-multiselectable=\"true\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div\r\n class=\"absolute w-full\"\r\n [style.transform]=\"'translateY(' + vItem.start + 'px)'\"\r\n role=\"option\"\r\n [attr.aria-selected]=\"item.$selected ?? false\"\r\n [attr.aria-disabled]=\"item.count === 0 ? 'true' : null\"\r\n [attr.disabled]=\"item.count === 0 ? 'disabled' : null\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"listItemTpl\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: item,\r\n field: aggregation()?.column\r\n }\">\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n</aggregation-panel>\r\n\r\n<ng-template #listItemTpl let-item let-field=\"field\">\r\n <a\r\n [attr.aria-label]=\"listItemName(item) | syslang\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n item.count === 0 && 'disabled pointer-events-none'\r\n )\r\n \"\r\n (click)=\"selectListItem(item, $event)\">\r\n @if (showCheckbox()) {\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ item.value }}\"\r\n [attr.disabled]=\"item.count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"item.count === 0\"\r\n (keydown.enter)=\"selectListItem(item, $event)\"\r\n [checked]=\"item.$selected\" />\r\n }\r\n @if (resolvedItemTemplate(); as tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: item,\r\n field: field,\r\n selected: item.$selected ?? false,\r\n count: item.count,\r\n name: (listItemName(item) | syslang) ?? '',\r\n searchText: searchText()\r\n }\" />\r\n } @else {\r\n @let icon = item.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"self-center justify-self-center\" />\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isListItemFiltered(item, field)\r\n ? 'filters.removeFilter'\r\n : 'filters.addFilter'\r\n ) | transloco) +\r\n ': ' +\r\n (listItemName(item) | syslang)\r\n : (listItemName(item) | syslang)\r\n \"\r\n (click)=\"onListItemTextClick(item, $event)\">\r\n @for (\r\n chunk of (listItemName(item) | syslang) ?? \"\"\r\n | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && item.count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n item.count\r\n }}</span>\r\n }\r\n }\r\n </a>\r\n</ng-template>\r\n", styles: ["div[role=option]{display:block;-webkit-user-select:none;user-select:none}div[role=option] a{padding-left:var(--agg-tree-indent, .5rem);line-height:var(--agg-item-height, inherit)}\n"] }]
|
|
15035
|
+
}], ctorParameters: () => [], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], showFiltersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFiltersCount", required: false }] }], showCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheckbox", required: false }] }], syncUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncUrl", required: false }] }], controlledQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], controlledAggregation: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregation", required: false }] }], controlledSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], onApply: [{ type: i0.Output, args: ["onApply"] }], onClear: [{ type: i0.Output, args: ["onClear"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], filtersChange: [{ type: i0.Output, args: ["filtersChange"] }], scrollElement: [{ type: i0.ViewChild, args: ["scrollElement", { isSignal: true }] }], itemDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AggregationItemDirective), { isSignal: true }] }], itemTemplateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemTemplate", required: false }] }], searchText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchText", required: false }] }, { type: i0.Output, args: ["searchTextChange"] }] } });
|
|
14356
15036
|
|
|
14357
15037
|
/**
|
|
14358
15038
|
* The `AggregationComponent` is responsible for rendering the appropriate aggregation component based on the type of aggregation (date, tree, or list). It uses the `AggregationsService` to retrieve the aggregation data and determines which component to display based on the column type and whether it is a tree structure. The component also provides inputs for configuring its behavior, such as collapsibility, searchability, and showing filter counts.
|
|
@@ -14377,6 +15057,12 @@ class AggregationComponent {
|
|
|
14377
15057
|
aggregationDate = viewChild(AggregationDateComponent, ...(ngDevMode ? [{ debugName: "aggregationDate" }] : []));
|
|
14378
15058
|
aggregationTree = viewChild(AggregationTreeComponent, ...(ngDevMode ? [{ debugName: "aggregationTree" }] : []));
|
|
14379
15059
|
aggregationList = viewChild(AggregationListComponent, ...(ngDevMode ? [{ debugName: "aggregationList" }] : []));
|
|
15060
|
+
/**
|
|
15061
|
+
* Host-projected `<ng-template aggregationItem>`, relayed to the active list/tree
|
|
15062
|
+
* sub-component (the wrapper has no `<ng-content>`, so the sub-component's own
|
|
15063
|
+
* `contentChild` cannot see it). See {@link AggregationItemDirective}.
|
|
15064
|
+
*/
|
|
15065
|
+
itemDirective = contentChild(AggregationItemDirective, ...(ngDevMode ? [{ debugName: "itemDirective" }] : []));
|
|
14380
15066
|
class = input("", ...(ngDevMode ? [{ debugName: "class" }] : []));
|
|
14381
15067
|
/**
|
|
14382
15068
|
* The name of the <details> element. When you provide the same id, the component work as an accordion
|
|
@@ -14420,6 +15106,13 @@ class AggregationComponent {
|
|
|
14420
15106
|
* "Undefined" and not "false" because this input overrides the custom json settings
|
|
14421
15107
|
*/
|
|
14422
15108
|
searchable = input(undefined, ...(ngDevMode ? [{ debugName: "searchable" }] : []));
|
|
15109
|
+
/**
|
|
15110
|
+
* Whether to render the per-item checkbox (list/tree). When `false`, the checkbox
|
|
15111
|
+
* is hidden but selection still toggles on row click. Ignored by the date variant.
|
|
15112
|
+
*
|
|
15113
|
+
* @default true
|
|
15114
|
+
*/
|
|
15115
|
+
showCheckbox = input(true, ...(ngDevMode ? [{ debugName: "showCheckbox", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
14423
15116
|
expandedLevel = input(undefined, ...(ngDevMode ? [{ debugName: "expandedLevel", transform: numberAttribute }] : [{ transform: numberAttribute }]));
|
|
14424
15117
|
/**
|
|
14425
15118
|
* Controls whether applying or clearing a filter synchronizes the URL.
|
|
@@ -14431,12 +15124,64 @@ class AggregationComponent {
|
|
|
14431
15124
|
* @default true
|
|
14432
15125
|
*/
|
|
14433
15126
|
syncUrl = input(true, ...(ngDevMode ? [{ debugName: "syncUrl", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
15127
|
+
/**
|
|
15128
|
+
* Controlled query. When set, the aggregation's own operations (load-more,
|
|
15129
|
+
* search-within-facet, tree-expand) run against this query instead of reading
|
|
15130
|
+
* the shared `QueryParamsStore`. Use to target a query not tied to the current
|
|
15131
|
+
* app, without mutating the shared store or the URL.
|
|
15132
|
+
*
|
|
15133
|
+
* @default undefined — read `QueryParamsStore.getQuery()` (current behavior)
|
|
15134
|
+
*/
|
|
15135
|
+
controlledQuery = input(undefined, ...(ngDevMode ? [{ debugName: "controlledQuery", alias: "query" }] : [{ alias: "query" }]));
|
|
15136
|
+
/**
|
|
15137
|
+
* Controlled data. When set, the component renders this aggregation object
|
|
15138
|
+
* (name/column/items) instead of reading it from the shared `AggregationsStore`.
|
|
15139
|
+
*
|
|
15140
|
+
* @default undefined — read `AggregationsStore` (current behavior)
|
|
15141
|
+
*/
|
|
15142
|
+
controlledAggregation = input(undefined, ...(ngDevMode ? [{ debugName: "controlledAggregation", alias: "aggregation" }] : [{ alias: "aggregation" }]));
|
|
15143
|
+
/**
|
|
15144
|
+
* Controlled selection. When set, item `$selected` state is derived from this
|
|
15145
|
+
* value (list: item values; tree: `"/A/B/*"` paths; date: the range) instead
|
|
15146
|
+
* of the shared `QueryParamsStore` filters.
|
|
15147
|
+
*
|
|
15148
|
+
* @default undefined — derive from `QueryParamsStore` filters (current behavior)
|
|
15149
|
+
*/
|
|
15150
|
+
controlledSelection = input(undefined, ...(ngDevMode ? [{ debugName: "controlledSelection", alias: "selection" }] : [{ alias: "selection" }]));
|
|
15151
|
+
/**
|
|
15152
|
+
* Which sub-component renders the aggregation.
|
|
15153
|
+
*
|
|
15154
|
+
* `"auto"` (default) keeps the current heuristic: a date column of the app
|
|
15155
|
+
* (`AppStore.isDateColumn`) renders `<AggregationDate>`, a tree aggregation
|
|
15156
|
+
* renders `<AggregationTree>`, anything else `<AggregationList>`. Set an
|
|
15157
|
+
* explicit value in controlled mode when the heuristic cannot apply — e.g. a
|
|
15158
|
+
* date facet of a query not attached to the current app, whose column is
|
|
15159
|
+
* unknown to the app's column map.
|
|
15160
|
+
*
|
|
15161
|
+
* @default "auto"
|
|
15162
|
+
*/
|
|
15163
|
+
type = input("auto", ...(ngDevMode ? [{ debugName: "type" }] : []));
|
|
14434
15164
|
/**
|
|
14435
15165
|
* An output event that emits an array of selected `AggregationItem` objects when the user selects items in the aggregation component. This allows parent components to listen for selection changes and react accordingly, such as applying filters or updating the UI based on the selected items.
|
|
14436
15166
|
*/
|
|
14437
15167
|
onSelect = output();
|
|
14438
15168
|
onApply = output();
|
|
14439
15169
|
onClear = output();
|
|
15170
|
+
/**
|
|
15171
|
+
* Emits the new selection when the user applies or clears filters in
|
|
15172
|
+
* controlled mode (list: item values; tree: `"/A/B/*"` paths; date: option or
|
|
15173
|
+
* range). The counterpart of the `[selection]` input — emitted before
|
|
15174
|
+
* `filtersChange` and `onApply`/`onClear`. In controlled mode this is the only
|
|
15175
|
+
* way changes surface: nothing is written to the shared stores or the URL.
|
|
15176
|
+
*/
|
|
15177
|
+
selectionChange = output();
|
|
15178
|
+
/**
|
|
15179
|
+
* Emits the applied selection as a structured `Filter` (the exact filter the
|
|
15180
|
+
* default mode would write to the store), ready for `fetchQuery({ filters })`.
|
|
15181
|
+
* `undefined` on clear. Controlled mode only — emitted after `selectionChange`,
|
|
15182
|
+
* before `onApply`/`onClear`.
|
|
15183
|
+
*/
|
|
15184
|
+
filtersChange = output();
|
|
14440
15185
|
/**
|
|
14441
15186
|
* A computed property that retrieves the items to be displayed in the aggregation component. It checks for the presence of date, tree, or list aggregation components and returns their respective items. If none of these components are present, it returns an empty array. This allows the component to dynamically display the appropriate items based on the type of aggregation being rendered.
|
|
14442
15187
|
*
|
|
@@ -14454,6 +15199,10 @@ class AggregationComponent {
|
|
|
14454
15199
|
selectedItems = computed(() => this.items().filter(item => item.$selected), ...(ngDevMode ? [{ debugName: "selectedItems" }] : []));
|
|
14455
15200
|
/* aggregation */
|
|
14456
15201
|
aggregation = computed(() => {
|
|
15202
|
+
// controlled mode: the input is the data source — the store is not read
|
|
15203
|
+
const controlled = this.controlledAggregation();
|
|
15204
|
+
if (controlled)
|
|
15205
|
+
return controlled;
|
|
14457
15206
|
// when the aggegation store updates, we need to check if the aggregation is still valid
|
|
14458
15207
|
getState(this.aggregationsStore);
|
|
14459
15208
|
const agg = this.aggregationsService.getAggregation(this.name(), this.column());
|
|
@@ -14463,6 +15212,20 @@ class AggregationComponent {
|
|
|
14463
15212
|
}
|
|
14464
15213
|
return agg;
|
|
14465
15214
|
}, ...(ngDevMode ? [{ debugName: "aggregation" }] : []));
|
|
15215
|
+
/**
|
|
15216
|
+
* The sub-component to render: the explicit `type` input, or the current
|
|
15217
|
+
* date/tree/list heuristic when `type` is `"auto"`.
|
|
15218
|
+
*/
|
|
15219
|
+
resolvedType = computed(() => {
|
|
15220
|
+
const type = this.type();
|
|
15221
|
+
if (type !== "auto")
|
|
15222
|
+
return type;
|
|
15223
|
+
if (this.isDate(this.aggregation()?.column))
|
|
15224
|
+
return "date";
|
|
15225
|
+
if (this.aggregation()?.isTree)
|
|
15226
|
+
return "tree";
|
|
15227
|
+
return "list";
|
|
15228
|
+
}, ...(ngDevMode ? [{ debugName: "resolvedType" }] : []));
|
|
14466
15229
|
/**
|
|
14467
15230
|
* Checks if the specified column is a date filter.
|
|
14468
15231
|
*
|
|
@@ -14476,8 +15239,9 @@ class AggregationComponent {
|
|
|
14476
15239
|
return this.appStore.isDateColumn(column);
|
|
14477
15240
|
}
|
|
14478
15241
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14479
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AggregationComponent, isStandalone: true, selector: "Aggregation, aggregation", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, showFiltersCount: { classPropertyName: "showFiltersCount", publicName: "showFiltersCount", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, expandedLevel: { classPropertyName: "expandedLevel", publicName: "expandedLevel", isSignal: true, isRequired: false, transformFunction: null }, syncUrl: { classPropertyName: "syncUrl", publicName: "syncUrl", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", onApply: "onApply", onClear: "onClear" }, host: { properties: { "class": "cn(\"block h-[inherit] max-h-[inherit]\",class())" } }, viewQueries: [{ propertyName: "aggregationDate", first: true, predicate: AggregationDateComponent, descendants: true, isSignal: true }, { propertyName: "aggregationTree", first: true, predicate: AggregationTreeComponent, descendants: true, isSignal: true }, { propertyName: "aggregationList", first: true, predicate: AggregationListComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
14480
|
-
@
|
|
15242
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AggregationComponent, isStandalone: true, selector: "Aggregation, aggregation", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, showFiltersCount: { classPropertyName: "showFiltersCount", publicName: "showFiltersCount", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, expandedLevel: { classPropertyName: "expandedLevel", publicName: "expandedLevel", isSignal: true, isRequired: false, transformFunction: null }, syncUrl: { classPropertyName: "syncUrl", publicName: "syncUrl", isSignal: true, isRequired: false, transformFunction: null }, controlledQuery: { classPropertyName: "controlledQuery", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, controlledAggregation: { classPropertyName: "controlledAggregation", publicName: "aggregation", isSignal: true, isRequired: false, transformFunction: null }, controlledSelection: { classPropertyName: "controlledSelection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", onApply: "onApply", onClear: "onClear", selectionChange: "selectionChange", filtersChange: "filtersChange" }, host: { properties: { "class": "cn(\"block h-[inherit] max-h-[inherit]\",class())" } }, queries: [{ propertyName: "itemDirective", first: true, predicate: AggregationItemDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "aggregationDate", first: true, predicate: AggregationDateComponent, descendants: true, isSignal: true }, { propertyName: "aggregationTree", first: true, predicate: AggregationTreeComponent, descendants: true, isSignal: true }, { propertyName: "aggregationList", first: true, predicate: AggregationListComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
15243
|
+
@switch (resolvedType()) {
|
|
15244
|
+
@case ("date") {
|
|
14481
15245
|
<AggregationDate
|
|
14482
15246
|
[id]="id()"
|
|
14483
15247
|
[name]="name()"
|
|
@@ -14487,11 +15251,17 @@ class AggregationComponent {
|
|
|
14487
15251
|
[showFiltersCount]="showFiltersCount()"
|
|
14488
15252
|
[searchable]="searchable()"
|
|
14489
15253
|
[syncUrl]="syncUrl()"
|
|
15254
|
+
[query]="controlledQuery()"
|
|
15255
|
+
[aggregation]="controlledAggregation()"
|
|
15256
|
+
[selection]="controlledSelection()"
|
|
14490
15257
|
[title]="{ label: 'Date', icon: 'far fa-calendar-day' }"
|
|
14491
15258
|
(onApply)="onApply.emit($event)"
|
|
14492
15259
|
(onClear)="onClear.emit($event)"
|
|
15260
|
+
(selectionChange)="selectionChange.emit($event)"
|
|
15261
|
+
(filtersChange)="filtersChange.emit($event)"
|
|
14493
15262
|
/>
|
|
14494
|
-
|
|
15263
|
+
}
|
|
15264
|
+
@case ("tree") {
|
|
14495
15265
|
<AggregationTree
|
|
14496
15266
|
[id]="id()"
|
|
14497
15267
|
[name]="name()"
|
|
@@ -14501,13 +15271,20 @@ class AggregationComponent {
|
|
|
14501
15271
|
[showFiltersCount]="showFiltersCount()"
|
|
14502
15272
|
[searchable]="searchable()"
|
|
14503
15273
|
[syncUrl]="syncUrl()"
|
|
15274
|
+
[query]="controlledQuery()"
|
|
15275
|
+
[aggregation]="controlledAggregation()"
|
|
15276
|
+
[selection]="controlledSelection()"
|
|
15277
|
+
[itemTemplate]="itemDirective()?.template"
|
|
15278
|
+
[showCheckbox]="showCheckbox()"
|
|
14504
15279
|
[expandedLevel]="expandedLevel()"
|
|
14505
15280
|
(onSelect)="onSelect.emit($event)"
|
|
14506
15281
|
(onApply)="onApply.emit($event)"
|
|
14507
15282
|
(onClear)="onClear.emit($event)"
|
|
15283
|
+
(selectionChange)="selectionChange.emit($event)"
|
|
15284
|
+
(filtersChange)="filtersChange.emit($event)"
|
|
14508
15285
|
/>
|
|
14509
|
-
|
|
14510
|
-
|
|
15286
|
+
}
|
|
15287
|
+
@default {
|
|
14511
15288
|
<AggregationList
|
|
14512
15289
|
[id]="id()"
|
|
14513
15290
|
[name]="name()"
|
|
@@ -14517,12 +15294,20 @@ class AggregationComponent {
|
|
|
14517
15294
|
[showFiltersCount]="showFiltersCount()"
|
|
14518
15295
|
[searchable]="searchable()"
|
|
14519
15296
|
[syncUrl]="syncUrl()"
|
|
15297
|
+
[query]="controlledQuery()"
|
|
15298
|
+
[aggregation]="controlledAggregation()"
|
|
15299
|
+
[selection]="controlledSelection()"
|
|
15300
|
+
[itemTemplate]="itemDirective()?.template"
|
|
15301
|
+
[showCheckbox]="showCheckbox()"
|
|
14520
15302
|
(onSelect)="onSelect.emit($event)"
|
|
14521
15303
|
(onApply)="onApply.emit($event)"
|
|
14522
15304
|
(onClear)="onClear.emit($event)"
|
|
15305
|
+
(selectionChange)="selectionChange.emit($event)"
|
|
15306
|
+
(filtersChange)="filtersChange.emit($event)"
|
|
14523
15307
|
/>
|
|
14524
|
-
|
|
14525
|
-
|
|
15308
|
+
}
|
|
15309
|
+
}
|
|
15310
|
+
`, isInline: true, dependencies: [{ kind: "component", type: AggregationListComponent, selector: "AggregationList, aggregation-list, aggregationlist", inputs: ["class", "id", "name", "column", "collapsible", "collapsed", "searchable", "showFiltersCount", "showCheckbox", "syncUrl", "query", "aggregation", "selection", "itemTemplate", "searchText"], outputs: ["onSelect", "onApply", "onClear", "selectionChange", "filtersChange", "searchTextChange"] }, { kind: "component", type: AggregationTreeComponent, selector: "AggregationTree, aggregation-tree, aggregationtree", inputs: ["class", "id", "name", "column", "collapsible", "collapsed", "searchable", "showFiltersCount", "showCheckbox", "expandedLevel", "syncUrl", "query", "aggregation", "selection", "itemTemplate", "searchText"], outputs: ["onSelect", "onApply", "onClear", "selectionChange", "filtersChange", "searchTextChange"] }, { kind: "component", type: AggregationDateComponent, selector: "aggregation-date, AggregationDate, aggregationdate", inputs: ["name", "column", "id", "collapsible", "collapsed", "searchable", "showFiltersCount", "syncUrl", "query", "aggregation", "selection", "title", "displayEmptyDistributionIntervals", "searchText"], outputs: ["onSelect", "onApply", "onClear", "selectionChange", "filtersChange", "searchTextChange"] }] });
|
|
14526
15311
|
}
|
|
14527
15312
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationComponent, decorators: [{
|
|
14528
15313
|
type: Component,
|
|
@@ -14530,7 +15315,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14530
15315
|
selector: "Aggregation, aggregation",
|
|
14531
15316
|
imports: [AggregationListComponent, AggregationTreeComponent, AggregationDateComponent],
|
|
14532
15317
|
template: `
|
|
14533
|
-
@
|
|
15318
|
+
@switch (resolvedType()) {
|
|
15319
|
+
@case ("date") {
|
|
14534
15320
|
<AggregationDate
|
|
14535
15321
|
[id]="id()"
|
|
14536
15322
|
[name]="name()"
|
|
@@ -14540,11 +15326,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14540
15326
|
[showFiltersCount]="showFiltersCount()"
|
|
14541
15327
|
[searchable]="searchable()"
|
|
14542
15328
|
[syncUrl]="syncUrl()"
|
|
15329
|
+
[query]="controlledQuery()"
|
|
15330
|
+
[aggregation]="controlledAggregation()"
|
|
15331
|
+
[selection]="controlledSelection()"
|
|
14543
15332
|
[title]="{ label: 'Date', icon: 'far fa-calendar-day' }"
|
|
14544
15333
|
(onApply)="onApply.emit($event)"
|
|
14545
15334
|
(onClear)="onClear.emit($event)"
|
|
15335
|
+
(selectionChange)="selectionChange.emit($event)"
|
|
15336
|
+
(filtersChange)="filtersChange.emit($event)"
|
|
14546
15337
|
/>
|
|
14547
|
-
|
|
15338
|
+
}
|
|
15339
|
+
@case ("tree") {
|
|
14548
15340
|
<AggregationTree
|
|
14549
15341
|
[id]="id()"
|
|
14550
15342
|
[name]="name()"
|
|
@@ -14554,13 +15346,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14554
15346
|
[showFiltersCount]="showFiltersCount()"
|
|
14555
15347
|
[searchable]="searchable()"
|
|
14556
15348
|
[syncUrl]="syncUrl()"
|
|
15349
|
+
[query]="controlledQuery()"
|
|
15350
|
+
[aggregation]="controlledAggregation()"
|
|
15351
|
+
[selection]="controlledSelection()"
|
|
15352
|
+
[itemTemplate]="itemDirective()?.template"
|
|
15353
|
+
[showCheckbox]="showCheckbox()"
|
|
14557
15354
|
[expandedLevel]="expandedLevel()"
|
|
14558
15355
|
(onSelect)="onSelect.emit($event)"
|
|
14559
15356
|
(onApply)="onApply.emit($event)"
|
|
14560
15357
|
(onClear)="onClear.emit($event)"
|
|
15358
|
+
(selectionChange)="selectionChange.emit($event)"
|
|
15359
|
+
(filtersChange)="filtersChange.emit($event)"
|
|
14561
15360
|
/>
|
|
14562
|
-
|
|
14563
|
-
|
|
15361
|
+
}
|
|
15362
|
+
@default {
|
|
14564
15363
|
<AggregationList
|
|
14565
15364
|
[id]="id()"
|
|
14566
15365
|
[name]="name()"
|
|
@@ -14570,17 +15369,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14570
15369
|
[showFiltersCount]="showFiltersCount()"
|
|
14571
15370
|
[searchable]="searchable()"
|
|
14572
15371
|
[syncUrl]="syncUrl()"
|
|
15372
|
+
[query]="controlledQuery()"
|
|
15373
|
+
[aggregation]="controlledAggregation()"
|
|
15374
|
+
[selection]="controlledSelection()"
|
|
15375
|
+
[itemTemplate]="itemDirective()?.template"
|
|
15376
|
+
[showCheckbox]="showCheckbox()"
|
|
14573
15377
|
(onSelect)="onSelect.emit($event)"
|
|
14574
15378
|
(onApply)="onApply.emit($event)"
|
|
14575
15379
|
(onClear)="onClear.emit($event)"
|
|
15380
|
+
(selectionChange)="selectionChange.emit($event)"
|
|
15381
|
+
(filtersChange)="filtersChange.emit($event)"
|
|
14576
15382
|
/>
|
|
14577
|
-
|
|
15383
|
+
}
|
|
15384
|
+
}
|
|
14578
15385
|
`,
|
|
14579
15386
|
host: {
|
|
14580
15387
|
"[class]": 'cn("block h-[inherit] max-h-[inherit]",class())'
|
|
14581
15388
|
}
|
|
14582
15389
|
}]
|
|
14583
|
-
}], propDecorators: { aggregationDate: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AggregationDateComponent), { isSignal: true }] }], aggregationTree: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AggregationTreeComponent), { isSignal: true }] }], aggregationList: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AggregationListComponent), { isSignal: true }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], showFiltersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFiltersCount", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], expandedLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedLevel", required: false }] }], syncUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncUrl", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], onApply: [{ type: i0.Output, args: ["onApply"] }], onClear: [{ type: i0.Output, args: ["onClear"] }] } });
|
|
15390
|
+
}], propDecorators: { aggregationDate: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AggregationDateComponent), { isSignal: true }] }], aggregationTree: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AggregationTreeComponent), { isSignal: true }] }], aggregationList: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AggregationListComponent), { isSignal: true }] }], itemDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AggregationItemDirective), { isSignal: true }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], showFiltersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFiltersCount", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], showCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheckbox", required: false }] }], expandedLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedLevel", required: false }] }], syncUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncUrl", required: false }] }], controlledQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], controlledAggregation: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregation", required: false }] }], controlledSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], onApply: [{ type: i0.Output, args: ["onApply"] }], onClear: [{ type: i0.Output, args: ["onClear"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], filtersChange: [{ type: i0.Output, args: ["filtersChange"] }] } });
|
|
14584
15391
|
|
|
14585
15392
|
class AsideFiltersComponent {
|
|
14586
15393
|
cn = cn;
|
|
@@ -14614,7 +15421,7 @@ class AsideFiltersComponent {
|
|
|
14614
15421
|
showFiltersCount />
|
|
14615
15422
|
}
|
|
14616
15423
|
</div>
|
|
14617
|
-
`, isInline: true, dependencies: [{ kind: "component", type: AggregationComponent, selector: "Aggregation, aggregation", inputs: ["class", "id", "name", "column", "showFiltersCount", "collapsible", "collapsed", "searchable", "expandedLevel", "syncUrl"], outputs: ["onSelect", "onApply", "onClear"] }] });
|
|
15424
|
+
`, isInline: true, dependencies: [{ kind: "component", type: AggregationComponent, selector: "Aggregation, aggregation", inputs: ["class", "id", "name", "column", "showFiltersCount", "collapsible", "collapsed", "searchable", "showCheckbox", "expandedLevel", "syncUrl", "query", "aggregation", "selection", "type"], outputs: ["onSelect", "onApply", "onClear", "selectionChange", "filtersChange"] }] });
|
|
14618
15425
|
}
|
|
14619
15426
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AsideFiltersComponent, decorators: [{
|
|
14620
15427
|
type: Component,
|
|
@@ -14765,7 +15572,7 @@ class FilterButtonComponent {
|
|
|
14765
15572
|
}
|
|
14766
15573
|
</PopoverContent>
|
|
14767
15574
|
</Popover>
|
|
14768
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "component", type: PopoverComponent, selector: "popover, Popover", inputs: ["disabled", "closeOnScroll"], outputs: ["closed"] }, { kind: "directive", type: PopoverContentComponent, selector: "popover-content, PopoverContent, popovercontent", inputs: ["class", "position", "keepOpen", "offset", "strategy"], exportAs: ["popoverContent"] }, { kind: "component", type: AggregationComponent, selector: "Aggregation, aggregation", inputs: ["class", "id", "name", "column", "showFiltersCount", "collapsible", "collapsed", "searchable", "expandedLevel", "syncUrl"], outputs: ["onSelect", "onApply", "onClear"] }, { kind: "directive", type: BadgeComponent, selector: "badge, Badge", inputs: ["class", "variant", "scheme", "size", "aria-label"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass", "class"] }, { kind: "pipe", type: OperatorPipe, name: "operator" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
|
|
15575
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "component", type: PopoverComponent, selector: "popover, Popover", inputs: ["disabled", "closeOnScroll"], outputs: ["closed"] }, { kind: "directive", type: PopoverContentComponent, selector: "popover-content, PopoverContent, popovercontent", inputs: ["class", "position", "keepOpen", "offset", "strategy"], exportAs: ["popoverContent"] }, { kind: "component", type: AggregationComponent, selector: "Aggregation, aggregation", inputs: ["class", "id", "name", "column", "showFiltersCount", "collapsible", "collapsed", "searchable", "showCheckbox", "expandedLevel", "syncUrl", "query", "aggregation", "selection", "type"], outputs: ["onSelect", "onApply", "onClear", "selectionChange", "filtersChange"] }, { kind: "directive", type: BadgeComponent, selector: "badge, Badge", inputs: ["class", "variant", "scheme", "size", "aria-label"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass", "class"] }, { kind: "pipe", type: OperatorPipe, name: "operator" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
|
|
14769
15576
|
}
|
|
14770
15577
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FilterButtonComponent, decorators: [{
|
|
14771
15578
|
type: Component,
|
|
@@ -14945,7 +15752,7 @@ class MoreComponent {
|
|
|
14945
15752
|
[name]="filter.name"
|
|
14946
15753
|
[column]="filter.column" />
|
|
14947
15754
|
}
|
|
14948
|
-
`, isInline: true, styles: [":host{scrollbar-width:none}\n"], dependencies: [{ kind: "component", type: AggregationComponent, selector: "Aggregation, aggregation", inputs: ["class", "id", "name", "column", "showFiltersCount", "collapsible", "collapsed", "searchable", "expandedLevel", "syncUrl"], outputs: ["onSelect", "onApply", "onClear"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
15755
|
+
`, isInline: true, styles: [":host{scrollbar-width:none}\n"], dependencies: [{ kind: "component", type: AggregationComponent, selector: "Aggregation, aggregation", inputs: ["class", "id", "name", "column", "showFiltersCount", "collapsible", "collapsed", "searchable", "showCheckbox", "expandedLevel", "syncUrl", "query", "aggregation", "selection", "type"], outputs: ["onSelect", "onApply", "onClear", "selectionChange", "filtersChange"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
14949
15756
|
}
|
|
14950
15757
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: MoreComponent, decorators: [{
|
|
14951
15758
|
type: Component,
|
|
@@ -15276,7 +16083,7 @@ class FiltersBarComponent {
|
|
|
15276
16083
|
@if (currentBasket()) {
|
|
15277
16084
|
<button
|
|
15278
16085
|
variant="destructive"
|
|
15279
|
-
class="max-w-40
|
|
16086
|
+
class="max-w-40 h-9"
|
|
15280
16087
|
role="listitem"
|
|
15281
16088
|
[title]="'filters.clearBasket' | transloco"
|
|
15282
16089
|
(click)="clearBasket()"
|
|
@@ -15348,7 +16155,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
15348
16155
|
@if (currentBasket()) {
|
|
15349
16156
|
<button
|
|
15350
16157
|
variant="destructive"
|
|
15351
|
-
class="max-w-40
|
|
16158
|
+
class="max-w-40 h-9"
|
|
15352
16159
|
role="listitem"
|
|
15353
16160
|
[title]="'filters.clearBasket' | transloco"
|
|
15354
16161
|
(click)="clearBasket()"
|
|
@@ -16803,5 +17610,5 @@ const queryNameResolver = () => {
|
|
|
16803
17610
|
* Generated bundle index. Do not edit.
|
|
16804
17611
|
*/
|
|
16805
17612
|
|
|
16806
|
-
export { AGGREGATIONS_NAMES, AGGREGATIONS_NAMES_PRESET_DEFAULT, APP_FEATURES, AdvancedFiltersComponent, AdvancedSearch, AdvancedSearchComponent, AggregationComponent, AggregationDateComponent, AggregationDateRangeDialogComponent, AggregationListComponent, AggregationPanelComponent, AggregationTreeComponent, AggregationsService, AggregationsStore, Alert, AlertDialog, AlertsComponent, AppService, AppStore, ApplicationService, ApplicationStore, ArticleEntities, ArticleExtracts, ArticleLabels, ArticleSimilarDocuments, AsideFiltersComponent, AuditFeedbackType, AuditService, AuthGuard, AuthPageComponent, AutocompleteService, BOOKMARKS_CONFIG, BOOKMARKS_OPTIONS, BackdropComponent, BackdropService, BookmarkButtonComponent, BookmarksComponent, COLLECTIONS_CONFIG, COLLECTIONS_OPTIONS, COMPONENTS_FOR_DOCUMENT_TYPE, ChangePasswordComponent, ChildMarkerDirective, CollectionsComponent, CollectionsDialog, DRAWER_COMPONENT, DRAWER_STACK_MAX_COUNT, DateComponent, DeleteCollectionDialog, DidYouMeanComponent, DocumentLocatorComponent, DrawerAdvancedFiltersComponent, DrawerComponent, DrawerNavbarComponent, DrawerPreviewComponent, DrawerService, DrawerStackComponent, DrawerStackService, DropdownInputComponent, DropdownListComponent, ErrorComponent, ExportDialog, ExportService, FILTERS_BREAKPOINT, FILTER_DATE_ALLOW_CUSTOM_RANGE, FeatureFlagsDialogComponent, FeedbackDialogComponent, FileSizePipe, FilterButtonComponent, FiltersBarComponent, HIGHLIGHTS, HighlightWordPipe, InfinityScrollDirective, InlineWorker, JsonMethodPluginService, KeyboardNavigatorDirective, LabelService, LabelsEditDialog, LoadingComponent, MetadataComponent, MissingTermsComponent, MoreButtonComponent, MoreComponent, MultiSelectLabelsComponent, MultiSelectionToolbarComponent, NON_SEARCHABLE_COLUMNS, NON_SEARCHABLE_DEFAULTS, NavbarTabsComponent, NavigationService, NoResultComponent, OpenArticleOnCtrlEnterDirective, OperatorPipe, OverflowItemDirective, OverflowManagerDirective, OverflowStopDirective, OverrideUserDialogComponent, PREVIEW_CONFIG, PagerComponent, PreviewNavigator, PreviewService, PrincipalService, PrincipalStore, QueryParamsStore, QueryService, RECENT_SEARCHES_CONFIG, RECENT_SEARCHES_OPTIONS, ROUTE_COMPONENTS, RecentSearchesComponent, ResetUserSettingsDialogComponent, SAVED_SEARCHES_CONFIG, SAVED_SEARCHES_OPTIONS, SavedSearchDialog, SavedSearchesComponent, SavedSearchesService, SearchFeedbackComponent, SearchInputFooter, SearchService, SelectArticleDirective, SelectArticleOnClickDirective, SelectionHistoryService, SelectionService, SelectionStore, ShowBookmarkDirective, SidebarNavComponent, SignInComponent, SignedOutComponent, SortSelectorComponent, SourceComponent, SourceIconPipe, SponsoredResultsComponent, SyslangPipe, THEMES, TextChunkService, ThemeProviderDirective, ThemeSelectorComponent, ThemeStore, ThemeToggleComponent, TranslocoDateImpurePipe, UserProfileDialog, UserProfileFormComponent, UserProfileService, UserSettingsStore, applyThemeToNativeElement, auditInterceptorFn, authInterceptorFn, bodyInterceptorFn, bootstrapApp, buildQuery, debouncedSignal, errorInterceptorFn, getCurrentPath, getCurrentQueryName, getQueryNameFromRoute, injectRouteNavigation, processCssVars, queryNameResolver, signIn, themeColorNameToCssVariable, themeColorsToCssVariables, toastInterceptorFn, withAggregationsFeatures, withAlertsFeatures, withAppFeatures, withApplicationFeatures, withAssistantFeatures, withBasketsFeatures, withBookmarkFeatures, withBootstrapApp, withExtractsFeatures, withFetch, withMultiSelectionFeatures, withPrincipalFeatures, withQueryParamsFeatures, withRecentSearchesFeatures, withSavedSearchesFeatures, withSelectionFeatures, withThemeBodyHook, withThemes, withThemesFeatures, withUserSettingsFeatures };
|
|
17613
|
+
export { AGGREGATIONS_NAMES, AGGREGATIONS_NAMES_PRESET_DEFAULT, APP_FEATURES, AdvancedFiltersComponent, AdvancedSearch, AdvancedSearchComponent, AggregationComponent, AggregationDateComponent, AggregationDateRangeDialogComponent, AggregationItemDirective, AggregationListComponent, AggregationPanelComponent, AggregationTreeComponent, AggregationsService, AggregationsStore, Alert, AlertDialog, AlertsComponent, AppService, AppStore, ApplicationService, ApplicationStore, ArticleEntities, ArticleExtracts, ArticleLabels, ArticleSimilarDocuments, AsideFiltersComponent, AuditFeedbackType, AuditService, AuthGuard, AuthPageComponent, AutocompleteService, BOOKMARKS_CONFIG, BOOKMARKS_OPTIONS, BackdropComponent, BackdropService, BookmarkButtonComponent, BookmarksComponent, COLLECTIONS_CONFIG, COLLECTIONS_OPTIONS, COMPONENTS_FOR_DOCUMENT_TYPE, ChangePasswordComponent, ChildMarkerDirective, CollectionsComponent, CollectionsDialog, DRAWER_COMPONENT, DRAWER_STACK_MAX_COUNT, DateComponent, DeleteCollectionDialog, DidYouMeanComponent, DocumentLocatorComponent, DrawerAdvancedFiltersComponent, DrawerComponent, DrawerNavbarComponent, DrawerPreviewComponent, DrawerService, DrawerStackComponent, DrawerStackService, DropdownInputComponent, DropdownListComponent, ErrorComponent, ExportDialog, ExportService, FILTERS_BREAKPOINT, FILTER_DATE_ALLOW_CUSTOM_RANGE, FeatureFlagsDialogComponent, FeedbackDialogComponent, FileSizePipe, FilterButtonComponent, FiltersBarComponent, HIGHLIGHTS, HighlightWordPipe, InfinityScrollDirective, InlineWorker, JsonMethodPluginService, KeyboardNavigatorDirective, LabelService, LabelsEditDialog, LoadingComponent, MetadataComponent, MissingTermsComponent, MoreButtonComponent, MoreComponent, MultiSelectLabelsComponent, MultiSelectionToolbarComponent, NON_SEARCHABLE_COLUMNS, NON_SEARCHABLE_DEFAULTS, NavbarTabsComponent, NavigationService, NoResultComponent, OpenArticleOnCtrlEnterDirective, OperatorPipe, OverflowItemDirective, OverflowManagerDirective, OverflowStopDirective, OverrideUserDialogComponent, PREVIEW_CONFIG, PagerComponent, PreviewNavigator, PreviewService, PrincipalService, PrincipalStore, QueryParamsStore, QueryService, RECENT_SEARCHES_CONFIG, RECENT_SEARCHES_OPTIONS, ROUTE_COMPONENTS, RecentSearchesComponent, ResetUserSettingsDialogComponent, SAVED_SEARCHES_CONFIG, SAVED_SEARCHES_OPTIONS, SavedSearchDialog, SavedSearchesComponent, SavedSearchesService, SearchFeedbackComponent, SearchInputFooter, SearchService, SelectArticleDirective, SelectArticleOnClickDirective, SelectionHistoryService, SelectionService, SelectionStore, ShowBookmarkDirective, SidebarNavComponent, SignInComponent, SignedOutComponent, SortSelectorComponent, SourceComponent, SourceIconPipe, SponsoredResultsComponent, SyslangPipe, THEMES, TextChunkService, ThemeProviderDirective, ThemeSelectorComponent, ThemeStore, ThemeToggleComponent, TranslocoDateImpurePipe, UserProfileDialog, UserProfileFormComponent, UserProfileService, UserSettingsStore, applyThemeToNativeElement, auditInterceptorFn, authInterceptorFn, bodyInterceptorFn, bootstrapApp, buildQuery, debouncedSignal, errorInterceptorFn, getCurrentPath, getCurrentQueryName, getQueryNameFromRoute, injectRouteNavigation, legacyFilterToFilter, processCssVars, queryNameResolver, signIn, themeColorNameToCssVariable, themeColorsToCssVariables, toastInterceptorFn, withAggregationsFeatures, withAlertsFeatures, withAppFeatures, withApplicationFeatures, withAssistantFeatures, withBasketsFeatures, withBookmarkFeatures, withBootstrapApp, withExtractsFeatures, withFetch, withMultiSelectionFeatures, withPrincipalFeatures, withQueryParamsFeatures, withRecentSearchesFeatures, withSavedSearchesFeatures, withSelectionFeatures, withThemeBodyHook, withThemes, withThemesFeatures, withUserSettingsFeatures };
|
|
16807
17614
|
//# sourceMappingURL=sinequa-atomic-angular.mjs.map
|