@sinequa/atomic-angular 1.14.0 → 1.15.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.
@@ -7,7 +7,7 @@ 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, filter, getQueryParamsFromUrl, isRedirectPending, info, clearSessionTokens, login, 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';
10
+ import { EngineType, fetchApp, extraColumns, sysLang, fetchPrincipal, globalConfig, filter, getQueryParamsFromUrl, isRedirectPending, info, clearSessionTokens, login, error, setGlobalConfig, initializeAppConfig, notify, addConcepts, queryParamsFromUrl, patchUserSettings, deleteUserSettings, fetchUserSettings, warn, buildPathsAndLevels, escapeExpr, fetchQuery, flattenLegacyFilterValues, 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';
@@ -3224,7 +3224,9 @@ class QueryService {
3224
3224
  if (allowEmptySearch === false && query?.text === "") {
3225
3225
  warn("QueryService.search: Empty search query is not allowed. Returning empty result.");
3226
3226
  notify.warning(this.transloco.translate("searches.emptySearch"));
3227
- return of({ records: [] });
3227
+ const emptyResult = { records: [] };
3228
+ this.result.set(emptyResult);
3229
+ return of(emptyResult);
3228
3230
  }
3229
3231
  }
3230
3232
  const body = {
@@ -3232,7 +3234,7 @@ class QueryService {
3232
3234
  query,
3233
3235
  $auditRecord
3234
3236
  };
3235
- return this.http.post(`${this.API_URL}/query`, body).pipe(catchError((err) => {
3237
+ return this.http.post(`${this.API_URL}/query`, body).pipe(catchError(err => {
3236
3238
  error("queryService.getResults failure - error: ", err);
3237
3239
  return of({});
3238
3240
  }), map((result) => {
@@ -3251,19 +3253,17 @@ class QueryService {
3251
3253
  }
3252
3254
  });
3253
3255
  return result;
3254
- }), map((result) => {
3256
+ }), map(result => {
3255
3257
  result.records?.map((article) => Object.assign(article, { value: article.title, type: "default" }));
3256
3258
  return result;
3257
- }), map((result) => {
3259
+ }), map(result => {
3258
3260
  const r = {
3259
3261
  ...result,
3260
- nextPage: result.page !== undefined && result.page < Math.ceil(result.rowCount / result.pageSize)
3261
- ? result.page + 1
3262
- : undefined,
3262
+ nextPage: result.page !== undefined && result.page < Math.ceil(result.rowCount / result.pageSize) ? result.page + 1 : undefined,
3263
3263
  previousPage: result.page !== undefined && result.page > 1 ? result.page - 1 : undefined
3264
3264
  };
3265
3265
  return r;
3266
- }), tap((response) => {
3266
+ }), tap(response => {
3267
3267
  this.result.set(response);
3268
3268
  info("queryService.getResults success - data: ", response);
3269
3269
  }));
@@ -3289,10 +3289,10 @@ class QueryService {
3289
3289
  query
3290
3290
  });
3291
3291
  }
3292
- return this.http.post(`${this.API_URL}/multi`, body).pipe(catchError((err) => {
3292
+ return this.http.post(`${this.API_URL}/multi`, body).pipe(catchError(err => {
3293
3293
  error("queryService.bulkSearch failure - error: ", err);
3294
3294
  return EMPTY;
3295
- }), map((response) => {
3295
+ }), map(response => {
3296
3296
  // update $hasMore flag
3297
3297
  response.results.forEach((result) => {
3298
3298
  result.aggregations.forEach((agg) => {
@@ -3310,13 +3310,13 @@ class QueryService {
3310
3310
  });
3311
3311
  });
3312
3312
  return response.results;
3313
- }), map((results) => {
3314
- results.forEach((result) => {
3313
+ }), map(results => {
3314
+ results.forEach(result => {
3315
3315
  result.records?.map((article) => Object.assign(article, { value: article.title, type: "default" }));
3316
3316
  });
3317
3317
  return results;
3318
- }), map((results) => {
3319
- return results.map((result) => {
3318
+ }), map(results => {
3319
+ return results.map(result => {
3320
3320
  const r = {
3321
3321
  ...result,
3322
3322
  nextPage: result.page < Math.ceil(result.rowCount / result.pageSize) ? result.page + 1 : undefined,
@@ -3326,7 +3326,7 @@ class QueryService {
3326
3326
  });
3327
3327
  }),
3328
3328
  // map(response => ResultsSchema.parse(response) as T),
3329
- tap((response) => {
3329
+ tap(response => {
3330
3330
  info("queryService.bulkSearch success - data: ", response);
3331
3331
  }));
3332
3332
  }
@@ -3346,30 +3346,30 @@ class QueryService {
3346
3346
  this.queryParamsStore.patch({ page }, audit);
3347
3347
  }
3348
3348
  /**
3349
- * Get the pages associated to some text locations.
3350
- *
3351
- * @param docs - all documents offsets we want to get the pages from.
3352
- * @returns An Observable that emits the found pages.
3353
- */
3349
+ * Get the pages associated to some text locations.
3350
+ *
3351
+ * @param docs - all documents offsets we want to get the pages from.
3352
+ * @returns An Observable that emits the found pages.
3353
+ */
3354
3354
  getDocPages(docs) {
3355
3355
  const queryName = this.appStore.getDefaultQuery()?.name || "_default";
3356
3356
  const appName = globalConfig.app;
3357
3357
  return this.http.post(`${this.API_V2_URL}/app/${appName}/query/${queryName}/doc-pages`, { docs }).pipe(map(({ items }) => items), catchError(err => {
3358
- error('queryService.getDocPages failure - error: ', err);
3358
+ error("queryService.getDocPages failure - error: ", err);
3359
3359
  return [];
3360
3360
  }));
3361
3361
  }
3362
3362
  /**
3363
- * Get the page associated to a single text location.
3364
- *
3365
- * @param id - the document id.
3366
- * @param offset - offset from the passage location.
3367
- * @param length - length from the passage location.
3368
- * @returns An Observable that emits the found page.
3369
- */
3363
+ * Get the page associated to a single text location.
3364
+ *
3365
+ * @param id - the document id.
3366
+ * @param offset - offset from the passage location.
3367
+ * @param length - length from the passage location.
3368
+ * @returns An Observable that emits the found page.
3369
+ */
3370
3370
  getDocPage(id, offset, length) {
3371
3371
  return this.getDocPages([{ id, textLocations: [{ offset, length }] }]).pipe(map((response) => response[0].pages[0].pageNumber), catchError(err => {
3372
- error('queryService.getDocPage failure - error: ', err);
3372
+ error("queryService.getDocPage failure - error: ", err);
3373
3373
  return [];
3374
3374
  }));
3375
3375
  }
@@ -3526,7 +3526,7 @@ class AggregationsService {
3526
3526
  else {
3527
3527
  // get current filters for the aggregation
3528
3528
  const filters = this.queryParamsStore.getFilter({ field: agg.column, name: agg.name });
3529
- flattenedValues = filters ? this.flattenFilters([filters]) : [];
3529
+ flattenedValues = filters ? flattenLegacyFilterValues([filters]) : [];
3530
3530
  }
3531
3531
  if (agg.isTree) {
3532
3532
  const selectedAgg = agg.items.map(item => this.selectNode(item, flattenedValues));
@@ -3563,34 +3563,44 @@ class AggregationsService {
3563
3563
  */
3564
3564
  toFilter(item, field, name, isDistribution) {
3565
3565
  if (isDistribution) {
3566
- const res = item.value.match(/.*:\(?([><=\d\-.AND ]+)\)?/);
3567
- if (res?.[1]) {
3568
- const expr = res?.[1].split(" AND ");
3569
- const filters = expr.map(e => {
3570
- const operator = e.indexOf(">=") !== -1 ? "gte" : "lt";
3571
- const value = e.substring(e.indexOf(" ") + 1);
3572
- return { name, field, operator, value, display: item.display || item.value };
3573
- });
3574
- if (filters.length === 2) {
3575
- return {
3576
- operator: "and",
3577
- filters,
3578
- display: filters[0].display || filters[0].value,
3579
- field,
3580
- name
3581
- };
3582
- }
3583
- else if (filters.length === 1) {
3584
- return { ...filters[0], field, name };
3585
- }
3586
- throw new Error("Failed to parse distribution expression");
3566
+ // Parsing lives in @sinequa/atomic — `filter.distribution` is the single implementation, and it
3567
+ // recognises all four bounds where this method only ever produced `gte` or `lt`, reading `>` as
3568
+ // `gte` and `<=` as `lt`. What is added here is what the store needs and a `Filter` has no room
3569
+ // for: the aggregation's `name` on every leaf, which is what makes `updateFilter` replace the
3570
+ // entry rather than merge into it, and the human-readable `display`.
3571
+ const parsed = filter.distribution(field, item.value);
3572
+ if (parsed) {
3573
+ return this.nameLeaves(parsed, field, name, item.display || String(item.value ?? ""));
3587
3574
  }
3575
+ throw new Error("Failed to parse distribution expression");
3588
3576
  }
3589
3577
  if (typeof item.value === "string") {
3590
3578
  return { name, field, value: item.value, operator: "contains", display: item.display };
3591
3579
  }
3592
3580
  return { name, field, value: item.value, display: item.display };
3593
3581
  }
3582
+ /**
3583
+ * Stamps the aggregation's `name` and the item's `display` onto a filter parsed by
3584
+ * `filter.distribution`.
3585
+ *
3586
+ * A `Filter` carries neither, and the store needs both: `name` is what
3587
+ * `QueryParamsStore.updateFilter` matches on to **replace** an aggregation's entry instead of merging
3588
+ * into it — the only way a selection can shrink — and `display` is the label the applied-filter chips
3589
+ * render. A combined filter also keeps `field` at its root, which is how the store finds the entry.
3590
+ */
3591
+ nameLeaves(parsed, field, name, display) {
3592
+ const leaves = parsed.filters;
3593
+ if (leaves?.length) {
3594
+ return {
3595
+ ...parsed,
3596
+ field,
3597
+ name,
3598
+ display,
3599
+ filters: leaves.map(leaf => ({ ...leaf, field, name, display }))
3600
+ };
3601
+ }
3602
+ return { ...parsed, field, name, display };
3603
+ }
3594
3604
  findNode(items, path = "") {
3595
3605
  if (!items || items.length === 0) {
3596
3606
  return undefined;
@@ -3638,27 +3648,6 @@ class AggregationsService {
3638
3648
  }
3639
3649
  return node;
3640
3650
  }
3641
- flattenFilters(filters) {
3642
- const flattenedValues = [];
3643
- function extractValues(filters) {
3644
- for (const filter of filters) {
3645
- if (filter.value) {
3646
- flattenedValues.push(filter.value);
3647
- if (filter.display)
3648
- flattenedValues.push(filter.display);
3649
- // we handle "values" field for trees
3650
- }
3651
- else if (filter.values) {
3652
- flattenedValues.push(...filter.values);
3653
- }
3654
- if (filter.filters) {
3655
- extractValues(filter.filters);
3656
- }
3657
- }
3658
- }
3659
- extractValues(filters);
3660
- return flattenedValues;
3661
- }
3662
3651
  /**
3663
3652
  * Recursively opens parent nodes in a tree structure if any of their child nodes are selected or opened.
3664
3653
  *
@@ -5397,6 +5386,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImpo
5397
5386
 
5398
5387
  class UserProfileService {
5399
5388
  API_URL = `${globalConfig.backendUrl}/api/v2/user-profile`;
5389
+ // Keyed by the userId signal's identity rather than its value: every current caller
5390
+ // passes the same PrincipalStore.userId signal, so sharing the resource here collapses
5391
+ // what would otherwise be one httpResource (and one HTTP request) per caller into one
5392
+ // for the whole app -- and lets a `.set()` from one consumer (e.g. the profile edit
5393
+ // form) show up everywhere else that reads this resource.
5394
+ resources = new WeakMap();
5400
5395
  /**
5401
5396
  * Retrieves the user profile by user ID.
5402
5397
  *
@@ -5404,11 +5399,16 @@ class UserProfileService {
5404
5399
  * @returns An Observable that emits an the UserProfile object.
5405
5400
  */
5406
5401
  getUserProfile(userId) {
5407
- return httpResource(() => (userId() ? `${this.API_URL}/${userId()}` : undefined), {
5408
- parse: (response) => {
5409
- return response;
5410
- }
5411
- });
5402
+ let resource = this.resources.get(userId);
5403
+ if (!resource) {
5404
+ resource = httpResource(() => (userId() ? `${this.API_URL}/${userId()}` : undefined), {
5405
+ parse: response => {
5406
+ return response;
5407
+ }
5408
+ });
5409
+ this.resources.set(userId, resource);
5410
+ }
5411
+ return resource;
5412
5412
  }
5413
5413
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: UserProfileService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
5414
5414
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: UserProfileService, providedIn: "root" });
@@ -7872,7 +7872,7 @@ class NavbarTabsComponent {
7872
7872
  // the filter params (f/sort/id/page) already in the URL are kept.
7873
7873
  // - default: 'replace' so everything is rewritten from scratch.
7874
7874
  getQueryParamsHandling() {
7875
- return this.persistFiltersAcrossTabs() ? 'merge' : 'replace';
7875
+ return this.persistFiltersAcrossTabs() ? "merge" : "replace";
7876
7876
  }
7877
7877
  // Get query params conditionally
7878
7878
  getQueryParams(tab) {
@@ -7915,7 +7915,7 @@ class NavbarTabsComponent {
7915
7915
  }
7916
7916
  changeTab() { }
7917
7917
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: NavbarTabsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7918
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", type: NavbarTabsComponent, isStandalone: true, selector: "navbar-tabs", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, showCount: { classPropertyName: "showCount", publicName: "showCount", isSignal: true, isRequired: false, transformFunction: null }, noTruncate: { classPropertyName: "noTruncate", publicName: "noTruncate", isSignal: true, isRequired: false, transformFunction: null }, minTabWidth: { classPropertyName: "minTabWidth", publicName: "minTabWidth", isSignal: true, isRequired: false, transformFunction: null }, path: { classPropertyName: "path", publicName: "path", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "cn('block', class())" } }, providers: [provideTranslocoScope('navbar-tabs')], ngImport: i0, template: `
7918
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", type: NavbarTabsComponent, isStandalone: true, selector: "navbar-tabs", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, showCount: { classPropertyName: "showCount", publicName: "showCount", isSignal: true, isRequired: false, transformFunction: null }, noTruncate: { classPropertyName: "noTruncate", publicName: "noTruncate", isSignal: true, isRequired: false, transformFunction: null }, minTabWidth: { classPropertyName: "minTabWidth", publicName: "minTabWidth", isSignal: true, isRequired: false, transformFunction: null }, path: { classPropertyName: "path", publicName: "path", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "cn('block', class())" } }, providers: [provideTranslocoScope("navbar-tabs")], ngImport: i0, template: `
7919
7919
  <!-- do not display the tabs if there are no tabs -->
7920
7920
  @if (nav.tabs().length > 0) {
7921
7921
  <div overflowManager (count)="visibleTabCount.set($event)" class="flex items-end rounded-[inherit] bg-inherit">
@@ -7923,8 +7923,7 @@ class NavbarTabsComponent {
7923
7923
  <TabsList class="w-full">
7924
7924
  @for (tab of nav.tabs(); track $index) {
7925
7925
  <Tab
7926
- [class]="cn('group', !noTruncate() && 'basis-[12.5rem]')"
7927
- title="{{ tab.display | syslang | transloco }}"
7926
+ [class]="cn('group', !noTruncate() && 'basis-50')"
7928
7927
  value="{{ tab.display }}"
7929
7928
  overflowItem
7930
7929
  [noTruncate]="noTruncate()"
@@ -8021,7 +8020,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImpo
8021
8020
  EllipsisIcon,
8022
8021
  FaIconComponent
8023
8022
  ],
8024
- providers: [provideTranslocoScope('navbar-tabs')],
8023
+ providers: [provideTranslocoScope("navbar-tabs")],
8025
8024
  template: `
8026
8025
  <!-- do not display the tabs if there are no tabs -->
8027
8026
  @if (nav.tabs().length > 0) {
@@ -8030,8 +8029,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImpo
8030
8029
  <TabsList class="w-full">
8031
8030
  @for (tab of nav.tabs(); track $index) {
8032
8031
  <Tab
8033
- [class]="cn('group', !noTruncate() && 'basis-[12.5rem]')"
8034
- title="{{ tab.display | syslang | transloco }}"
8032
+ [class]="cn('group', !noTruncate() && 'basis-50')"
8035
8033
  value="{{ tab.display }}"
8036
8034
  overflowItem
8037
8035
  [noTruncate]="noTruncate()"
@@ -9475,41 +9473,57 @@ function injectAggregationBase(refs) {
9475
9473
  }
9476
9474
  function addCurrentFiltersToItems(aggregation) {
9477
9475
  const currentItems = aggregation?.items || [];
9478
- if (!aggregation?.isTree && !aggregation?.isDistribution) {
9479
- const currentFilters = isControlled() ? selectionAsFilter() : queryParamsStore.getFilter({ field: aggregation?.column, name: aggregation?.name });
9480
- if (currentFilters) {
9481
- if (Array.isArray(currentFilters.filters) && currentFilters.filters.length) {
9482
- currentFilters.filters.forEach((filter) => {
9483
- const found = currentItems.find(item => item.value?.toString().toLocaleLowerCase() === filter.value?.toLocaleLowerCase());
9484
- if (!found) {
9485
- currentItems.unshift({ value: filter.value, display: filter.display, $selected: true });
9486
- }
9487
- else {
9488
- found.$selected = true;
9489
- }
9490
- });
9491
- }
9492
- else if (Array.isArray(currentFilters.values) && currentFilters.values.length) {
9493
- // multiple values stored as a string array, e.g. { values: ["alice_martin", "caroline_dubois"] }
9494
- // (no `filters` sub-array and no single `value`) — mark each matching item as selected
9495
- currentFilters.values.forEach(value => {
9496
- const found = currentItems.find(item => item.value?.toString().toLocaleLowerCase() === value?.toString().toLocaleLowerCase());
9497
- if (!found) {
9498
- currentItems.unshift({ value, display: value, $selected: true });
9499
- }
9500
- else {
9501
- found.$selected = true;
9502
- }
9503
- });
9476
+ if (aggregation?.isTree) {
9477
+ return currentItems;
9478
+ }
9479
+ const currentFilters = isControlled() ? selectionAsFilter() : queryParamsStore.getFilter({ field: aggregation?.column, name: aggregation?.name });
9480
+ if (aggregation?.isDistribution) {
9481
+ // A distribution item's `value` is the full backend expression (e.g.
9482
+ // `size`10 Kb to 100 Kb`:(>= 10240 AND < 102400)`), not comparable to what the applied
9483
+ // filter stores — its leaves only ever carry the bucket's numeric thresholds. Flatten the
9484
+ // filter and compare on `display` instead (or `value` when `valuesAreExpressions` is not
9485
+ // set) — same strategy as `AggregationsService.enrichAggregation`.
9486
+ const flattenedValues = currentFilters ? flattenLegacyFilterValues([currentFilters]) : [];
9487
+ currentItems.forEach(item => {
9488
+ const valueToSearch = aggregation.valuesAreExpressions ? item.display : item.value;
9489
+ if (flattenedValues.includes(valueToSearch?.toString() ?? "")) {
9490
+ item.$selected = true;
9504
9491
  }
9505
- else if (currentFilters.value) {
9506
- const found = currentItems.find(item => item.value?.toString().toLocaleLowerCase() === currentFilters.value?.toLocaleLowerCase());
9492
+ });
9493
+ return currentItems;
9494
+ }
9495
+ if (currentFilters) {
9496
+ if (Array.isArray(currentFilters.filters) && currentFilters.filters.length) {
9497
+ currentFilters.filters.forEach((filter) => {
9498
+ const found = currentItems.find(item => item.value?.toString().toLocaleLowerCase() === filter.value?.toLocaleLowerCase());
9507
9499
  if (!found) {
9508
- currentItems.push({ value: currentFilters.value, display: currentFilters.display, $selected: true });
9500
+ currentItems.unshift({ value: filter.value, display: filter.display, $selected: true });
9509
9501
  }
9510
9502
  else {
9511
9503
  found.$selected = true;
9512
9504
  }
9505
+ });
9506
+ }
9507
+ else if (Array.isArray(currentFilters.values) && currentFilters.values.length) {
9508
+ // multiple values stored as a string array, e.g. { values: ["alice_martin", "caroline_dubois"] }
9509
+ // (no `filters` sub-array and no single `value`) — mark each matching item as selected
9510
+ currentFilters.values.forEach(value => {
9511
+ const found = currentItems.find(item => item.value?.toString().toLocaleLowerCase() === value?.toString().toLocaleLowerCase());
9512
+ if (!found) {
9513
+ currentItems.unshift({ value, display: value, $selected: true });
9514
+ }
9515
+ else {
9516
+ found.$selected = true;
9517
+ }
9518
+ });
9519
+ }
9520
+ else if (currentFilters.value) {
9521
+ const found = currentItems.find(item => item.value?.toString().toLocaleLowerCase() === currentFilters.value?.toLocaleLowerCase());
9522
+ if (!found) {
9523
+ currentItems.push({ value: currentFilters.value, display: currentFilters.display, $selected: true });
9524
+ }
9525
+ else {
9526
+ found.$selected = true;
9513
9527
  }
9514
9528
  }
9515
9529
  }
@@ -10120,7 +10134,7 @@ class AggregationDateComponent {
10120
10134
  throw new Error("filters.filterInvalid");
10121
10135
  }
10122
10136
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: AggregationDateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
10123
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", 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 }, emitOn: { classPropertyName: "emitOn", publicName: "emitOn", 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: { properties: { "class": "cn(\"@container block\", isEmpty() && \"hidden\")" } }, 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\">\n <summary\n [class.cursor-pointer]=\"collapsible()\"\n class=\"m-0 flex h-8 w-full items-center gap-1 pl-1 font-semibold select-none\"\n (click)=\"onHeaderClick($event)\">\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n\n @if (hasFilters()) {\n @let clearLabel = \"filters.clearFilters\" | transloco;\n <button\n variant=\"none\"\n icon-button\n [aria-label]=\"clearLabel\"\n (click)=\"$event.stopPropagation(); clear()\">\n <filter-x-icon />\n </button>\n }\n\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n @let label = \"filters.apply\" | transloco;\n <button\n variant=\"accent\"\n size=\"sm\"\n [aria-label]=\"label\"\n (click)=\"$event.stopPropagation(); apply()\">\n <FilterIcon />\n {{ label }}\n </button>\n }\n @if (collapsible()) {\n <icon-button\n title=\"Open/Close\"\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevronright />\n </icon-button>\n }\n </summary>\n\n <!-- content wrapper -->\n <form [formGroup]=\"form\">\n <ul\n class=\"flex max-h-[var(--scroll-height,20rem)] snap-y snap-start scrollbar-thin flex-col gap-1 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track $index) {\n <li\n role=\"listitem\"\n tabindex=\"0\"\n (click)=\"radio.click()\"\n [attr.aria-label]=\"option.display | syslang | transloco\"\n [class]=\"\n cn(\n 'flex p-0 px-2 leading-7',\n form.get('option')?.value === option.display && 'bg-accent',\n option.hidden && 'hidden',\n option.disabled && 'disabled pointer-events-none text-neutral-300'\n )\n \"\n [attr.aria-hidden]=\"option.disabled\">\n <input\n #radio\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-{{ option.display }}\"\n [attr.disabled]=\"option.disabled ? true : null\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"select()\"\n value=\"{{ option.display }}\" />\n\n <label\n for=\"date-filter-{{ option.display }}\"\n class=\"grow cursor-pointer p-1\">\n {{ option.display | syslang | transloco }}\n </label>\n </li>\n }\n\n @if (allowCustomRange) {\n <li\n role=\"listitem\"\n aria-label=\"open date range picker\"\n class=\"flex px-2 leading-7\"\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\n <input\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-range-dialog\"\n value=\"custom-range\"\n (click)=\"select()\" />\n <div\n class=\"@container flex grow justify-end gap-1 p-1 @max-[340px]:flex-wrap\">\n <div class=\"flex gap-1\">\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\n \"filters.from\" | transloco\n }}</label>\n <input\n id=\"datepicker-range-start\"\n name=\"start\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeFrom()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n <div class=\"flex gap-1\">\n <label\n for=\"datepicker-range-end\"\n class=\"min-w-10 truncate text-right\"\n >{{ \"filters.to\" | transloco }}</label\n >\n <input\n id=\"datepicker-range-end\"\n name=\"end\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeTo()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n </div>\n </li>\n }\n </ul>\n </form>\n</details>\n\n<aggregation-date-range-dialog\n [lang]=\"lang()\"\n [useDateRange]=\"false\"\n [min]=\"form.get('customRange.from')?.value || undefined\"\n [max]=\"form.get('customRange.to')?.value || undefined\"\n (rangeSelected)=\"onRangeSelected($event)\" />\n", styles: [":host{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: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { 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" }] });
10137
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", 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 }, emitOn: { classPropertyName: "emitOn", publicName: "emitOn", 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: { properties: { "class": "cn(\"@container block\", isEmpty() && \"hidden\")" } }, 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\">\n <summary\n [class.cursor-pointer]=\"collapsible()\"\n class=\"m-0 flex h-8 w-full items-center gap-1 pl-1 font-semibold select-none\"\n (click)=\"onHeaderClick($event)\">\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n\n @if (hasFilters()) {\n @let clearLabel = \"filters.clearFilters\" | transloco;\n <button\n variant=\"none\"\n icon-button\n [aria-label]=\"clearLabel\"\n (click)=\"$event.stopPropagation(); clear()\">\n <filter-x-icon />\n </button>\n }\n\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n @let label = \"filters.apply\" | transloco;\n <button\n variant=\"accent\"\n size=\"sm\"\n [aria-label]=\"label\"\n (click)=\"$event.stopPropagation(); apply()\">\n <FilterIcon />\n {{ label }}\n </button>\n }\n @if (collapsible()) {\n <icon-button\n title=\"Open/Close\"\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevronright />\n </icon-button>\n }\n </summary>\n\n <!-- content wrapper -->\n <form [formGroup]=\"form\">\n <ul\n class=\"flex max-h-[var(--scroll-height,20rem)] snap-y snap-start scrollbar-thin flex-col gap-1 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track $index) {\n <li\n role=\"listitem\"\n tabindex=\"0\"\n (click)=\"radio.click()\"\n [attr.aria-label]=\"option.display | syslang | transloco\"\n [class]=\"\n cn(\n 'flex p-0 px-2 leading-7',\n form.get('option')?.value === option.display && 'bg-accent',\n option.hidden && 'hidden',\n option.disabled && 'disabled pointer-events-none text-neutral-300'\n )\n \"\n [attr.aria-hidden]=\"option.disabled\">\n <input\n #radio\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-{{ option.display }}\"\n [attr.disabled]=\"option.disabled ? true : null\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"select()\"\n value=\"{{ option.display }}\" />\n\n <label\n for=\"date-filter-{{ option.display }}\"\n class=\"grow cursor-pointer p-1\">\n {{ option.display | syslang | transloco }}\n </label>\n </li>\n }\n\n @if (allowCustomRange) {\n <li\n role=\"listitem\"\n aria-label=\"open date range picker\"\n class=\"flex px-2 leading-7\"\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\n <input\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-range-dialog\"\n value=\"custom-range\"\n (click)=\"select()\" />\n <div class=\"@container flex grow gap-1 p-1 @max-[340px]:flex-wrap\">\n <div class=\"flex gap-1\">\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\n \"filters.from\" | transloco\n }}</label>\n <input\n id=\"datepicker-range-start\"\n name=\"start\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeFrom()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n <div class=\"flex gap-1\">\n <label\n for=\"datepicker-range-end\"\n class=\"min-w-10 truncate text-right\"\n >{{ \"filters.to\" | transloco }}</label\n >\n <input\n id=\"datepicker-range-end\"\n name=\"end\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeTo()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n </div>\n </li>\n }\n </ul>\n </form>\n</details>\n\n<aggregation-date-range-dialog\n [lang]=\"lang()\"\n [useDateRange]=\"false\"\n [min]=\"form.get('customRange.from')?.value || undefined\"\n [max]=\"form.get('customRange.to')?.value || undefined\"\n (rangeSelected)=\"onRangeSelected($event)\" />\n", styles: [":host{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: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { 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" }] });
10124
10138
  }
10125
10139
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: AggregationDateComponent, decorators: [{
10126
10140
  type: Component,
@@ -10145,7 +10159,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImpo
10145
10159
  // layout — a date facet has no "empty but resolved" state to gate behind
10146
10160
  // `hideWhenEmpty` (see `isEmpty`), unlike list/tree.
10147
10161
  "[class]": 'cn("@container block", isEmpty() && "hidden")'
10148
- }, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\n <summary\n [class.cursor-pointer]=\"collapsible()\"\n class=\"m-0 flex h-8 w-full items-center gap-1 pl-1 font-semibold select-none\"\n (click)=\"onHeaderClick($event)\">\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n\n @if (hasFilters()) {\n @let clearLabel = \"filters.clearFilters\" | transloco;\n <button\n variant=\"none\"\n icon-button\n [aria-label]=\"clearLabel\"\n (click)=\"$event.stopPropagation(); clear()\">\n <filter-x-icon />\n </button>\n }\n\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n @let label = \"filters.apply\" | transloco;\n <button\n variant=\"accent\"\n size=\"sm\"\n [aria-label]=\"label\"\n (click)=\"$event.stopPropagation(); apply()\">\n <FilterIcon />\n {{ label }}\n </button>\n }\n @if (collapsible()) {\n <icon-button\n title=\"Open/Close\"\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevronright />\n </icon-button>\n }\n </summary>\n\n <!-- content wrapper -->\n <form [formGroup]=\"form\">\n <ul\n class=\"flex max-h-[var(--scroll-height,20rem)] snap-y snap-start scrollbar-thin flex-col gap-1 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track $index) {\n <li\n role=\"listitem\"\n tabindex=\"0\"\n (click)=\"radio.click()\"\n [attr.aria-label]=\"option.display | syslang | transloco\"\n [class]=\"\n cn(\n 'flex p-0 px-2 leading-7',\n form.get('option')?.value === option.display && 'bg-accent',\n option.hidden && 'hidden',\n option.disabled && 'disabled pointer-events-none text-neutral-300'\n )\n \"\n [attr.aria-hidden]=\"option.disabled\">\n <input\n #radio\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-{{ option.display }}\"\n [attr.disabled]=\"option.disabled ? true : null\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"select()\"\n value=\"{{ option.display }}\" />\n\n <label\n for=\"date-filter-{{ option.display }}\"\n class=\"grow cursor-pointer p-1\">\n {{ option.display | syslang | transloco }}\n </label>\n </li>\n }\n\n @if (allowCustomRange) {\n <li\n role=\"listitem\"\n aria-label=\"open date range picker\"\n class=\"flex px-2 leading-7\"\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\n <input\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-range-dialog\"\n value=\"custom-range\"\n (click)=\"select()\" />\n <div\n class=\"@container flex grow justify-end gap-1 p-1 @max-[340px]:flex-wrap\">\n <div class=\"flex gap-1\">\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\n \"filters.from\" | transloco\n }}</label>\n <input\n id=\"datepicker-range-start\"\n name=\"start\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeFrom()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n <div class=\"flex gap-1\">\n <label\n for=\"datepicker-range-end\"\n class=\"min-w-10 truncate text-right\"\n >{{ \"filters.to\" | transloco }}</label\n >\n <input\n id=\"datepicker-range-end\"\n name=\"end\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeTo()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n </div>\n </li>\n }\n </ul>\n </form>\n</details>\n\n<aggregation-date-range-dialog\n [lang]=\"lang()\"\n [useDateRange]=\"false\"\n [min]=\"form.get('customRange.from')?.value || undefined\"\n [max]=\"form.get('customRange.to')?.value || undefined\"\n (rangeSelected)=\"onRangeSelected($event)\" />\n", styles: [":host{min-width:200px}ul[role=list]{scrollbar-width:thin}\n"] }]
10162
+ }, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\n <summary\n [class.cursor-pointer]=\"collapsible()\"\n class=\"m-0 flex h-8 w-full items-center gap-1 pl-1 font-semibold select-none\"\n (click)=\"onHeaderClick($event)\">\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n\n @if (hasFilters()) {\n @let clearLabel = \"filters.clearFilters\" | transloco;\n <button\n variant=\"none\"\n icon-button\n [aria-label]=\"clearLabel\"\n (click)=\"$event.stopPropagation(); clear()\">\n <filter-x-icon />\n </button>\n }\n\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n @let label = \"filters.apply\" | transloco;\n <button\n variant=\"accent\"\n size=\"sm\"\n [aria-label]=\"label\"\n (click)=\"$event.stopPropagation(); apply()\">\n <FilterIcon />\n {{ label }}\n </button>\n }\n @if (collapsible()) {\n <icon-button\n title=\"Open/Close\"\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevronright />\n </icon-button>\n }\n </summary>\n\n <!-- content wrapper -->\n <form [formGroup]=\"form\">\n <ul\n class=\"flex max-h-[var(--scroll-height,20rem)] snap-y snap-start scrollbar-thin flex-col gap-1 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track $index) {\n <li\n role=\"listitem\"\n tabindex=\"0\"\n (click)=\"radio.click()\"\n [attr.aria-label]=\"option.display | syslang | transloco\"\n [class]=\"\n cn(\n 'flex p-0 px-2 leading-7',\n form.get('option')?.value === option.display && 'bg-accent',\n option.hidden && 'hidden',\n option.disabled && 'disabled pointer-events-none text-neutral-300'\n )\n \"\n [attr.aria-hidden]=\"option.disabled\">\n <input\n #radio\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-{{ option.display }}\"\n [attr.disabled]=\"option.disabled ? true : null\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"select()\"\n value=\"{{ option.display }}\" />\n\n <label\n for=\"date-filter-{{ option.display }}\"\n class=\"grow cursor-pointer p-1\">\n {{ option.display | syslang | transloco }}\n </label>\n </li>\n }\n\n @if (allowCustomRange) {\n <li\n role=\"listitem\"\n aria-label=\"open date range picker\"\n class=\"flex px-2 leading-7\"\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\n <input\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-range-dialog\"\n value=\"custom-range\"\n (click)=\"select()\" />\n <div class=\"@container flex grow gap-1 p-1 @max-[340px]:flex-wrap\">\n <div class=\"flex gap-1\">\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\n \"filters.from\" | transloco\n }}</label>\n <input\n id=\"datepicker-range-start\"\n name=\"start\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeFrom()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n <div class=\"flex gap-1\">\n <label\n for=\"datepicker-range-end\"\n class=\"min-w-10 truncate text-right\"\n >{{ \"filters.to\" | transloco }}</label\n >\n <input\n id=\"datepicker-range-end\"\n name=\"end\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeTo()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n </div>\n </li>\n }\n </ul>\n </form>\n</details>\n\n<aggregation-date-range-dialog\n [lang]=\"lang()\"\n [useDateRange]=\"false\"\n [min]=\"form.get('customRange.from')?.value || undefined\"\n [max]=\"form.get('customRange.to')?.value || undefined\"\n (rangeSelected)=\"onRangeSelected($event)\" />\n", styles: [":host{min-width:200px}ul[role=list]{scrollbar-width:thin}\n"] }]
10149
10163
  }], 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 }] }], emitOn: [{ type: i0.Input, args: [{ isSignal: true, alias: "emitOn", 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"] }] } });
10150
10164
 
10151
10165
  /**
@@ -10155,7 +10169,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImpo
10155
10169
  */
10156
10170
  class DateComponent extends AggregationDateComponent {
10157
10171
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: DateComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
10158
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", 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\">\n <summary\n [class.cursor-pointer]=\"collapsible()\"\n class=\"m-0 flex h-8 w-full items-center gap-1 pl-1 font-semibold select-none\"\n (click)=\"onHeaderClick($event)\">\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n\n @if (hasFilters()) {\n @let clearLabel = \"filters.clearFilters\" | transloco;\n <button\n variant=\"none\"\n icon-button\n [aria-label]=\"clearLabel\"\n (click)=\"$event.stopPropagation(); clear()\">\n <filter-x-icon />\n </button>\n }\n\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n @let label = \"filters.apply\" | transloco;\n <button\n variant=\"accent\"\n size=\"sm\"\n [aria-label]=\"label\"\n (click)=\"$event.stopPropagation(); apply()\">\n <FilterIcon />\n {{ label }}\n </button>\n }\n @if (collapsible()) {\n <icon-button\n title=\"Open/Close\"\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevronright />\n </icon-button>\n }\n </summary>\n\n <!-- content wrapper -->\n <form [formGroup]=\"form\">\n <ul\n class=\"flex max-h-[var(--scroll-height,20rem)] snap-y snap-start scrollbar-thin flex-col gap-1 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track $index) {\n <li\n role=\"listitem\"\n tabindex=\"0\"\n (click)=\"radio.click()\"\n [attr.aria-label]=\"option.display | syslang | transloco\"\n [class]=\"\n cn(\n 'flex p-0 px-2 leading-7',\n form.get('option')?.value === option.display && 'bg-accent',\n option.hidden && 'hidden',\n option.disabled && 'disabled pointer-events-none text-neutral-300'\n )\n \"\n [attr.aria-hidden]=\"option.disabled\">\n <input\n #radio\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-{{ option.display }}\"\n [attr.disabled]=\"option.disabled ? true : null\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"select()\"\n value=\"{{ option.display }}\" />\n\n <label\n for=\"date-filter-{{ option.display }}\"\n class=\"grow cursor-pointer p-1\">\n {{ option.display | syslang | transloco }}\n </label>\n </li>\n }\n\n @if (allowCustomRange) {\n <li\n role=\"listitem\"\n aria-label=\"open date range picker\"\n class=\"flex px-2 leading-7\"\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\n <input\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-range-dialog\"\n value=\"custom-range\"\n (click)=\"select()\" />\n <div\n class=\"@container flex grow justify-end gap-1 p-1 @max-[340px]:flex-wrap\">\n <div class=\"flex gap-1\">\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\n \"filters.from\" | transloco\n }}</label>\n <input\n id=\"datepicker-range-start\"\n name=\"start\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeFrom()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n <div class=\"flex gap-1\">\n <label\n for=\"datepicker-range-end\"\n class=\"min-w-10 truncate text-right\"\n >{{ \"filters.to\" | transloco }}</label\n >\n <input\n id=\"datepicker-range-end\"\n name=\"end\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeTo()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n </div>\n </li>\n }\n </ul>\n </form>\n</details>\n\n<aggregation-date-range-dialog\n [lang]=\"lang()\"\n [useDateRange]=\"false\"\n [min]=\"form.get('customRange.from')?.value || undefined\"\n [max]=\"form.get('customRange.to')?.value || undefined\"\n (rangeSelected)=\"onRangeSelected($event)\" />\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" }] });
10172
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", 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\">\n <summary\n [class.cursor-pointer]=\"collapsible()\"\n class=\"m-0 flex h-8 w-full items-center gap-1 pl-1 font-semibold select-none\"\n (click)=\"onHeaderClick($event)\">\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n\n @if (hasFilters()) {\n @let clearLabel = \"filters.clearFilters\" | transloco;\n <button\n variant=\"none\"\n icon-button\n [aria-label]=\"clearLabel\"\n (click)=\"$event.stopPropagation(); clear()\">\n <filter-x-icon />\n </button>\n }\n\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n @let label = \"filters.apply\" | transloco;\n <button\n variant=\"accent\"\n size=\"sm\"\n [aria-label]=\"label\"\n (click)=\"$event.stopPropagation(); apply()\">\n <FilterIcon />\n {{ label }}\n </button>\n }\n @if (collapsible()) {\n <icon-button\n title=\"Open/Close\"\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevronright />\n </icon-button>\n }\n </summary>\n\n <!-- content wrapper -->\n <form [formGroup]=\"form\">\n <ul\n class=\"flex max-h-[var(--scroll-height,20rem)] snap-y snap-start scrollbar-thin flex-col gap-1 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track $index) {\n <li\n role=\"listitem\"\n tabindex=\"0\"\n (click)=\"radio.click()\"\n [attr.aria-label]=\"option.display | syslang | transloco\"\n [class]=\"\n cn(\n 'flex p-0 px-2 leading-7',\n form.get('option')?.value === option.display && 'bg-accent',\n option.hidden && 'hidden',\n option.disabled && 'disabled pointer-events-none text-neutral-300'\n )\n \"\n [attr.aria-hidden]=\"option.disabled\">\n <input\n #radio\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-{{ option.display }}\"\n [attr.disabled]=\"option.disabled ? true : null\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"select()\"\n value=\"{{ option.display }}\" />\n\n <label\n for=\"date-filter-{{ option.display }}\"\n class=\"grow cursor-pointer p-1\">\n {{ option.display | syslang | transloco }}\n </label>\n </li>\n }\n\n @if (allowCustomRange) {\n <li\n role=\"listitem\"\n aria-label=\"open date range picker\"\n class=\"flex px-2 leading-7\"\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\n <input\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-range-dialog\"\n value=\"custom-range\"\n (click)=\"select()\" />\n <div class=\"@container flex grow gap-1 p-1 @max-[340px]:flex-wrap\">\n <div class=\"flex gap-1\">\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\n \"filters.from\" | transloco\n }}</label>\n <input\n id=\"datepicker-range-start\"\n name=\"start\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeFrom()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n <div class=\"flex gap-1\">\n <label\n for=\"datepicker-range-end\"\n class=\"min-w-10 truncate text-right\"\n >{{ \"filters.to\" | transloco }}</label\n >\n <input\n id=\"datepicker-range-end\"\n name=\"end\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeTo()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n </div>\n </li>\n }\n </ul>\n </form>\n</details>\n\n<aggregation-date-range-dialog\n [lang]=\"lang()\"\n [useDateRange]=\"false\"\n [min]=\"form.get('customRange.from')?.value || undefined\"\n [max]=\"form.get('customRange.to')?.value || undefined\"\n (rangeSelected)=\"onRangeSelected($event)\" />\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" }] });
10159
10173
  }
10160
10174
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: DateComponent, decorators: [{
10161
10175
  type: Component,
@@ -10172,7 +10186,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImpo
10172
10186
  FilterXIcon
10173
10187
  ], host: {
10174
10188
  class: "@container"
10175
- }, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\n <summary\n [class.cursor-pointer]=\"collapsible()\"\n class=\"m-0 flex h-8 w-full items-center gap-1 pl-1 font-semibold select-none\"\n (click)=\"onHeaderClick($event)\">\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n\n @if (hasFilters()) {\n @let clearLabel = \"filters.clearFilters\" | transloco;\n <button\n variant=\"none\"\n icon-button\n [aria-label]=\"clearLabel\"\n (click)=\"$event.stopPropagation(); clear()\">\n <filter-x-icon />\n </button>\n }\n\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n @let label = \"filters.apply\" | transloco;\n <button\n variant=\"accent\"\n size=\"sm\"\n [aria-label]=\"label\"\n (click)=\"$event.stopPropagation(); apply()\">\n <FilterIcon />\n {{ label }}\n </button>\n }\n @if (collapsible()) {\n <icon-button\n title=\"Open/Close\"\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevronright />\n </icon-button>\n }\n </summary>\n\n <!-- content wrapper -->\n <form [formGroup]=\"form\">\n <ul\n class=\"flex max-h-[var(--scroll-height,20rem)] snap-y snap-start scrollbar-thin flex-col gap-1 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track $index) {\n <li\n role=\"listitem\"\n tabindex=\"0\"\n (click)=\"radio.click()\"\n [attr.aria-label]=\"option.display | syslang | transloco\"\n [class]=\"\n cn(\n 'flex p-0 px-2 leading-7',\n form.get('option')?.value === option.display && 'bg-accent',\n option.hidden && 'hidden',\n option.disabled && 'disabled pointer-events-none text-neutral-300'\n )\n \"\n [attr.aria-hidden]=\"option.disabled\">\n <input\n #radio\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-{{ option.display }}\"\n [attr.disabled]=\"option.disabled ? true : null\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"select()\"\n value=\"{{ option.display }}\" />\n\n <label\n for=\"date-filter-{{ option.display }}\"\n class=\"grow cursor-pointer p-1\">\n {{ option.display | syslang | transloco }}\n </label>\n </li>\n }\n\n @if (allowCustomRange) {\n <li\n role=\"listitem\"\n aria-label=\"open date range picker\"\n class=\"flex px-2 leading-7\"\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\n <input\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-range-dialog\"\n value=\"custom-range\"\n (click)=\"select()\" />\n <div\n class=\"@container flex grow justify-end gap-1 p-1 @max-[340px]:flex-wrap\">\n <div class=\"flex gap-1\">\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\n \"filters.from\" | transloco\n }}</label>\n <input\n id=\"datepicker-range-start\"\n name=\"start\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeFrom()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n <div class=\"flex gap-1\">\n <label\n for=\"datepicker-range-end\"\n class=\"min-w-10 truncate text-right\"\n >{{ \"filters.to\" | transloco }}</label\n >\n <input\n id=\"datepicker-range-end\"\n name=\"end\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeTo()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n </div>\n </li>\n }\n </ul>\n </form>\n</details>\n\n<aggregation-date-range-dialog\n [lang]=\"lang()\"\n [useDateRange]=\"false\"\n [min]=\"form.get('customRange.from')?.value || undefined\"\n [max]=\"form.get('customRange.to')?.value || undefined\"\n (rangeSelected)=\"onRangeSelected($event)\" />\n", styles: [":host{display:block;min-width:200px}ul[role=list]{scrollbar-width:thin}\n"] }]
10189
+ }, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\n <summary\n [class.cursor-pointer]=\"collapsible()\"\n class=\"m-0 flex h-8 w-full items-center gap-1 pl-1 font-semibold select-none\"\n (click)=\"onHeaderClick($event)\">\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n\n @if (hasFilters()) {\n @let clearLabel = \"filters.clearFilters\" | transloco;\n <button\n variant=\"none\"\n icon-button\n [aria-label]=\"clearLabel\"\n (click)=\"$event.stopPropagation(); clear()\">\n <filter-x-icon />\n </button>\n }\n\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n @let label = \"filters.apply\" | transloco;\n <button\n variant=\"accent\"\n size=\"sm\"\n [aria-label]=\"label\"\n (click)=\"$event.stopPropagation(); apply()\">\n <FilterIcon />\n {{ label }}\n </button>\n }\n @if (collapsible()) {\n <icon-button\n title=\"Open/Close\"\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevronright />\n </icon-button>\n }\n </summary>\n\n <!-- content wrapper -->\n <form [formGroup]=\"form\">\n <ul\n class=\"flex max-h-[var(--scroll-height,20rem)] snap-y snap-start scrollbar-thin flex-col gap-1 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track $index) {\n <li\n role=\"listitem\"\n tabindex=\"0\"\n (click)=\"radio.click()\"\n [attr.aria-label]=\"option.display | syslang | transloco\"\n [class]=\"\n cn(\n 'flex p-0 px-2 leading-7',\n form.get('option')?.value === option.display && 'bg-accent',\n option.hidden && 'hidden',\n option.disabled && 'disabled pointer-events-none text-neutral-300'\n )\n \"\n [attr.aria-hidden]=\"option.disabled\">\n <input\n #radio\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-{{ option.display }}\"\n [attr.disabled]=\"option.disabled ? true : null\"\n [attr.aria-disabled]=\"option.disabled\"\n (click)=\"select()\"\n value=\"{{ option.display }}\" />\n\n <label\n for=\"date-filter-{{ option.display }}\"\n class=\"grow cursor-pointer p-1\">\n {{ option.display | syslang | transloco }}\n </label>\n </li>\n }\n\n @if (allowCustomRange) {\n <li\n role=\"listitem\"\n aria-label=\"open date range picker\"\n class=\"flex px-2 leading-7\"\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\n <input\n type=\"radio\"\n formControlName=\"option\"\n id=\"date-filter-range-dialog\"\n value=\"custom-range\"\n (click)=\"select()\" />\n <div class=\"@container flex grow gap-1 p-1 @max-[340px]:flex-wrap\">\n <div class=\"flex gap-1\">\n <label for=\"datepicker-range-start\" class=\"min-w-10 truncate\">{{\n \"filters.from\" | transloco\n }}</label>\n <input\n id=\"datepicker-range-start\"\n name=\"start\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeFrom()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n <div class=\"flex gap-1\">\n <label\n for=\"datepicker-range-end\"\n class=\"min-w-10 truncate text-right\"\n >{{ \"filters.to\" | transloco }}</label\n >\n <input\n id=\"datepicker-range-end\"\n name=\"end\"\n type=\"text\"\n readonly\n class=\"h-8 max-w-[13ch] min-w-[13ch]\"\n [value]=\"customRangeTo()\"\n (click)=\"selectAndOpenDialog()\" />\n </div>\n </div>\n </li>\n }\n </ul>\n </form>\n</details>\n\n<aggregation-date-range-dialog\n [lang]=\"lang()\"\n [useDateRange]=\"false\"\n [min]=\"form.get('customRange.from')?.value || undefined\"\n [max]=\"form.get('customRange.to')?.value || undefined\"\n (rangeSelected)=\"onRangeSelected($event)\" />\n", styles: [":host{display:block;min-width:200px}ul[role=list]{scrollbar-width:thin}\n"] }]
10176
10190
  }] });
10177
10191
 
10178
10192
  class ArticleEntities {
@@ -12379,6 +12393,7 @@ const COLLECTIONS_CONFIG = new InjectionToken("Collections options", {
12379
12393
  class CollectionsComponent {
12380
12394
  floating = inject(PopoverComponent, { skipSelf: true, optional: true });
12381
12395
  userSettingsStore = inject(UserSettingsStore);
12396
+ queryParamsStore = inject(QueryParamsStore);
12382
12397
  drawerStack = inject(DrawerStackService);
12383
12398
  router = inject(Router);
12384
12399
  options = input(...(ngDevMode ? [undefined, { debugName: "options" }] : []));
@@ -12398,6 +12413,9 @@ class CollectionsComponent {
12398
12413
  }
12399
12414
  onClick(collection) {
12400
12415
  this.drawerStack.closeAll();
12416
+ // Sync the store before navigating: patchState is synchronous while router.navigate is not,
12417
+ // so the destination /search page must already see the basket when it fires its first query.
12418
+ this.queryParamsStore.patch({ basket: collection.name }, undefined, { syncUrl: false });
12401
12419
  this.router.navigate(["/search"], { queryParams: { b: collection.name } });
12402
12420
  }
12403
12421
  onDelete(collection, index, e) {
@@ -12413,16 +12431,7 @@ class CollectionsComponent {
12413
12431
  }
12414
12432
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: CollectionsComponent, decorators: [{
12415
12433
  type: Component,
12416
- args: [{ selector: "app-collections, collections", standalone: true, imports: [
12417
- TranslocoPipe,
12418
- RouterLink,
12419
- DeleteCollectionDialog,
12420
- ButtonComponent,
12421
- ListItemComponent,
12422
- Separator,
12423
- TrashIcon,
12424
- InboxIcon
12425
- ], providers: [provideTranslocoScope("collections")], template: "@if (floating) {\r\n <div class=\"p-2\">\r\n <label class=\"text-xl font-bold\">{{\r\n \"collections.label\" | transloco\r\n }}</label>\r\n <Separator />\r\n </div>\r\n}\r\n\r\n<ul class=\"flex max-h-[460px] flex-col overflow-auto\">\r\n @for (collection of paginatedCollections(); track $index) {\r\n <li\r\n role=\"listitem\"\r\n class=\"group grid grid-cols-[min-content_auto_min-content] items-center\"\r\n tabindex=\"0\"\r\n (click)=\"onClick(collection)\"\r\n (keydown.enter)=\"onClick(collection)\">\r\n <inbox-icon />\r\n\r\n <p class=\"line-clamp-1\">{{ collection.name }}</p>\r\n\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"text-destructive\"\r\n title=\"{{ 'collections.deleteCollection' | transloco }}\"\r\n [attr.aria-label]=\"'collections.deleteCollection' | transloco\"\r\n (click)=\"onDelete(collection, $index, $event)\">\r\n <TrashIcon />\r\n </button>\r\n </li>\r\n } @empty {\r\n <span class=\"py-4 text-center text-neutral-500\">\r\n {{ \"collections.noCollections\" | transloco }}\r\n </span>\r\n }\r\n</ul>\r\n\r\n@if (paginatedCollections().length > 0) {\r\n <div class=\"flex flex-col px-2\">\r\n @if (hasMore() && config.showLoadMore) {\r\n <button\r\n variant=\"outline\"\r\n class=\"w-full\"\r\n tabindex=\"0\"\r\n [attr.title]=\"'loadMore' | transloco\"\r\n (click)=\"loadMore($event)\">\r\n {{ \"loadMore\" | transloco }}\r\n </button>\r\n }\r\n <button\r\n variant=\"primary\"\r\n class=\"ml-auto\"\r\n [attr.title]=\"'seeMore' | transloco\"\r\n [routerLink]=\"[config.routerLink]\">\r\n {{ \"seeMore\" | transloco }}\r\n </button>\r\n </div>\r\n\r\n <delete-collection-dialog />\r\n}\r\n" }]
12434
+ args: [{ selector: "app-collections, collections", standalone: true, imports: [TranslocoPipe, RouterLink, DeleteCollectionDialog, ButtonComponent, ListItemComponent, Separator, TrashIcon, InboxIcon], providers: [provideTranslocoScope("collections")], template: "@if (floating) {\r\n <div class=\"p-2\">\r\n <label class=\"text-xl font-bold\">{{\r\n \"collections.label\" | transloco\r\n }}</label>\r\n <Separator />\r\n </div>\r\n}\r\n\r\n<ul class=\"flex max-h-[460px] flex-col overflow-auto\">\r\n @for (collection of paginatedCollections(); track $index) {\r\n <li\r\n role=\"listitem\"\r\n class=\"group grid grid-cols-[min-content_auto_min-content] items-center\"\r\n tabindex=\"0\"\r\n (click)=\"onClick(collection)\"\r\n (keydown.enter)=\"onClick(collection)\">\r\n <inbox-icon />\r\n\r\n <p class=\"line-clamp-1\">{{ collection.name }}</p>\r\n\r\n <button\r\n variant=\"ghost\"\r\n [iconOnly]=\"true\"\r\n class=\"text-destructive\"\r\n title=\"{{ 'collections.deleteCollection' | transloco }}\"\r\n [attr.aria-label]=\"'collections.deleteCollection' | transloco\"\r\n (click)=\"onDelete(collection, $index, $event)\">\r\n <TrashIcon />\r\n </button>\r\n </li>\r\n } @empty {\r\n <span class=\"py-4 text-center text-neutral-500\">\r\n {{ \"collections.noCollections\" | transloco }}\r\n </span>\r\n }\r\n</ul>\r\n\r\n@if (paginatedCollections().length > 0) {\r\n <div class=\"flex flex-col px-2\">\r\n @if (hasMore() && config.showLoadMore) {\r\n <button\r\n variant=\"outline\"\r\n class=\"w-full\"\r\n tabindex=\"0\"\r\n [attr.title]=\"'loadMore' | transloco\"\r\n (click)=\"loadMore($event)\">\r\n {{ \"loadMore\" | transloco }}\r\n </button>\r\n }\r\n <button\r\n variant=\"primary\"\r\n class=\"ml-auto\"\r\n [attr.title]=\"'seeMore' | transloco\"\r\n [routerLink]=\"[config.routerLink]\">\r\n {{ \"seeMore\" | transloco }}\r\n </button>\r\n </div>\r\n\r\n <delete-collection-dialog />\r\n}\r\n" }]
12426
12435
  }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], deleteCollectionDialog: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DeleteCollectionDialog), { isSignal: true }] }] } });
12427
12436
 
12428
12437
  /** Static catalog of the known boolean feature flags, with human-readable metadata. */
@@ -13249,25 +13258,23 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
13249
13258
  aggregations = signal([], ...(ngDevMode ? [{ debugName: "aggregations" }] : []));
13250
13259
  inputVariant = signal("default", ...(ngDevMode ? [{ debugName: "inputVariant" }] : []));
13251
13260
  tabs = computed(() => {
13252
- const routeData = this.router.config.find((c) => c.path === searchRoute);
13261
+ const routeData = this.router.config.find(c => c.path === searchRoute);
13253
13262
  return !routeData
13254
13263
  ? []
13255
- : [{ path: "all", display: "All" }].concat(routeData
13256
- .children.filter((c) => !!c.path && c.path !== "all" && c.path !== "**")
13257
- .map((c) => ({ path: c.path, display: c.data?.["display"] })));
13264
+ : [{ path: "all", display: "All" }].concat(routeData.children.filter(c => !!c.path && c.path !== "all" && c.path !== "**").map(c => ({ path: c.path, display: c.data?.["display"] })));
13258
13265
  }, ...(ngDevMode ? [{ debugName: "tabs" }] : []));
13259
13266
  filters = computed(() => {
13260
13267
  const cjsonFilters = this.appStore
13261
13268
  .customizationJson()
13262
- .filters?.filter((f) => f.column !== "#date")
13263
- .map((f) => ({
13269
+ .filters?.filter(f => f.column !== "#date")
13270
+ .map(f => ({
13264
13271
  column: f.column,
13265
13272
  alias: this.appStore.getColumnAlias(f.column),
13266
13273
  display: f.display,
13267
- items: this.aggregations()?.find((a) => a.column === f.column)?.items
13274
+ items: this.aggregations()?.find(a => a.column === f.column)?.items
13268
13275
  })) || [];
13269
13276
  const agg = this.aggregations()
13270
- .filter((f) => !cjsonFilters.some((cf) => cf.column === f.column))
13277
+ .filter(f => !cjsonFilters.some(cf => cf.column === f.column))
13271
13278
  .map((f) => ({
13272
13279
  column: f.column,
13273
13280
  alias: this.appStore.getColumnAlias(f.column),
@@ -13336,7 +13343,7 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
13336
13343
  }
13337
13344
  this.queryParamsStore.patch({ text });
13338
13345
  const filters = [];
13339
- this.appliedFilters().forEach((filter) => {
13346
+ this.appliedFilters().forEach(filter => {
13340
13347
  if (filter.values.length) {
13341
13348
  // todo should handle dates? how?
13342
13349
  /* if (filter.column.startsWith('modified') || filter.column.startsWith('date')) {
@@ -13354,7 +13361,7 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
13354
13361
  });
13355
13362
  this.queryParamsStore.clearFilters();
13356
13363
  if (filters.length) {
13357
- filters.forEach((filter) => {
13364
+ filters.forEach(filter => {
13358
13365
  this.queryParamsStore.updateFilter(filter);
13359
13366
  });
13360
13367
  }
@@ -13372,10 +13379,10 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
13372
13379
  const display = values[0].display;
13373
13380
  if (this.appStore.isDateColumn(field)) {
13374
13381
  // dates
13375
- const agg = this.aggregations().find((a) => a.column === field);
13382
+ const agg = this.aggregations().find(a => a.column === field);
13376
13383
  if (agg) {
13377
13384
  const options = translateAggregationToDateOptions(agg, false);
13378
- const option = options.find((o) => !!o.display && value.indexOf(o.display) !== -1);
13385
+ const option = options.find(o => !!o.display && value.indexOf(o.display) !== -1);
13379
13386
  if (option)
13380
13387
  return [
13381
13388
  {
@@ -13393,11 +13400,11 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
13393
13400
  return [{ operator: op, value, field: f }];
13394
13401
  }
13395
13402
  else {
13396
- const agg = this.aggregations().find((a) => a.column === field);
13403
+ const agg = this.aggregations().find(a => a.column === field);
13397
13404
  // if AggregationItem
13398
13405
  if (!!value.match(aggItemRegex)?.[1] && !!agg) {
13399
- const items = agg.items.filter((item) => values.some((v) => item.value === v.value));
13400
- const filters = items.map((item) => parseValueAndOperatorFromItem(item, field));
13406
+ const items = agg.items.filter(item => values.some(v => item.value === v.value));
13407
+ const filters = items.map(item => parseValueAndOperatorFromItem(item, field));
13401
13408
  return [
13402
13409
  {
13403
13410
  operator: "or",
@@ -13415,10 +13422,10 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
13415
13422
  {
13416
13423
  operator,
13417
13424
  name: agg?.name,
13418
- values: values.map((v) => v.value),
13425
+ values: values.map(v => v.value),
13419
13426
  field,
13420
13427
  display: display || value,
13421
- filters: values.map((v) => ({
13428
+ filters: values.map(v => ({
13422
13429
  operator: "contains",
13423
13430
  name: agg?.name,
13424
13431
  field,
@@ -13447,7 +13454,7 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
13447
13454
  // value can be separated by AND or OR keywords, if it's the case each words must be written field:word1 AND field:word2 etc...
13448
13455
  const words = value.split(/\s+(AND|OR)\s+/);
13449
13456
  return words
13450
- .map((w) => {
13457
+ .map(w => {
13451
13458
  if (w === "AND" || w === "OR")
13452
13459
  return w;
13453
13460
  return `(${fieldStr}${w})`;
@@ -13456,7 +13463,7 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
13456
13463
  }
13457
13464
  case "any": {
13458
13465
  const splitWords = value.split(" ");
13459
- return `(${splitWords.map((w) => `${fieldStr}[${w}]`).join(" OR ")})`;
13466
+ return `(${splitWords.map(w => `${fieldStr}[${w}]`).join(" OR ")})`;
13460
13467
  }
13461
13468
  case "none":
13462
13469
  return `${fieldStr}NOT [${value}]`;
@@ -13465,20 +13472,19 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
13465
13472
  }
13466
13473
  }
13467
13474
  getPlaceholder(key) {
13468
- return (this.selectData.find((d) => d.operator === this.form.get(key)?.value)?.placeholder ||
13469
- this.transloco.translate("drawers.searchWords"));
13475
+ return this.selectData.find(d => d.operator === this.form.get(key)?.value)?.placeholder || this.transloco.translate("drawers.searchWords");
13470
13476
  }
13471
13477
  /** Get applied filters for a column */
13472
13478
  getItems(column) {
13473
- return this.appliedFilters().find((f) => f.column === column)?.values || [];
13479
+ return this.appliedFilters().find(f => f.column === column)?.values || [];
13474
13480
  }
13475
13481
  /** add item to current selection */
13476
13482
  addItem(item, filter) {
13477
- const appliedFilter = this.appliedFilters().find((af) => af.column === filter.column);
13483
+ const appliedFilter = this.appliedFilters().find(af => af.column === filter.column);
13478
13484
  if (!appliedFilter) {
13479
13485
  this.appliedFilters.set(this.appliedFilters().concat([{ column: filter.column, values: [item] }]));
13480
13486
  }
13481
- else if (!appliedFilter.values.some((v) => v.value === item.value)) {
13487
+ else if (!appliedFilter.values.some(v => v.value === item.value)) {
13482
13488
  if (this.appStore.isDateColumn(filter.column)) {
13483
13489
  appliedFilter.values = [item]; // only one item for date filters
13484
13490
  }
@@ -13489,9 +13495,9 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
13489
13495
  }
13490
13496
  /** remove item from current selection */
13491
13497
  removeItem(item, filter) {
13492
- const appliedFilter = this.appliedFilters().find((af) => af.column === filter.column);
13498
+ const appliedFilter = this.appliedFilters().find(af => af.column === filter.column);
13493
13499
  if (appliedFilter) {
13494
- appliedFilter.values = appliedFilter.values.filter((v) => v.value !== item.value);
13500
+ appliedFilter.values = appliedFilter.values.filter(v => v.value !== item.value);
13495
13501
  }
13496
13502
  }
13497
13503
  /** Update suggestions on input focus */
@@ -13505,23 +13511,22 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
13505
13511
  }
13506
13512
  setSuggestions(value) {
13507
13513
  this.suggestions.set([]);
13508
- const filter = this.filters().find((f) => f.column === this.currentFilter());
13514
+ const filter = this.filters().find(f => f.column === this.currentFilter());
13509
13515
  if (!!value && !!filter && filter.items?.length) {
13510
- this.suggestions.set(this.getSuggestionItems(value.toLowerCase(), filter.items).map((i) => ({
13516
+ this.suggestions.set(this.getSuggestionItems(value.toLowerCase(), filter.items).map(i => ({
13511
13517
  display: i.display || String(i.value),
13512
13518
  value: String(filter.column === "treepath" ? `/${i.$path || i.value}/*` : i.value)
13513
13519
  })));
13514
13520
  }
13515
13521
  }
13516
13522
  getSuggestionItems(value, items) {
13517
- const res = items.filter((i) => (!!i.display && i.display.toLowerCase().indexOf(value) !== -1) ||
13518
- String(i.value).toLowerCase().indexOf(value) !== -1);
13523
+ const res = items.filter(i => (!!i.display && i.display.toLowerCase().indexOf(value) !== -1) || String(i.value).toLowerCase().indexOf(value) !== -1);
13519
13524
  // todo handle tree items
13520
13525
  return res;
13521
13526
  }
13522
13527
  getWithSubItems(items) {
13523
13528
  const res = [...items];
13524
- items.forEach((item) => {
13529
+ items.forEach(item => {
13525
13530
  if (item.hasChildren && item.items?.length) {
13526
13531
  res.push(...this.getWithSubItems(item.items));
13527
13532
  }
@@ -13529,7 +13534,7 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
13529
13534
  return res;
13530
13535
  }
13531
13536
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: DrawerAdvancedFiltersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13532
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", type: DrawerAdvancedFiltersComponent, isStandalone: true, selector: "advanced-filters", host: { classAttribute: "fixed bg-background grid h-full top-0 -right-full justify-end" }, providers: [DrawerService, SyslangPipe, TranslocoPipe, provideTranslocoScope("drawers")], usesInheritance: true, ngImport: i0, template: "<div (click)=\"drawer.toggleExtension()\" (keydown.escape)=\"drawer.toggleExtension()\" [attr.aria-hidden]=\"true\"></div>\n\n<div class=\"flex h-full flex-col overflow-auto\">\n <DrawerNavbar class=\"border-foreground/10 block border-b\">\n <button [attr.title]=\"'drawers.search' | transloco\" (click)=\"onSearch()\">\n {{ 'drawers.search' | transloco }}\n </button>\n </DrawerNavbar>\n\n <div class=\"flex h-full grow flex-col overflow-auto\">\n <section class=\"flex flex-col gap-4 p-6\" [formGroup]=\"form\">\n <!-- FIND IN -->\n <h1 class=\"text-xl font-bold\">{{ 'drawers.findInContent' | transloco }}</h1>\n <div class=\"flex items-center gap-4\" formGroupName=\"content\">\n <span class=\"w-1/3 font-semibold\">{{ 'drawers.findInContent' | transloco }}</span>\n <select\n id=\"content-operator\"\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 w-full rounded-md border px-2 hover:outline focus:outline\"\n formControlName=\"operator\">\n @for (data of selectData; track $index) {\n <option [value]=\"data.operator\">{{ 'drawers.' + data.display | transloco }}</option>\n }\n </select>\n <input\n [variant]=\"inputVariant()\"\n id=\"content-value\"\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [placeholder]=\"getPlaceholder('content.operator')\"\n formControlName=\"value\" />\n </div>\n @if (enableFieldedSearch()) {\n <div class=\"flex items-center gap-4\" formGroupName=\"title\">\n <span class=\"w-1/3 font-semibold\">{{ 'drawers.findInTitle' | transloco }}</span>\n <select\n id=\"title-operator\"\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 w-full rounded-md border px-2 hover:outline focus:outline\"\n formControlName=\"operator\">\n @for (data of selectData; track $index) {\n <option [value]=\"data.operator\">{{ 'drawers.' + data.display | transloco }}</option>\n }\n </select>\n <input id=\"title-value\" type=\"text\" autocomplete=\"off\" spellcheck=\"false\" [placeholder]=\"getPlaceholder('title.operator')\" formControlName=\"value\" />\n </div>\n }\n\n <!-- TABS -->\n <h1 class=\"mt-4 text-xl font-bold\">{{ 'drawers.inScope' | transloco }}&nbsp;&quot;{{ currentTab() }}&quot;</h1>\n\n <Tabs>\n <TabsList variant=\"ghost\">\n @for (tab of tabs(); track $index) {\n <Tab class=\"w-fit\" [value]=\"tab.path\" [active]=\"tab.path === currentTab()\" (click)=\"onTabChange(tab.path)\">\n {{ tab.display | syslang | transloco }}\n </Tab>\n }\n </TabsList>\n </Tabs>\n\n <!-- FILTERS -->\n <h1 class=\"mt-4 text-xl font-bold\">{{ 'drawers.applyFilters' | transloco }}</h1>\n @for (filter of filters(); track $index) {\n <DropdownInput\n [suggestions]=\"suggestions()\"\n [selected]=\"getItems(filter.column)\"\n [label]=\"filter.display || filter.alias | syslang | transloco\"\n [placeholder]=\"'drawers.startTyping' | transloco\"\n [noResultLabel]=\"'drawers.noResult' | transloco\"\n (onFocus)=\"setFilterFocus($event, filter)\"\n (onKeyUp)=\"onInputTyping($event)\"\n (removeItem)=\"removeItem($event, filter)\"\n (addItem)=\"addItem($event, filter)\" />\n } @empty {\n {{ 'drawers.noFilters' | transloco }}\n }\n </section>\n </div>\n</div>\n", styles: [":host{--drawer-width: 46;--drawer-subdrawer-width: 400px;width:calc(100vw / 100 * var(--drawer-width) + var(--drawer-subdrawer-width));z-index:var(--z-drawer);grid-template-columns:0 1fr var(--drawer-subdrawer-width);transition:right .3s ease-in-out,transform .3s ease-in-out}:host[drawer-opened=true]{right:calc(var(--drawer-subdrawer-width) * -1);box-shadow:var(--drawer-box-shadow)}:host[drawer-extended=true]{width:100vw;right:calc(var(--spacing) * 0);grid-template-columns:1fr calc(var(--drawer-width) * 1%) var(--drawer-subdrawer-width);box-shadow:unset}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { 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: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: DrawerNavbarComponent, selector: "DrawerNavbar, drawernavbar" }, { kind: "component", type: DropdownInputComponent, selector: "dropdown-input, DropdownInput", inputs: ["label", "placeholder", "noResultLabel", "suggestions", "selected"], outputs: ["onFocus", "onKeyUp", "removeItem", "addItem"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { 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: TabsComponent, selector: "tabs, Tabs", inputs: ["class"] }, { kind: "directive", type: TabComponent, selector: "tab, Tab", inputs: ["class", "variant", "size", "noTruncate", "value", "active"], outputs: ["clicked"] }, { kind: "directive", type: TabsListComponent, selector: "tabs-list, TabsList", inputs: ["class", "variant", "size"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
13537
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", type: DrawerAdvancedFiltersComponent, isStandalone: true, selector: "advanced-filters", host: { attributes: { "data-legacy": "" }, classAttribute: "fixed bg-background grid h-full top-0 -right-full justify-end" }, providers: [DrawerService, SyslangPipe, TranslocoPipe, provideTranslocoScope("drawers")], usesInheritance: true, ngImport: i0, template: "<div (click)=\"drawer.toggleExtension()\" (keydown.escape)=\"drawer.toggleExtension()\" [attr.aria-hidden]=\"true\"></div>\n\n<div class=\"flex h-full flex-col overflow-auto\">\n <DrawerNavbar class=\"border-foreground/10 block border-b\">\n <button [attr.title]=\"'drawers.search' | transloco\" (click)=\"onSearch()\">\n {{ 'drawers.search' | transloco }}\n </button>\n </DrawerNavbar>\n\n <div class=\"flex h-full grow flex-col overflow-auto\">\n <section class=\"flex flex-col gap-4 p-6\" [formGroup]=\"form\">\n <!-- FIND IN -->\n <h1 class=\"text-xl font-bold\">{{ 'drawers.findInContent' | transloco }}</h1>\n <div class=\"flex items-center gap-4\" formGroupName=\"content\">\n <span class=\"w-1/3 font-semibold\">{{ 'drawers.findInContent' | transloco }}</span>\n <select\n id=\"content-operator\"\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 w-full rounded-md border px-2 hover:outline focus:outline\"\n formControlName=\"operator\">\n @for (data of selectData; track $index) {\n <option [value]=\"data.operator\">{{ 'drawers.' + data.display | transloco }}</option>\n }\n </select>\n <input\n [variant]=\"inputVariant()\"\n id=\"content-value\"\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [placeholder]=\"getPlaceholder('content.operator')\"\n formControlName=\"value\" />\n </div>\n @if (enableFieldedSearch()) {\n <div class=\"flex items-center gap-4\" formGroupName=\"title\">\n <span class=\"w-1/3 font-semibold\">{{ 'drawers.findInTitle' | transloco }}</span>\n <select\n id=\"title-operator\"\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 w-full rounded-md border px-2 hover:outline focus:outline\"\n formControlName=\"operator\">\n @for (data of selectData; track $index) {\n <option [value]=\"data.operator\">{{ 'drawers.' + data.display | transloco }}</option>\n }\n </select>\n <input id=\"title-value\" type=\"text\" autocomplete=\"off\" spellcheck=\"false\" [placeholder]=\"getPlaceholder('title.operator')\" formControlName=\"value\" />\n </div>\n }\n\n <!-- TABS -->\n <h1 class=\"mt-4 text-xl font-bold\">{{ 'drawers.inScope' | transloco }}&nbsp;&quot;{{ currentTab() }}&quot;</h1>\n\n <Tabs>\n <TabsList variant=\"ghost\">\n @for (tab of tabs(); track $index) {\n <Tab class=\"w-fit\" [value]=\"tab.path\" [active]=\"tab.path === currentTab()\" (click)=\"onTabChange(tab.path)\">\n {{ tab.display | syslang | transloco }}\n </Tab>\n }\n </TabsList>\n </Tabs>\n\n <!-- FILTERS -->\n <h1 class=\"mt-4 text-xl font-bold\">{{ 'drawers.applyFilters' | transloco }}</h1>\n @for (filter of filters(); track $index) {\n <DropdownInput\n [suggestions]=\"suggestions()\"\n [selected]=\"getItems(filter.column)\"\n [label]=\"filter.display || filter.alias | syslang | transloco\"\n [placeholder]=\"'drawers.startTyping' | transloco\"\n [noResultLabel]=\"'drawers.noResult' | transloco\"\n (onFocus)=\"setFilterFocus($event, filter)\"\n (onKeyUp)=\"onInputTyping($event)\"\n (removeItem)=\"removeItem($event, filter)\"\n (addItem)=\"addItem($event, filter)\" />\n } @empty {\n {{ 'drawers.noFilters' | transloco }}\n }\n </section>\n </div>\n</div>\n", styles: [":host{--drawer-width: 46;--drawer-subdrawer-width: 400px;width:calc(100vw / 100 * var(--drawer-width) + var(--drawer-subdrawer-width));z-index:var(--z-drawer);grid-template-columns:0 1fr var(--drawer-subdrawer-width);transition:right .3s ease-in-out,transform .3s ease-in-out}:host[drawer-opened=true]{right:calc(var(--drawer-subdrawer-width) * -1);box-shadow:var(--drawer-box-shadow)}:host[drawer-extended=true]{width:100vw;right:calc(var(--spacing) * 0);grid-template-columns:1fr calc(var(--drawer-width) * 1%) var(--drawer-subdrawer-width);box-shadow:unset}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { 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.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { 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: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: DrawerNavbarComponent, selector: "DrawerNavbar, drawernavbar" }, { kind: "component", type: DropdownInputComponent, selector: "dropdown-input, DropdownInput", inputs: ["label", "placeholder", "noResultLabel", "suggestions", "selected"], outputs: ["onFocus", "onKeyUp", "removeItem", "addItem"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { 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: TabsComponent, selector: "tabs, Tabs", inputs: ["class"] }, { kind: "directive", type: TabComponent, selector: "tab, Tab", inputs: ["class", "variant", "size", "noTruncate", "value", "active"], outputs: ["clicked"] }, { kind: "directive", type: TabsListComponent, selector: "tabs-list, TabsList", inputs: ["class", "variant", "size"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
13533
13538
  }
13534
13539
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: DrawerAdvancedFiltersComponent, decorators: [{
13535
13540
  type: Component,
@@ -13545,7 +13550,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImpo
13545
13550
  TabComponent,
13546
13551
  TabsListComponent
13547
13552
  ], providers: [DrawerService, SyslangPipe, TranslocoPipe, provideTranslocoScope("drawers")], host: {
13548
- class: "fixed bg-background grid h-full top-0 -right-full justify-end"
13553
+ class: "fixed bg-background grid h-full top-0 -right-full justify-end",
13554
+ "data-legacy": ""
13549
13555
  }, template: "<div (click)=\"drawer.toggleExtension()\" (keydown.escape)=\"drawer.toggleExtension()\" [attr.aria-hidden]=\"true\"></div>\n\n<div class=\"flex h-full flex-col overflow-auto\">\n <DrawerNavbar class=\"border-foreground/10 block border-b\">\n <button [attr.title]=\"'drawers.search' | transloco\" (click)=\"onSearch()\">\n {{ 'drawers.search' | transloco }}\n </button>\n </DrawerNavbar>\n\n <div class=\"flex h-full grow flex-col overflow-auto\">\n <section class=\"flex flex-col gap-4 p-6\" [formGroup]=\"form\">\n <!-- FIND IN -->\n <h1 class=\"text-xl font-bold\">{{ 'drawers.findInContent' | transloco }}</h1>\n <div class=\"flex items-center gap-4\" formGroupName=\"content\">\n <span class=\"w-1/3 font-semibold\">{{ 'drawers.findInContent' | transloco }}</span>\n <select\n id=\"content-operator\"\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 w-full rounded-md border px-2 hover:outline focus:outline\"\n formControlName=\"operator\">\n @for (data of selectData; track $index) {\n <option [value]=\"data.operator\">{{ 'drawers.' + data.display | transloco }}</option>\n }\n </select>\n <input\n [variant]=\"inputVariant()\"\n id=\"content-value\"\n type=\"text\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n [placeholder]=\"getPlaceholder('content.operator')\"\n formControlName=\"value\" />\n </div>\n @if (enableFieldedSearch()) {\n <div class=\"flex items-center gap-4\" formGroupName=\"title\">\n <span class=\"w-1/3 font-semibold\">{{ 'drawers.findInTitle' | transloco }}</span>\n <select\n id=\"title-operator\"\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 w-full rounded-md border px-2 hover:outline focus:outline\"\n formControlName=\"operator\">\n @for (data of selectData; track $index) {\n <option [value]=\"data.operator\">{{ 'drawers.' + data.display | transloco }}</option>\n }\n </select>\n <input id=\"title-value\" type=\"text\" autocomplete=\"off\" spellcheck=\"false\" [placeholder]=\"getPlaceholder('title.operator')\" formControlName=\"value\" />\n </div>\n }\n\n <!-- TABS -->\n <h1 class=\"mt-4 text-xl font-bold\">{{ 'drawers.inScope' | transloco }}&nbsp;&quot;{{ currentTab() }}&quot;</h1>\n\n <Tabs>\n <TabsList variant=\"ghost\">\n @for (tab of tabs(); track $index) {\n <Tab class=\"w-fit\" [value]=\"tab.path\" [active]=\"tab.path === currentTab()\" (click)=\"onTabChange(tab.path)\">\n {{ tab.display | syslang | transloco }}\n </Tab>\n }\n </TabsList>\n </Tabs>\n\n <!-- FILTERS -->\n <h1 class=\"mt-4 text-xl font-bold\">{{ 'drawers.applyFilters' | transloco }}</h1>\n @for (filter of filters(); track $index) {\n <DropdownInput\n [suggestions]=\"suggestions()\"\n [selected]=\"getItems(filter.column)\"\n [label]=\"filter.display || filter.alias | syslang | transloco\"\n [placeholder]=\"'drawers.startTyping' | transloco\"\n [noResultLabel]=\"'drawers.noResult' | transloco\"\n (onFocus)=\"setFilterFocus($event, filter)\"\n (onKeyUp)=\"onInputTyping($event)\"\n (removeItem)=\"removeItem($event, filter)\"\n (addItem)=\"addItem($event, filter)\" />\n } @empty {\n {{ 'drawers.noFilters' | transloco }}\n }\n </section>\n </div>\n</div>\n", styles: [":host{--drawer-width: 46;--drawer-subdrawer-width: 400px;width:calc(100vw / 100 * var(--drawer-width) + var(--drawer-subdrawer-width));z-index:var(--z-drawer);grid-template-columns:0 1fr var(--drawer-subdrawer-width);transition:right .3s ease-in-out,transform .3s ease-in-out}:host[drawer-opened=true]{right:calc(var(--drawer-subdrawer-width) * -1);box-shadow:var(--drawer-box-shadow)}:host[drawer-extended=true]{width:100vw;right:calc(var(--spacing) * 0);grid-template-columns:1fr calc(var(--drawer-width) * 1%) var(--drawer-subdrawer-width);box-shadow:unset}\n"] }]
13550
13556
  }], ctorParameters: () => [] });
13551
13557
 
@@ -14037,7 +14043,7 @@ class AdvancedSearchComponent {
14037
14043
  const { queryText } = getState(this.selectionStore);
14038
14044
  return queryText;
14039
14045
  },
14040
- computation: (queryText) => {
14046
+ computation: queryText => {
14041
14047
  const text = queryText || "";
14042
14048
  return text ? text.trim() : "";
14043
14049
  } }] : [{
@@ -14045,7 +14051,7 @@ class AdvancedSearchComponent {
14045
14051
  const { queryText } = getState(this.selectionStore);
14046
14052
  return queryText;
14047
14053
  },
14048
- computation: (queryText) => {
14054
+ computation: queryText => {
14049
14055
  const text = queryText || "";
14050
14056
  return text ? text.trim() : "";
14051
14057
  }
@@ -14058,21 +14064,13 @@ class AdvancedSearchComponent {
14058
14064
  this.queryText.set("");
14059
14065
  }
14060
14066
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: AdvancedSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14061
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", type: AdvancedSearchComponent, isStandalone: true, selector: "advanced-search", inputs: { article: { classPropertyName: "article", publicName: "article", isSignal: true, isRequired: true, transformFunction: null }, previewStrategy: { classPropertyName: "previewStrategy", publicName: "previewStrategy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selected" }, providers: [provideTranslocoScope("drawers")], ngImport: i0, template: "@if (article()) {\r\n <div\r\n [class]=\"\r\n cn(\r\n 'scroll-stable flex flex-col overflow-y-auto pr-3 pb-6 pl-6',\r\n '[&_details]:border-muted-foreground/18 [&_details]:border-b',\r\n '[&_details>summary]:bg-menu [&_details_details]:last:border-b-0',\r\n '[&_details>summary>i>svg]:transition-transform [&_details>summary>i>svg]:duration-300',\r\n '[&>details>summary>i>svg]:group-open/parent:rotate-180'\r\n )\r\n \">\r\n <section class=\"mt-6 mb-4 flex w-full flex-col gap-6\">\r\n <header\r\n id=\"panel-control\"\r\n class=\"focus-within:outline-primary not-focus-within:hover:outline-muted-foreground group flex rounded focus-within:outline hover:outline\">\r\n <div\r\n class=\"bg-muted flex grow items-center gap-2 rounded-s rounded-e-none px-3\">\r\n <input\r\n id=\"advanced-search-input\"\r\n type=\"search\"\r\n class=\"grow focus:outline-none\"\r\n autocomplete=\"off\"\r\n spellcheck=\"false\"\r\n [attr.placeholder]=\"'drawers.inputPlaceholder' | transloco\"\r\n [attr.aria-label]=\"'drawers.inputPlaceholder' | transloco\"\r\n [ngModel]=\"queryText()\"\r\n (ngModelChange)=\"queryText.set($event)\"\r\n (keydown.enter)=\"executeSearch()\" />\r\n\r\n @if (queryText() !== \"\") {\r\n <button\r\n [iconOnly]=\"true\"\r\n size=\"sm\"\r\n [attr.title]=\"'clear' | transloco\"\r\n [attr.aria-label]=\"'clear' | transloco\"\r\n (click)=\"clearInput()\"\r\n (keydown.enter)=\"clearInput()\">\r\n <xmark-icon />\r\n </button>\r\n }\r\n </div>\r\n\r\n <button\r\n class=\"size-8 rounded-s-none rounded-e-lg\"\r\n [disabled]=\"queryText() === '' || null\"\r\n [attr.title]=\"'search' | transloco\"\r\n [attr.aria-label]=\"'search' | transloco\"\r\n (click)=\"executeSearch()\">\r\n <magnifying-glass-icon />\r\n </button>\r\n </header>\r\n </section>\r\n\r\n <!-- the same name is used everywhere to simulate an accordion -->\r\n <!-- extracts -->\r\n <ExtractsPanel [article]=\"article()\" />\r\n\r\n <!-- labels -->\r\n <LabelsPanel [article]=\"article()\" />\r\n\r\n <!-- entities -->\r\n <EntitiesPanel [article]=\"article()\" />\r\n\r\n <!-- similar documents -->\r\n <SimilarDocumentsPanel\r\n [article]=\"article()\"\r\n [strategy]=\"previewStrategy()\"\r\n (selected)=\"selected.emit($event)\" />\r\n </div>\r\n}\r\n", styles: [":host{--panel-max-height: none}input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "component", type: SimilarDocumentsPanel, selector: "similar-documents-panel, SimilarDocumentsPanel, similardocumentspanel", inputs: ["article", "strategy"], outputs: ["selected"] }, { kind: "component", type: EntitiesPanel, selector: "entities-panel,EntitiesPanel,entitiesPanel", inputs: ["article"] }, { kind: "component", type: LabelsPanel, selector: "labels-panel, LabelsPanel, labelspanel", inputs: ["article"] }, { kind: "component", type: ExtractsPanel, selector: "extracts-panel, ExtractsPanel, extractspanel", inputs: ["article"] }, { kind: "component", type: XMarkIcon, selector: "XMarkIcon, xmark-icon, x-mark-icon", inputs: ["class"] }, { kind: "component", type: MagnifyingGlassIcon, selector: "magnifying-glass-icon, MagnifyingGlassIcon", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
14067
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.29", type: AdvancedSearchComponent, isStandalone: true, selector: "advanced-search", inputs: { article: { classPropertyName: "article", publicName: "article", isSignal: true, isRequired: true, transformFunction: null }, previewStrategy: { classPropertyName: "previewStrategy", publicName: "previewStrategy", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selected" }, host: { attributes: { "data-legacy": "" } }, providers: [provideTranslocoScope("drawers")], ngImport: i0, template: "@if (article()) {\r\n <div\r\n [class]=\"\r\n cn(\r\n 'scroll-stable flex flex-col overflow-y-auto pr-3 pb-6 pl-6',\r\n '[&_details]:border-muted-foreground/18 [&_details]:border-b',\r\n '[&_details>summary]:bg-menu [&_details_details]:last:border-b-0',\r\n '[&_details>summary>i>svg]:transition-transform [&_details>summary>i>svg]:duration-300',\r\n '[&>details>summary>i>svg]:group-open/parent:rotate-180'\r\n )\r\n \">\r\n <section class=\"mt-6 mb-4 flex w-full flex-col gap-6\">\r\n <header\r\n id=\"panel-control\"\r\n class=\"focus-within:outline-primary not-focus-within:hover:outline-muted-foreground group flex rounded focus-within:outline hover:outline\">\r\n <div\r\n class=\"bg-muted flex grow items-center gap-2 rounded-s rounded-e-none px-3\">\r\n <input\r\n id=\"advanced-search-input\"\r\n type=\"search\"\r\n class=\"grow focus:outline-none\"\r\n autocomplete=\"off\"\r\n spellcheck=\"false\"\r\n [attr.placeholder]=\"'drawers.inputPlaceholder' | transloco\"\r\n [attr.aria-label]=\"'drawers.inputPlaceholder' | transloco\"\r\n [ngModel]=\"queryText()\"\r\n (ngModelChange)=\"queryText.set($event)\"\r\n (keydown.enter)=\"executeSearch()\" />\r\n\r\n @if (queryText() !== \"\") {\r\n <button\r\n [iconOnly]=\"true\"\r\n size=\"sm\"\r\n [attr.title]=\"'clear' | transloco\"\r\n [attr.aria-label]=\"'clear' | transloco\"\r\n (click)=\"clearInput()\"\r\n (keydown.enter)=\"clearInput()\">\r\n <xmark-icon />\r\n </button>\r\n }\r\n </div>\r\n\r\n <button\r\n class=\"size-8 rounded-s-none rounded-e-lg\"\r\n [disabled]=\"queryText() === '' || null\"\r\n [attr.title]=\"'search' | transloco\"\r\n [attr.aria-label]=\"'search' | transloco\"\r\n (click)=\"executeSearch()\">\r\n <magnifying-glass-icon />\r\n </button>\r\n </header>\r\n </section>\r\n\r\n <!-- the same name is used everywhere to simulate an accordion -->\r\n <!-- extracts -->\r\n <ExtractsPanel [article]=\"article()\" />\r\n\r\n <!-- labels -->\r\n <LabelsPanel [article]=\"article()\" />\r\n\r\n <!-- entities -->\r\n <EntitiesPanel [article]=\"article()\" />\r\n\r\n <!-- similar documents -->\r\n <SimilarDocumentsPanel\r\n [article]=\"article()\"\r\n [strategy]=\"previewStrategy()\"\r\n (selected)=\"selected.emit($event)\" />\r\n </div>\r\n}\r\n", styles: [":host{--panel-max-height: none}input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "component", type: SimilarDocumentsPanel, selector: "similar-documents-panel, SimilarDocumentsPanel, similardocumentspanel", inputs: ["article", "strategy"], outputs: ["selected"] }, { kind: "component", type: EntitiesPanel, selector: "entities-panel,EntitiesPanel,entitiesPanel", inputs: ["article"] }, { kind: "component", type: LabelsPanel, selector: "labels-panel, LabelsPanel, labelspanel", inputs: ["article"] }, { kind: "component", type: ExtractsPanel, selector: "extracts-panel, ExtractsPanel, extractspanel", inputs: ["article"] }, { kind: "component", type: XMarkIcon, selector: "XMarkIcon, xmark-icon, x-mark-icon", inputs: ["class"] }, { kind: "component", type: MagnifyingGlassIcon, selector: "magnifying-glass-icon, MagnifyingGlassIcon", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
14062
14068
  }
14063
14069
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.29", ngImport: i0, type: AdvancedSearchComponent, decorators: [{
14064
14070
  type: Component,
14065
- args: [{ selector: "advanced-search", standalone: true, providers: [provideTranslocoScope("drawers")], imports: [
14066
- FormsModule,
14067
- ButtonComponent,
14068
- TranslocoPipe,
14069
- SimilarDocumentsPanel,
14070
- EntitiesPanel,
14071
- LabelsPanel,
14072
- ExtractsPanel,
14073
- XMarkIcon,
14074
- MagnifyingGlassIcon
14075
- ], template: "@if (article()) {\r\n <div\r\n [class]=\"\r\n cn(\r\n 'scroll-stable flex flex-col overflow-y-auto pr-3 pb-6 pl-6',\r\n '[&_details]:border-muted-foreground/18 [&_details]:border-b',\r\n '[&_details>summary]:bg-menu [&_details_details]:last:border-b-0',\r\n '[&_details>summary>i>svg]:transition-transform [&_details>summary>i>svg]:duration-300',\r\n '[&>details>summary>i>svg]:group-open/parent:rotate-180'\r\n )\r\n \">\r\n <section class=\"mt-6 mb-4 flex w-full flex-col gap-6\">\r\n <header\r\n id=\"panel-control\"\r\n class=\"focus-within:outline-primary not-focus-within:hover:outline-muted-foreground group flex rounded focus-within:outline hover:outline\">\r\n <div\r\n class=\"bg-muted flex grow items-center gap-2 rounded-s rounded-e-none px-3\">\r\n <input\r\n id=\"advanced-search-input\"\r\n type=\"search\"\r\n class=\"grow focus:outline-none\"\r\n autocomplete=\"off\"\r\n spellcheck=\"false\"\r\n [attr.placeholder]=\"'drawers.inputPlaceholder' | transloco\"\r\n [attr.aria-label]=\"'drawers.inputPlaceholder' | transloco\"\r\n [ngModel]=\"queryText()\"\r\n (ngModelChange)=\"queryText.set($event)\"\r\n (keydown.enter)=\"executeSearch()\" />\r\n\r\n @if (queryText() !== \"\") {\r\n <button\r\n [iconOnly]=\"true\"\r\n size=\"sm\"\r\n [attr.title]=\"'clear' | transloco\"\r\n [attr.aria-label]=\"'clear' | transloco\"\r\n (click)=\"clearInput()\"\r\n (keydown.enter)=\"clearInput()\">\r\n <xmark-icon />\r\n </button>\r\n }\r\n </div>\r\n\r\n <button\r\n class=\"size-8 rounded-s-none rounded-e-lg\"\r\n [disabled]=\"queryText() === '' || null\"\r\n [attr.title]=\"'search' | transloco\"\r\n [attr.aria-label]=\"'search' | transloco\"\r\n (click)=\"executeSearch()\">\r\n <magnifying-glass-icon />\r\n </button>\r\n </header>\r\n </section>\r\n\r\n <!-- the same name is used everywhere to simulate an accordion -->\r\n <!-- extracts -->\r\n <ExtractsPanel [article]=\"article()\" />\r\n\r\n <!-- labels -->\r\n <LabelsPanel [article]=\"article()\" />\r\n\r\n <!-- entities -->\r\n <EntitiesPanel [article]=\"article()\" />\r\n\r\n <!-- similar documents -->\r\n <SimilarDocumentsPanel\r\n [article]=\"article()\"\r\n [strategy]=\"previewStrategy()\"\r\n (selected)=\"selected.emit($event)\" />\r\n </div>\r\n}\r\n", styles: [":host{--panel-max-height: none}input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none}\n"] }]
14071
+ args: [{ selector: "advanced-search", standalone: true, providers: [provideTranslocoScope("drawers")], imports: [FormsModule, ButtonComponent, TranslocoPipe, SimilarDocumentsPanel, EntitiesPanel, LabelsPanel, ExtractsPanel, XMarkIcon, MagnifyingGlassIcon], host: {
14072
+ "data-legacy": ""
14073
+ }, template: "@if (article()) {\r\n <div\r\n [class]=\"\r\n cn(\r\n 'scroll-stable flex flex-col overflow-y-auto pr-3 pb-6 pl-6',\r\n '[&_details]:border-muted-foreground/18 [&_details]:border-b',\r\n '[&_details>summary]:bg-menu [&_details_details]:last:border-b-0',\r\n '[&_details>summary>i>svg]:transition-transform [&_details>summary>i>svg]:duration-300',\r\n '[&>details>summary>i>svg]:group-open/parent:rotate-180'\r\n )\r\n \">\r\n <section class=\"mt-6 mb-4 flex w-full flex-col gap-6\">\r\n <header\r\n id=\"panel-control\"\r\n class=\"focus-within:outline-primary not-focus-within:hover:outline-muted-foreground group flex rounded focus-within:outline hover:outline\">\r\n <div\r\n class=\"bg-muted flex grow items-center gap-2 rounded-s rounded-e-none px-3\">\r\n <input\r\n id=\"advanced-search-input\"\r\n type=\"search\"\r\n class=\"grow focus:outline-none\"\r\n autocomplete=\"off\"\r\n spellcheck=\"false\"\r\n [attr.placeholder]=\"'drawers.inputPlaceholder' | transloco\"\r\n [attr.aria-label]=\"'drawers.inputPlaceholder' | transloco\"\r\n [ngModel]=\"queryText()\"\r\n (ngModelChange)=\"queryText.set($event)\"\r\n (keydown.enter)=\"executeSearch()\" />\r\n\r\n @if (queryText() !== \"\") {\r\n <button\r\n [iconOnly]=\"true\"\r\n size=\"sm\"\r\n [attr.title]=\"'clear' | transloco\"\r\n [attr.aria-label]=\"'clear' | transloco\"\r\n (click)=\"clearInput()\"\r\n (keydown.enter)=\"clearInput()\">\r\n <xmark-icon />\r\n </button>\r\n }\r\n </div>\r\n\r\n <button\r\n class=\"size-8 rounded-s-none rounded-e-lg\"\r\n [disabled]=\"queryText() === '' || null\"\r\n [attr.title]=\"'search' | transloco\"\r\n [attr.aria-label]=\"'search' | transloco\"\r\n (click)=\"executeSearch()\">\r\n <magnifying-glass-icon />\r\n </button>\r\n </header>\r\n </section>\r\n\r\n <!-- the same name is used everywhere to simulate an accordion -->\r\n <!-- extracts -->\r\n <ExtractsPanel [article]=\"article()\" />\r\n\r\n <!-- labels -->\r\n <LabelsPanel [article]=\"article()\" />\r\n\r\n <!-- entities -->\r\n <EntitiesPanel [article]=\"article()\" />\r\n\r\n <!-- similar documents -->\r\n <SimilarDocumentsPanel\r\n [article]=\"article()\"\r\n [strategy]=\"previewStrategy()\"\r\n (selected)=\"selected.emit($event)\" />\r\n </div>\r\n}\r\n", styles: [":host{--panel-max-height: none}input[type=search]::-webkit-search-cancel-button{-webkit-appearance:none}\n"] }]
14076
14074
  }], propDecorators: { article: [{ type: i0.Input, args: [{ isSignal: true, alias: "article", required: true }] }], previewStrategy: [{ type: i0.Input, args: [{ isSignal: true, alias: "previewStrategy", required: false }] }], selected: [{ type: i0.Output, args: ["selected"] }] } });
14077
14075
 
14078
14076
  const COMPONENTS_FOR_DOCUMENT_TYPE = new InjectionToken("COMPONENTS_FOR_DOCUMENT_TYPE");
@@ -15598,6 +15596,15 @@ class AggregationListComponent {
15598
15596
  const filters = this.queryParamsStore.getFilter({ field: field ?? undefined, name: this.listItemName(item) });
15599
15597
  if (!filters)
15600
15598
  return false;
15599
+ const agg = this.aggregation();
15600
+ if (agg?.isDistribution) {
15601
+ // Same reasoning as `injectAggregationBase.addCurrentFiltersToItems`: a distribution item's
15602
+ // `value` is the full backend expression, not comparable to the applied filter's stored
15603
+ // thresholds — flatten it and compare on `display` (or `value` without `valuesAreExpressions`).
15604
+ const flattenedValues = flattenLegacyFilterValues([filters]);
15605
+ const valueToSearch = agg.valuesAreExpressions ? item.display : item.value;
15606
+ return flattenedValues.includes(valueToSearch?.toString() ?? "");
15607
+ }
15601
15608
  const values = [item.value];
15602
15609
  return values.some(v => v === filters.value) || !!(filters.values?.length && filters.values.some((value) => values.some(v => v === value)));
15603
15610
  }