@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.
- package/README.md +0 -2
- package/document-workspaces/i18n/de.json +229 -0
- package/document-workspaces/i18n/en.json +229 -0
- package/document-workspaces/i18n/fr.json +229 -0
- package/document-workspaces/i18n/ja.json +229 -0
- package/document-workspaces/index.d.ts +2094 -0
- package/elements/index.d.ts +112 -16
- package/fesm2022/sinequa-atomic-angular-document-workspaces.mjs +7979 -0
- package/fesm2022/sinequa-atomic-angular-document-workspaces.mjs.map +1 -0
- package/fesm2022/sinequa-atomic-angular-elements.mjs +4538 -2457
- package/fesm2022/sinequa-atomic-angular-elements.mjs.map +1 -1
- package/fesm2022/sinequa-atomic-angular-workspace-elements.mjs +172 -0
- package/fesm2022/sinequa-atomic-angular-workspace-elements.mjs.map +1 -0
- package/fesm2022/sinequa-atomic-angular.mjs +182 -175
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/index.d.ts +187 -176
- package/package.json +37 -16
- package/workspace-elements/index.d.ts +37 -0
- package/workspace-elements/styles/lexiq-workspaces.css +10224 -0
|
@@ -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
|
-
|
|
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(
|
|
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(
|
|
3256
|
+
}), map(result => {
|
|
3255
3257
|
result.records?.map((article) => Object.assign(article, { value: article.title, type: "default" }));
|
|
3256
3258
|
return result;
|
|
3257
|
-
}), map(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
3314
|
-
results.forEach(
|
|
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(
|
|
3319
|
-
return results.map(
|
|
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(
|
|
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
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
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(
|
|
3358
|
+
error("queryService.getDocPages failure - error: ", err);
|
|
3359
3359
|
return [];
|
|
3360
3360
|
}));
|
|
3361
3361
|
}
|
|
3362
3362
|
/**
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
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(
|
|
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 ?
|
|
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
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
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
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
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() ?
|
|
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(
|
|
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-
|
|
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(
|
|
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-
|
|
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 (
|
|
9479
|
-
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9484
|
-
|
|
9485
|
-
|
|
9486
|
-
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
|
|
9491
|
-
|
|
9492
|
-
|
|
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
|
-
|
|
9506
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
13263
|
-
.map(
|
|
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(
|
|
13274
|
+
items: this.aggregations()?.find(a => a.column === f.column)?.items
|
|
13268
13275
|
})) || [];
|
|
13269
13276
|
const agg = this.aggregations()
|
|
13270
|
-
.filter(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
13400
|
-
const filters = items.map(
|
|
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(
|
|
13425
|
+
values: values.map(v => v.value),
|
|
13419
13426
|
field,
|
|
13420
13427
|
display: display || value,
|
|
13421
|
-
filters: values.map(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
13498
|
+
const appliedFilter = this.appliedFilters().find(af => af.column === filter.column);
|
|
13493
13499
|
if (appliedFilter) {
|
|
13494
|
-
appliedFilter.values = appliedFilter.values.filter(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 }} "{{ currentTab() }}"</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 }} "{{ currentTab() }}"</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 }} "{{ currentTab() }}"</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:
|
|
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:
|
|
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
|
-
|
|
14067
|
-
|
|
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
|
}
|