@sinequa/atomic-angular 1.7.2 → 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 +885 -73
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/index.d.ts +570 -168
- 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);
|
|
@@ -6488,6 +6578,43 @@ function injectRouteNavigation(path, showCount) {
|
|
|
6488
6578
|
return { tabs, currentPath, searchText };
|
|
6489
6579
|
}
|
|
6490
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
|
+
|
|
6491
6618
|
/**
|
|
6492
6619
|
* Directive to mark a child element for special processing.
|
|
6493
6620
|
* This can be used to apply specific styles or behaviors to child elements.
|
|
@@ -8912,12 +9039,98 @@ function injectAggregationBase(refs) {
|
|
|
8912
9039
|
const aggregationsService = inject(AggregationsService);
|
|
8913
9040
|
const injector = inject(Injector);
|
|
8914
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" }] : []));
|
|
8915
9086
|
const debouncedSearchText = debouncedSignal(refs.searchText, 300);
|
|
8916
9087
|
const normalizedSearchText = computed(() => debouncedSearchText()
|
|
8917
9088
|
.normalize("NFD")
|
|
8918
9089
|
.replace(/[̀-ͯ]/g, ""), ...(ngDevMode ? [{ debugName: "normalizedSearchText" }] : []));
|
|
8919
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" }] : []));
|
|
8920
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
|
+
}
|
|
8921
9134
|
getState(aggregationsStore);
|
|
8922
9135
|
const { count = 0 } = queryParamsStore.getFilter({ field: refs.column() ?? undefined, name: refs.name() ?? undefined }) || {};
|
|
8923
9136
|
return count;
|
|
@@ -8940,7 +9153,7 @@ function injectAggregationBase(refs) {
|
|
|
8940
9153
|
.pipe(takeUntilDestroyed(destroyRef), switchMap(({ text, column }) => {
|
|
8941
9154
|
if (text === "" || column === null)
|
|
8942
9155
|
return of([]);
|
|
8943
|
-
const q =
|
|
9156
|
+
const q = resolvedQuery();
|
|
8944
9157
|
return from(withFetch(() => fetchSuggestField(normalizedSearchText(), [column || ""], q), injector));
|
|
8945
9158
|
}))
|
|
8946
9159
|
.subscribe((result) => suggests.set(result || []));
|
|
@@ -8959,7 +9172,9 @@ function injectAggregationBase(refs) {
|
|
|
8959
9172
|
function addCurrentFiltersToItems(aggregation) {
|
|
8960
9173
|
const currentItems = aggregation?.items || [];
|
|
8961
9174
|
if (!aggregation?.isTree && !aggregation?.isDistribution) {
|
|
8962
|
-
const currentFilters =
|
|
9175
|
+
const currentFilters = isControlled()
|
|
9176
|
+
? selectionAsFilter()
|
|
9177
|
+
: queryParamsStore.getFilter({ field: aggregation?.column, name: aggregation?.name });
|
|
8963
9178
|
if (currentFilters) {
|
|
8964
9179
|
if (Array.isArray(currentFilters.filters) && currentFilters.filters.length) {
|
|
8965
9180
|
currentFilters.filters.forEach((filter) => {
|
|
@@ -8998,34 +9213,45 @@ function injectAggregationBase(refs) {
|
|
|
8998
9213
|
}
|
|
8999
9214
|
return currentItems;
|
|
9000
9215
|
}
|
|
9001
|
-
|
|
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) {
|
|
9002
9223
|
const { name: aggName, column: field } = agg;
|
|
9003
|
-
const syncUrl = refs.syncUrl?.() ?? true;
|
|
9004
9224
|
if (appliedFilters.length > 1) {
|
|
9005
9225
|
const display = appliedFilters[0].display;
|
|
9006
9226
|
if (agg.isDistribution) {
|
|
9007
|
-
|
|
9227
|
+
return {
|
|
9008
9228
|
operator: "or",
|
|
9009
9229
|
filters: appliedFilters,
|
|
9010
9230
|
name: aggName,
|
|
9011
9231
|
field,
|
|
9012
9232
|
display,
|
|
9013
|
-
}
|
|
9014
|
-
}
|
|
9015
|
-
else {
|
|
9016
|
-
const values = appliedFilters.map((f) => f.value);
|
|
9017
|
-
queryParamsStore.updateFilter({
|
|
9018
|
-
operator: "in",
|
|
9019
|
-
name: aggName,
|
|
9020
|
-
field,
|
|
9021
|
-
values,
|
|
9022
|
-
display,
|
|
9023
|
-
filters: appliedFilters,
|
|
9024
|
-
}, undefined, { syncUrl });
|
|
9233
|
+
};
|
|
9025
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
|
+
};
|
|
9026
9244
|
}
|
|
9027
|
-
|
|
9028
|
-
|
|
9245
|
+
if (appliedFilters.length === 1) {
|
|
9246
|
+
return appliedFilters[0];
|
|
9247
|
+
}
|
|
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 });
|
|
9029
9255
|
}
|
|
9030
9256
|
else {
|
|
9031
9257
|
clearFn();
|
|
@@ -9038,6 +9264,14 @@ function injectAggregationBase(refs) {
|
|
|
9038
9264
|
aggregationsService,
|
|
9039
9265
|
injector,
|
|
9040
9266
|
destroyRef,
|
|
9267
|
+
isControlled,
|
|
9268
|
+
isQueryControlled,
|
|
9269
|
+
isDataControlled,
|
|
9270
|
+
isSelectionControlled,
|
|
9271
|
+
resolvedQuery,
|
|
9272
|
+
resolvedAggregationCount,
|
|
9273
|
+
controlledAggregation,
|
|
9274
|
+
selectionValues,
|
|
9041
9275
|
debouncedSearchText,
|
|
9042
9276
|
normalizedSearchText,
|
|
9043
9277
|
suggests,
|
|
@@ -9048,6 +9282,7 @@ function injectAggregationBase(refs) {
|
|
|
9048
9282
|
clearSearch,
|
|
9049
9283
|
selectItems,
|
|
9050
9284
|
addCurrentFiltersToItems,
|
|
9285
|
+
buildAppliedFilter,
|
|
9051
9286
|
applyFilters,
|
|
9052
9287
|
};
|
|
9053
9288
|
}
|
|
@@ -9095,12 +9330,56 @@ class AggregationDateComponent {
|
|
|
9095
9330
|
* @default true
|
|
9096
9331
|
*/
|
|
9097
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" }]));
|
|
9098
9360
|
title = input({ label: "Date", icon: "far fa-calendar-day" }, ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
9099
9361
|
displayEmptyDistributionIntervals = input(false, ...(ngDevMode ? [{ debugName: "displayEmptyDistributionIntervals" }] : []));
|
|
9100
9362
|
/* outputs */
|
|
9101
9363
|
onSelect = output();
|
|
9102
9364
|
onApply = output();
|
|
9103
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();
|
|
9104
9383
|
/* collapse state */
|
|
9105
9384
|
isCollapsed = linkedSignal(() => this.collapsed(), ...(ngDevMode ? [{ debugName: "isCollapsed" }] : []));
|
|
9106
9385
|
expanded = computed(() => (this.isCollapsed() ? null : ""), ...(ngDevMode ? [{ debugName: "expanded" }] : []));
|
|
@@ -9115,8 +9394,12 @@ class AggregationDateComponent {
|
|
|
9115
9394
|
searchInput: this.searchInput,
|
|
9116
9395
|
expanded: this.expanded,
|
|
9117
9396
|
syncUrl: this.syncUrl,
|
|
9397
|
+
query: this.controlledQuery,
|
|
9398
|
+
aggregation: this.controlledAggregation,
|
|
9399
|
+
selection: this.controlledSelection,
|
|
9118
9400
|
});
|
|
9119
9401
|
/* spread from base */
|
|
9402
|
+
isControlled = this.base.isControlled;
|
|
9120
9403
|
aggregationsService = this.base.aggregationsService;
|
|
9121
9404
|
queryParamsStore = this.base.queryParamsStore;
|
|
9122
9405
|
hasFilters = this.base.hasFilters;
|
|
@@ -9127,7 +9410,24 @@ class AggregationDateComponent {
|
|
|
9127
9410
|
/* state */
|
|
9128
9411
|
selection = signal(false, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
9129
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;
|
|
9130
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
|
+
}
|
|
9131
9431
|
const name = this.name();
|
|
9132
9432
|
if (name !== null) {
|
|
9133
9433
|
const agg = this.aggregationsService.processAggregation(name, this.column());
|
|
@@ -9163,28 +9463,75 @@ class AggregationDateComponent {
|
|
|
9163
9463
|
this.transloco.langChanges$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((lang) => {
|
|
9164
9464
|
this.lang.set(lang);
|
|
9165
9465
|
});
|
|
9166
|
-
// apply current date filter from queryParamsStore
|
|
9466
|
+
// apply current date filter from the selection input (controlled) or the queryParamsStore
|
|
9167
9467
|
effect(() => {
|
|
9168
9468
|
const agg = this.aggregation();
|
|
9169
|
-
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
|
+
}
|
|
9170
9478
|
this.updateForm(this.queryParamsStore.getFilter({
|
|
9171
9479
|
field: agg.column,
|
|
9172
9480
|
name: agg.name
|
|
9173
9481
|
}));
|
|
9174
9482
|
}
|
|
9483
|
+
finally {
|
|
9484
|
+
this.seedingForm = false;
|
|
9485
|
+
}
|
|
9175
9486
|
});
|
|
9176
9487
|
this.form.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((changes) => {
|
|
9177
9488
|
this.validSelection.set(!!changes.option &&
|
|
9178
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
|
+
}
|
|
9179
9506
|
});
|
|
9180
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
|
+
}
|
|
9181
9524
|
select() {
|
|
9182
9525
|
this.selection.set(true);
|
|
9183
9526
|
}
|
|
9184
9527
|
apply() {
|
|
9185
9528
|
try {
|
|
9186
|
-
|
|
9187
|
-
|
|
9529
|
+
if (this.isControlled()) {
|
|
9530
|
+
this.emitCurrentSelection();
|
|
9531
|
+
}
|
|
9532
|
+
else {
|
|
9533
|
+
this.queryParamsStore.updateFilter(this.getFormValueFilter(), undefined, { syncUrl: this.syncUrl() });
|
|
9534
|
+
}
|
|
9188
9535
|
this.selection.set(false);
|
|
9189
9536
|
this.onApply.emit();
|
|
9190
9537
|
}
|
|
@@ -9205,7 +9552,13 @@ class AggregationDateComponent {
|
|
|
9205
9552
|
});
|
|
9206
9553
|
const agg = this.aggregation();
|
|
9207
9554
|
if (agg && notify) {
|
|
9208
|
-
|
|
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
|
+
}
|
|
9209
9562
|
this.onClear.emit();
|
|
9210
9563
|
}
|
|
9211
9564
|
}
|
|
@@ -9223,12 +9576,22 @@ class AggregationDateComponent {
|
|
|
9223
9576
|
this.dateRangeDialog()?.open();
|
|
9224
9577
|
}
|
|
9225
9578
|
onRangeSelected(range) {
|
|
9226
|
-
console.log("Selected range:", range);
|
|
9227
9579
|
this.form.patchValue({
|
|
9228
9580
|
option: "custom-range",
|
|
9229
9581
|
customRange: { from: range.from, to: range.to }
|
|
9230
9582
|
});
|
|
9231
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
|
+
}
|
|
9232
9595
|
}
|
|
9233
9596
|
updateForm(filter) {
|
|
9234
9597
|
if (!filter) {
|
|
@@ -9268,6 +9631,90 @@ class AggregationDateComponent {
|
|
|
9268
9631
|
};
|
|
9269
9632
|
this.form.setValue(formValue);
|
|
9270
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
|
+
}
|
|
9271
9718
|
toEndOfDay(dateStr) {
|
|
9272
9719
|
return `${new Date(dateStr).toLocaleDateString("en-CA", options)} 23:59:59`;
|
|
9273
9720
|
}
|
|
@@ -9343,7 +9790,7 @@ class AggregationDateComponent {
|
|
|
9343
9790
|
throw new Error("filters.filterInvalid");
|
|
9344
9791
|
}
|
|
9345
9792
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationDateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9346
|
-
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" }] });
|
|
9347
9794
|
}
|
|
9348
9795
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationDateComponent, decorators: [{
|
|
9349
9796
|
type: Component,
|
|
@@ -9361,8 +9808,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
9361
9808
|
IconButtonComponent
|
|
9362
9809
|
], host: {
|
|
9363
9810
|
class: "@container"
|
|
9364
|
-
}, 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=\"
|
|
9365
|
-
}], 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"] }] } });
|
|
9366
9813
|
|
|
9367
9814
|
/**
|
|
9368
9815
|
* Component that allows users to select a date or a date range for filtering search results.
|
|
@@ -9371,7 +9818,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
9371
9818
|
*/
|
|
9372
9819
|
class DateComponent extends AggregationDateComponent {
|
|
9373
9820
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: DateComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
9374
|
-
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" }] });
|
|
9375
9822
|
}
|
|
9376
9823
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: DateComponent, decorators: [{
|
|
9377
9824
|
type: Component,
|
|
@@ -9388,7 +9835,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
9388
9835
|
FilterXIcon
|
|
9389
9836
|
], host: {
|
|
9390
9837
|
class: "@container"
|
|
9391
|
-
}, 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"] }]
|
|
9392
9839
|
}] });
|
|
9393
9840
|
|
|
9394
9841
|
class ArticleEntities {
|
|
@@ -13721,6 +14168,14 @@ class AggregationTreeComponent {
|
|
|
13721
14168
|
collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : []));
|
|
13722
14169
|
searchable = input(undefined, ...(ngDevMode ? [{ debugName: "searchable" }] : []));
|
|
13723
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 }]));
|
|
13724
14179
|
expandedLevel = input(undefined, ...(ngDevMode ? [{ debugName: "expandedLevel", transform: (v) => {
|
|
13725
14180
|
const n = numberAttribute(v);
|
|
13726
14181
|
return Number.isNaN(n) ? undefined : n;
|
|
@@ -13737,13 +14192,63 @@ class AggregationTreeComponent {
|
|
|
13737
14192
|
* @default true
|
|
13738
14193
|
*/
|
|
13739
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" }]));
|
|
13740
14219
|
/* outputs */
|
|
13741
14220
|
onSelect = output();
|
|
13742
14221
|
onApply = output();
|
|
13743
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();
|
|
13744
14238
|
/* view queries */
|
|
13745
14239
|
virtualItems = viewChildren("virtualItem", ...(ngDevMode ? [{ debugName: "virtualItems" }] : []));
|
|
13746
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" }] : []));
|
|
13747
14252
|
/* selection state */
|
|
13748
14253
|
selection = signal(false, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
13749
14254
|
isAllSelected = signal(false, ...(ngDevMode ? [{ debugName: "isAllSelected" }] : []));
|
|
@@ -13755,8 +14260,12 @@ class AggregationTreeComponent {
|
|
|
13755
14260
|
column: this.column,
|
|
13756
14261
|
searchText: this.searchText,
|
|
13757
14262
|
syncUrl: this.syncUrl,
|
|
14263
|
+
query: this.controlledQuery,
|
|
14264
|
+
aggregation: this.controlledAggregation,
|
|
14265
|
+
selection: this.controlledSelection,
|
|
13758
14266
|
});
|
|
13759
14267
|
/* spread from base */
|
|
14268
|
+
isControlled = this.base.isControlled;
|
|
13760
14269
|
aggregationsStore = this.base.aggregationsStore;
|
|
13761
14270
|
queryParamsStore = this.base.queryParamsStore;
|
|
13762
14271
|
appStore = this.base.appStore;
|
|
@@ -13783,6 +14292,21 @@ class AggregationTreeComponent {
|
|
|
13783
14292
|
#measureItems = effect(() => this.virtualItems().forEach((el) => this.virtualizer.measureElement(el.nativeElement)), ...(ngDevMode ? [{ debugName: "#measureItems" }] : []));
|
|
13784
14293
|
linkChildren = computed(() => this.appStore.general()?.features?.filterLinkChildren, ...(ngDevMode ? [{ debugName: "linkChildren" }] : []));
|
|
13785
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
|
+
}
|
|
13786
14310
|
getState(this.aggregationsStore);
|
|
13787
14311
|
const name = this.name();
|
|
13788
14312
|
const column = this.column();
|
|
@@ -13813,7 +14337,10 @@ class AggregationTreeComponent {
|
|
|
13813
14337
|
else if (agg?.items) {
|
|
13814
14338
|
res = this.addCurrentFiltersToItems();
|
|
13815
14339
|
}
|
|
13816
|
-
|
|
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}`);
|
|
13817
14344
|
const sessionAgg = JSON.parse(sessionAggItem || "[]");
|
|
13818
14345
|
return this.processAggregations(sessionAgg.length ? this.setSelected(res, sessionAgg) : res);
|
|
13819
14346
|
}, ...(ngDevMode ? [{ debugName: "items" }] : []));
|
|
@@ -13837,13 +14364,16 @@ class AggregationTreeComponent {
|
|
|
13837
14364
|
constructor() {
|
|
13838
14365
|
effect(() => {
|
|
13839
14366
|
getState(this.aggregationsStore);
|
|
14367
|
+
if (this.isControlled())
|
|
14368
|
+
return;
|
|
13840
14369
|
sessionStorage.removeItem(`agg-${this.column()}`);
|
|
13841
14370
|
});
|
|
13842
14371
|
effect(() => {
|
|
13843
14372
|
this.filters.set(this.getFilters());
|
|
13844
14373
|
});
|
|
13845
14374
|
this.destroyRef.onDestroy(() => {
|
|
13846
|
-
|
|
14375
|
+
if (!this.isControlled())
|
|
14376
|
+
sessionStorage.removeItem(`agg-${this.aggregation()?.column}`);
|
|
13847
14377
|
if (this.selection()) {
|
|
13848
14378
|
const unselect = (items) => {
|
|
13849
14379
|
items.forEach((item) => {
|
|
@@ -13860,7 +14390,13 @@ class AggregationTreeComponent {
|
|
|
13860
14390
|
clear() {
|
|
13861
14391
|
const agg = this.aggregation();
|
|
13862
14392
|
if (agg) {
|
|
13863
|
-
|
|
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
|
+
}
|
|
13864
14400
|
const unselect = (items) => {
|
|
13865
14401
|
items.forEach((item) => {
|
|
13866
14402
|
item.$selected = false;
|
|
@@ -13870,7 +14406,8 @@ class AggregationTreeComponent {
|
|
|
13870
14406
|
});
|
|
13871
14407
|
};
|
|
13872
14408
|
unselect(this.items());
|
|
13873
|
-
|
|
14409
|
+
if (!this.isControlled())
|
|
14410
|
+
sessionStorage.removeItem(`agg-${this.aggregation()?.column}`);
|
|
13874
14411
|
this.selection.set(false);
|
|
13875
14412
|
this.isAllSelected.set(false);
|
|
13876
14413
|
this.onClear.emit();
|
|
@@ -13891,21 +14428,57 @@ class AggregationTreeComponent {
|
|
|
13891
14428
|
}
|
|
13892
14429
|
}
|
|
13893
14430
|
apply(overrideFilters) {
|
|
13894
|
-
sessionStorage.setItem(`agg-${this.aggregation()?.column}`, JSON.stringify([...this.items()]));
|
|
13895
14431
|
const filters = overrideFilters || this.getFilters();
|
|
13896
14432
|
const agg = this.aggregation();
|
|
13897
|
-
|
|
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
|
+
}
|
|
13898
14442
|
this.searchText.set("");
|
|
13899
14443
|
this.selection.set(false);
|
|
13900
14444
|
this.onApply.emit();
|
|
13901
14445
|
}
|
|
13902
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
|
+
}
|
|
13903
14459
|
const q = this.queryParamsStore.getQuery();
|
|
13904
14460
|
this.aggregationsService.loadMore(q, this.aggregation()).subscribe((aggregation) => {
|
|
13905
14461
|
this.aggregationsStore.updateAggregation(aggregation);
|
|
13906
14462
|
});
|
|
13907
14463
|
}
|
|
13908
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
|
+
}
|
|
13909
14482
|
const q = this.queryParamsStore.getQuery();
|
|
13910
14483
|
delete q.filters;
|
|
13911
14484
|
const agg = await firstValueFrom(this.aggregationsService.open(q, this.aggregation(), node));
|
|
@@ -13917,7 +14490,9 @@ class AggregationTreeComponent {
|
|
|
13917
14490
|
this.onSelect.emit(selectedItems);
|
|
13918
14491
|
this.selection.set(selectedItems.length > 0 || this.hasFilters());
|
|
13919
14492
|
this.verifySelected();
|
|
13920
|
-
|
|
14493
|
+
if (!this.isControlled()) {
|
|
14494
|
+
sessionStorage.setItem(`agg-${this.aggregation()?.column}`, JSON.stringify([...this.items()]));
|
|
14495
|
+
}
|
|
13921
14496
|
}
|
|
13922
14497
|
/* item-level methods — called from the ng-template */
|
|
13923
14498
|
treeItemName(item) {
|
|
@@ -13925,6 +14500,10 @@ class AggregationTreeComponent {
|
|
|
13925
14500
|
return typeof value === "string" ? value : `${value}`;
|
|
13926
14501
|
}
|
|
13927
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
|
+
}
|
|
13928
14507
|
const filters = this.queryParamsStore.getFilter({ field: field ?? undefined, name: this.treeItemName(item) });
|
|
13929
14508
|
if (!filters)
|
|
13930
14509
|
return false;
|
|
@@ -13985,7 +14564,10 @@ class AggregationTreeComponent {
|
|
|
13985
14564
|
const notSelectedItems = this.getFlattenTreeItems()
|
|
13986
14565
|
.filter((item) => !item.$selected)
|
|
13987
14566
|
.map((item) => (item.$path ? `/${item.$path}/*` : ""));
|
|
13988
|
-
const
|
|
14567
|
+
const sel = this.controlledSelection();
|
|
14568
|
+
const currentFilters = this.isControlled()
|
|
14569
|
+
? (sel?.kind === "paths" ? sel.paths : [])
|
|
14570
|
+
: this.queryParamsStore.getFilter({ field, name })?.values || [];
|
|
13989
14571
|
if (currentFilters) {
|
|
13990
14572
|
const filteredCurrentFilters = currentFilters.filter((filter) => !notSelectedItems.includes(filter));
|
|
13991
14573
|
if (filteredCurrentFilters.length > 0) {
|
|
@@ -14111,7 +14693,7 @@ class AggregationTreeComponent {
|
|
|
14111
14693
|
return false;
|
|
14112
14694
|
}
|
|
14113
14695
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14114
|
-
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" }] });
|
|
14115
14697
|
}
|
|
14116
14698
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationTreeComponent, decorators: [{
|
|
14117
14699
|
type: Component,
|
|
@@ -14129,8 +14711,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14129
14711
|
AggregationPanelComponent,
|
|
14130
14712
|
], standalone: true, host: {
|
|
14131
14713
|
"[class]": 'cn("block h-[inherit] max-h-[inherit] w-[inherit]", class())',
|
|
14132
|
-
}, 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=\"
|
|
14133
|
-
}], 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"] }] } });
|
|
14134
14716
|
|
|
14135
14717
|
class AggregationListComponent {
|
|
14136
14718
|
cn = cn;
|
|
@@ -14143,6 +14725,14 @@ class AggregationListComponent {
|
|
|
14143
14725
|
collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : []));
|
|
14144
14726
|
searchable = input(undefined, ...(ngDevMode ? [{ debugName: "searchable" }] : []));
|
|
14145
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 }]));
|
|
14146
14736
|
/**
|
|
14147
14737
|
* When `false`, applying or clearing filters updates the store without
|
|
14148
14738
|
* synchronizing the URL (no browser history entry, not shareable/restorable).
|
|
@@ -14150,12 +14740,61 @@ class AggregationListComponent {
|
|
|
14150
14740
|
* @default true
|
|
14151
14741
|
*/
|
|
14152
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" }]));
|
|
14153
14766
|
/* outputs */
|
|
14154
14767
|
onSelect = output();
|
|
14155
14768
|
onApply = output();
|
|
14156
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();
|
|
14157
14785
|
/* view queries */
|
|
14158
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" }] : []));
|
|
14159
14798
|
/* selection state */
|
|
14160
14799
|
selection = signal(false, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
14161
14800
|
isAllSelected = signal(false, ...(ngDevMode ? [{ debugName: "isAllSelected" }] : []));
|
|
@@ -14167,8 +14806,12 @@ class AggregationListComponent {
|
|
|
14167
14806
|
column: this.column,
|
|
14168
14807
|
searchText: this.searchText,
|
|
14169
14808
|
syncUrl: this.syncUrl,
|
|
14809
|
+
query: this.controlledQuery,
|
|
14810
|
+
aggregation: this.controlledAggregation,
|
|
14811
|
+
selection: this.controlledSelection,
|
|
14170
14812
|
});
|
|
14171
14813
|
/* spread from base */
|
|
14814
|
+
isControlled = this.base.isControlled;
|
|
14172
14815
|
aggregationsStore = this.base.aggregationsStore;
|
|
14173
14816
|
queryParamsStore = this.base.queryParamsStore;
|
|
14174
14817
|
appStore = this.base.appStore;
|
|
@@ -14192,6 +14835,17 @@ class AggregationListComponent {
|
|
|
14192
14835
|
scrollElement: this.scrollElement(),
|
|
14193
14836
|
}));
|
|
14194
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
|
+
}
|
|
14195
14849
|
getState(this.aggregationsStore);
|
|
14196
14850
|
const name = this.name();
|
|
14197
14851
|
const column = this.column();
|
|
@@ -14253,7 +14907,13 @@ class AggregationListComponent {
|
|
|
14253
14907
|
clear() {
|
|
14254
14908
|
const agg = this.aggregation();
|
|
14255
14909
|
if (agg) {
|
|
14256
|
-
|
|
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
|
+
}
|
|
14257
14917
|
this.selection.set(false);
|
|
14258
14918
|
this.isAllSelected.set(false);
|
|
14259
14919
|
}
|
|
@@ -14278,12 +14938,34 @@ class AggregationListComponent {
|
|
|
14278
14938
|
const agg = this.aggregation();
|
|
14279
14939
|
if (!agg)
|
|
14280
14940
|
return;
|
|
14281
|
-
|
|
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
|
+
}
|
|
14282
14951
|
this.searchText.set("");
|
|
14283
14952
|
this.selection.set(false);
|
|
14284
14953
|
this.onApply.emit();
|
|
14285
14954
|
}
|
|
14286
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
|
+
}
|
|
14287
14969
|
const q = this.queryParamsStore.getQuery();
|
|
14288
14970
|
this.aggregationsService.loadMore(q, this.aggregation()).subscribe((aggregation) => {
|
|
14289
14971
|
this.aggregationsStore.updateAggregation(aggregation);
|
|
@@ -14301,6 +14983,9 @@ class AggregationListComponent {
|
|
|
14301
14983
|
return typeof value === "string" ? value : `${value}`;
|
|
14302
14984
|
}
|
|
14303
14985
|
isListItemFiltered(item, field) {
|
|
14986
|
+
if (this.isControlled()) {
|
|
14987
|
+
return (this.base.selectionValues() ?? []).some((value) => value === item.value);
|
|
14988
|
+
}
|
|
14304
14989
|
const filters = this.queryParamsStore.getFilter({ field: field ?? undefined, name: this.listItemName(item) });
|
|
14305
14990
|
if (!filters)
|
|
14306
14991
|
return false;
|
|
@@ -14330,7 +15015,7 @@ class AggregationListComponent {
|
|
|
14330
15015
|
return this.base.addCurrentFiltersToItems(this.aggregation());
|
|
14331
15016
|
}
|
|
14332
15017
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14333
|
-
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" }] });
|
|
14334
15019
|
}
|
|
14335
15020
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationListComponent, decorators: [{
|
|
14336
15021
|
type: Component,
|
|
@@ -14346,8 +15031,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14346
15031
|
AggregationPanelComponent,
|
|
14347
15032
|
], standalone: true, host: {
|
|
14348
15033
|
"[class]": 'cn("block h-[inherit] max-h-[inherit]", class())',
|
|
14349
|
-
}, 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=\"
|
|
14350
|
-
}], 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"] }] } });
|
|
14351
15036
|
|
|
14352
15037
|
/**
|
|
14353
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.
|
|
@@ -14372,6 +15057,12 @@ class AggregationComponent {
|
|
|
14372
15057
|
aggregationDate = viewChild(AggregationDateComponent, ...(ngDevMode ? [{ debugName: "aggregationDate" }] : []));
|
|
14373
15058
|
aggregationTree = viewChild(AggregationTreeComponent, ...(ngDevMode ? [{ debugName: "aggregationTree" }] : []));
|
|
14374
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" }] : []));
|
|
14375
15066
|
class = input("", ...(ngDevMode ? [{ debugName: "class" }] : []));
|
|
14376
15067
|
/**
|
|
14377
15068
|
* The name of the <details> element. When you provide the same id, the component work as an accordion
|
|
@@ -14415,6 +15106,13 @@ class AggregationComponent {
|
|
|
14415
15106
|
* "Undefined" and not "false" because this input overrides the custom json settings
|
|
14416
15107
|
*/
|
|
14417
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 }]));
|
|
14418
15116
|
expandedLevel = input(undefined, ...(ngDevMode ? [{ debugName: "expandedLevel", transform: numberAttribute }] : [{ transform: numberAttribute }]));
|
|
14419
15117
|
/**
|
|
14420
15118
|
* Controls whether applying or clearing a filter synchronizes the URL.
|
|
@@ -14426,12 +15124,64 @@ class AggregationComponent {
|
|
|
14426
15124
|
* @default true
|
|
14427
15125
|
*/
|
|
14428
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" }] : []));
|
|
14429
15164
|
/**
|
|
14430
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.
|
|
14431
15166
|
*/
|
|
14432
15167
|
onSelect = output();
|
|
14433
15168
|
onApply = output();
|
|
14434
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();
|
|
14435
15185
|
/**
|
|
14436
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.
|
|
14437
15187
|
*
|
|
@@ -14449,6 +15199,10 @@ class AggregationComponent {
|
|
|
14449
15199
|
selectedItems = computed(() => this.items().filter(item => item.$selected), ...(ngDevMode ? [{ debugName: "selectedItems" }] : []));
|
|
14450
15200
|
/* aggregation */
|
|
14451
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;
|
|
14452
15206
|
// when the aggegation store updates, we need to check if the aggregation is still valid
|
|
14453
15207
|
getState(this.aggregationsStore);
|
|
14454
15208
|
const agg = this.aggregationsService.getAggregation(this.name(), this.column());
|
|
@@ -14458,6 +15212,20 @@ class AggregationComponent {
|
|
|
14458
15212
|
}
|
|
14459
15213
|
return agg;
|
|
14460
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" }] : []));
|
|
14461
15229
|
/**
|
|
14462
15230
|
* Checks if the specified column is a date filter.
|
|
14463
15231
|
*
|
|
@@ -14471,8 +15239,9 @@ class AggregationComponent {
|
|
|
14471
15239
|
return this.appStore.isDateColumn(column);
|
|
14472
15240
|
}
|
|
14473
15241
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14474
|
-
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: `
|
|
14475
|
-
@
|
|
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") {
|
|
14476
15245
|
<AggregationDate
|
|
14477
15246
|
[id]="id()"
|
|
14478
15247
|
[name]="name()"
|
|
@@ -14482,11 +15251,17 @@ class AggregationComponent {
|
|
|
14482
15251
|
[showFiltersCount]="showFiltersCount()"
|
|
14483
15252
|
[searchable]="searchable()"
|
|
14484
15253
|
[syncUrl]="syncUrl()"
|
|
15254
|
+
[query]="controlledQuery()"
|
|
15255
|
+
[aggregation]="controlledAggregation()"
|
|
15256
|
+
[selection]="controlledSelection()"
|
|
14485
15257
|
[title]="{ label: 'Date', icon: 'far fa-calendar-day' }"
|
|
14486
15258
|
(onApply)="onApply.emit($event)"
|
|
14487
15259
|
(onClear)="onClear.emit($event)"
|
|
15260
|
+
(selectionChange)="selectionChange.emit($event)"
|
|
15261
|
+
(filtersChange)="filtersChange.emit($event)"
|
|
14488
15262
|
/>
|
|
14489
|
-
|
|
15263
|
+
}
|
|
15264
|
+
@case ("tree") {
|
|
14490
15265
|
<AggregationTree
|
|
14491
15266
|
[id]="id()"
|
|
14492
15267
|
[name]="name()"
|
|
@@ -14496,13 +15271,20 @@ class AggregationComponent {
|
|
|
14496
15271
|
[showFiltersCount]="showFiltersCount()"
|
|
14497
15272
|
[searchable]="searchable()"
|
|
14498
15273
|
[syncUrl]="syncUrl()"
|
|
15274
|
+
[query]="controlledQuery()"
|
|
15275
|
+
[aggregation]="controlledAggregation()"
|
|
15276
|
+
[selection]="controlledSelection()"
|
|
15277
|
+
[itemTemplate]="itemDirective()?.template"
|
|
15278
|
+
[showCheckbox]="showCheckbox()"
|
|
14499
15279
|
[expandedLevel]="expandedLevel()"
|
|
14500
15280
|
(onSelect)="onSelect.emit($event)"
|
|
14501
15281
|
(onApply)="onApply.emit($event)"
|
|
14502
15282
|
(onClear)="onClear.emit($event)"
|
|
15283
|
+
(selectionChange)="selectionChange.emit($event)"
|
|
15284
|
+
(filtersChange)="filtersChange.emit($event)"
|
|
14503
15285
|
/>
|
|
14504
|
-
|
|
14505
|
-
|
|
15286
|
+
}
|
|
15287
|
+
@default {
|
|
14506
15288
|
<AggregationList
|
|
14507
15289
|
[id]="id()"
|
|
14508
15290
|
[name]="name()"
|
|
@@ -14512,12 +15294,20 @@ class AggregationComponent {
|
|
|
14512
15294
|
[showFiltersCount]="showFiltersCount()"
|
|
14513
15295
|
[searchable]="searchable()"
|
|
14514
15296
|
[syncUrl]="syncUrl()"
|
|
15297
|
+
[query]="controlledQuery()"
|
|
15298
|
+
[aggregation]="controlledAggregation()"
|
|
15299
|
+
[selection]="controlledSelection()"
|
|
15300
|
+
[itemTemplate]="itemDirective()?.template"
|
|
15301
|
+
[showCheckbox]="showCheckbox()"
|
|
14515
15302
|
(onSelect)="onSelect.emit($event)"
|
|
14516
15303
|
(onApply)="onApply.emit($event)"
|
|
14517
15304
|
(onClear)="onClear.emit($event)"
|
|
15305
|
+
(selectionChange)="selectionChange.emit($event)"
|
|
15306
|
+
(filtersChange)="filtersChange.emit($event)"
|
|
14518
15307
|
/>
|
|
14519
|
-
|
|
14520
|
-
|
|
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"] }] });
|
|
14521
15311
|
}
|
|
14522
15312
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationComponent, decorators: [{
|
|
14523
15313
|
type: Component,
|
|
@@ -14525,7 +15315,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14525
15315
|
selector: "Aggregation, aggregation",
|
|
14526
15316
|
imports: [AggregationListComponent, AggregationTreeComponent, AggregationDateComponent],
|
|
14527
15317
|
template: `
|
|
14528
|
-
@
|
|
15318
|
+
@switch (resolvedType()) {
|
|
15319
|
+
@case ("date") {
|
|
14529
15320
|
<AggregationDate
|
|
14530
15321
|
[id]="id()"
|
|
14531
15322
|
[name]="name()"
|
|
@@ -14535,11 +15326,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14535
15326
|
[showFiltersCount]="showFiltersCount()"
|
|
14536
15327
|
[searchable]="searchable()"
|
|
14537
15328
|
[syncUrl]="syncUrl()"
|
|
15329
|
+
[query]="controlledQuery()"
|
|
15330
|
+
[aggregation]="controlledAggregation()"
|
|
15331
|
+
[selection]="controlledSelection()"
|
|
14538
15332
|
[title]="{ label: 'Date', icon: 'far fa-calendar-day' }"
|
|
14539
15333
|
(onApply)="onApply.emit($event)"
|
|
14540
15334
|
(onClear)="onClear.emit($event)"
|
|
15335
|
+
(selectionChange)="selectionChange.emit($event)"
|
|
15336
|
+
(filtersChange)="filtersChange.emit($event)"
|
|
14541
15337
|
/>
|
|
14542
|
-
|
|
15338
|
+
}
|
|
15339
|
+
@case ("tree") {
|
|
14543
15340
|
<AggregationTree
|
|
14544
15341
|
[id]="id()"
|
|
14545
15342
|
[name]="name()"
|
|
@@ -14549,13 +15346,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14549
15346
|
[showFiltersCount]="showFiltersCount()"
|
|
14550
15347
|
[searchable]="searchable()"
|
|
14551
15348
|
[syncUrl]="syncUrl()"
|
|
15349
|
+
[query]="controlledQuery()"
|
|
15350
|
+
[aggregation]="controlledAggregation()"
|
|
15351
|
+
[selection]="controlledSelection()"
|
|
15352
|
+
[itemTemplate]="itemDirective()?.template"
|
|
15353
|
+
[showCheckbox]="showCheckbox()"
|
|
14552
15354
|
[expandedLevel]="expandedLevel()"
|
|
14553
15355
|
(onSelect)="onSelect.emit($event)"
|
|
14554
15356
|
(onApply)="onApply.emit($event)"
|
|
14555
15357
|
(onClear)="onClear.emit($event)"
|
|
15358
|
+
(selectionChange)="selectionChange.emit($event)"
|
|
15359
|
+
(filtersChange)="filtersChange.emit($event)"
|
|
14556
15360
|
/>
|
|
14557
|
-
|
|
14558
|
-
|
|
15361
|
+
}
|
|
15362
|
+
@default {
|
|
14559
15363
|
<AggregationList
|
|
14560
15364
|
[id]="id()"
|
|
14561
15365
|
[name]="name()"
|
|
@@ -14565,17 +15369,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14565
15369
|
[showFiltersCount]="showFiltersCount()"
|
|
14566
15370
|
[searchable]="searchable()"
|
|
14567
15371
|
[syncUrl]="syncUrl()"
|
|
15372
|
+
[query]="controlledQuery()"
|
|
15373
|
+
[aggregation]="controlledAggregation()"
|
|
15374
|
+
[selection]="controlledSelection()"
|
|
15375
|
+
[itemTemplate]="itemDirective()?.template"
|
|
15376
|
+
[showCheckbox]="showCheckbox()"
|
|
14568
15377
|
(onSelect)="onSelect.emit($event)"
|
|
14569
15378
|
(onApply)="onApply.emit($event)"
|
|
14570
15379
|
(onClear)="onClear.emit($event)"
|
|
15380
|
+
(selectionChange)="selectionChange.emit($event)"
|
|
15381
|
+
(filtersChange)="filtersChange.emit($event)"
|
|
14571
15382
|
/>
|
|
14572
|
-
|
|
15383
|
+
}
|
|
15384
|
+
}
|
|
14573
15385
|
`,
|
|
14574
15386
|
host: {
|
|
14575
15387
|
"[class]": 'cn("block h-[inherit] max-h-[inherit]",class())'
|
|
14576
15388
|
}
|
|
14577
15389
|
}]
|
|
14578
|
-
}], 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"] }] } });
|
|
14579
15391
|
|
|
14580
15392
|
class AsideFiltersComponent {
|
|
14581
15393
|
cn = cn;
|
|
@@ -14609,7 +15421,7 @@ class AsideFiltersComponent {
|
|
|
14609
15421
|
showFiltersCount />
|
|
14610
15422
|
}
|
|
14611
15423
|
</div>
|
|
14612
|
-
`, 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"] }] });
|
|
14613
15425
|
}
|
|
14614
15426
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AsideFiltersComponent, decorators: [{
|
|
14615
15427
|
type: Component,
|
|
@@ -14760,7 +15572,7 @@ class FilterButtonComponent {
|
|
|
14760
15572
|
}
|
|
14761
15573
|
</PopoverContent>
|
|
14762
15574
|
</Popover>
|
|
14763
|
-
`, 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" }] });
|
|
14764
15576
|
}
|
|
14765
15577
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FilterButtonComponent, decorators: [{
|
|
14766
15578
|
type: Component,
|
|
@@ -14940,7 +15752,7 @@ class MoreComponent {
|
|
|
14940
15752
|
[name]="filter.name"
|
|
14941
15753
|
[column]="filter.column" />
|
|
14942
15754
|
}
|
|
14943
|
-
`, 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" }] });
|
|
14944
15756
|
}
|
|
14945
15757
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: MoreComponent, decorators: [{
|
|
14946
15758
|
type: Component,
|
|
@@ -16798,5 +17610,5 @@ const queryNameResolver = () => {
|
|
|
16798
17610
|
* Generated bundle index. Do not edit.
|
|
16799
17611
|
*/
|
|
16800
17612
|
|
|
16801
|
-
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 };
|
|
16802
17614
|
//# sourceMappingURL=sinequa-atomic-angular.mjs.map
|