@sinequa/atomic-angular 0.3.33 → 0.4.19-dev.1
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/features/auth/i18n/de.json +2 -0
- package/features/auth/i18n/en.json +3 -1
- package/features/auth/i18n/fr.json +2 -0
- package/features/user-profile/i18n/de.json +3 -0
- package/features/user-profile/i18n/en.json +3 -0
- package/features/user-profile/i18n/fr.json +3 -0
- package/fesm2022/sinequa-atomic-angular.mjs +1638 -851
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/index.d.ts +5232 -3598
- package/package.json +1 -1
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, inject, HostBinding, Component, Pipe, InjectionToken, computed, ChangeDetectorRef, DestroyRef, LOCALE_ID, Inject, Optional, input, output, signal, effect, assertInInjectionContext, runInInjectionContext, Injector, EventEmitter, Directive, viewChild, ElementRef, afterNextRender, untracked, linkedSignal, model, TemplateRef, HostListener, Renderer2, contentChildren, contentChild, booleanAttribute, resource, ViewContainerRef,
|
|
2
|
+
import { Injectable, inject, HostBinding, Component, Pipe, InjectionToken, computed, ChangeDetectorRef, DestroyRef, LOCALE_ID, Inject, Optional, input, output, signal, effect, assertInInjectionContext, runInInjectionContext, Injector, EventEmitter, Directive, viewChild, ElementRef, afterNextRender, untracked, linkedSignal, model, TemplateRef, HostListener, Renderer2, contentChildren, contentChild, booleanAttribute, resource, ViewContainerRef, viewChildren, numberAttribute, afterEveryRender } from '@angular/core';
|
|
3
3
|
import { BehaviorSubject, Subscription, catchError, EMPTY, firstValueFrom, map, Subject, of, tap, throwError, filter, shareReplay, fromEvent, debounceTime, from, switchMap } from 'rxjs';
|
|
4
4
|
import { TranslocoService, TranslocoPipe, provideTranslocoScope } from '@jsverse/transloco';
|
|
5
|
-
import { DropdownComponent, DropdownContentComponent, InputComponent, ButtonComponent, cn, EllipsisIcon, ChevronRightIcon, MenuComponent, MenuContentComponent, MenuItemComponent, BadgeComponent, DialogComponent, DialogHeaderComponent, DialogTitleComponent, DialogContentComponent, DialogFooterComponent, ListItemComponent, SwitchComponent, SelectOptionDirective, DialogService, TabsComponent, TabsListComponent, TabComponent, ChevronLeftIconComponent, ChevronsLeftIconComponent, ChevronsRightIconComponent, Separator, SheetCloseDirective, SheetService, LoadingCircleIconComponent, CircleCheckIconComponent, PopoverComponent, HorizontalDividerComponent, CardComponent, CardHeaderComponent, CardContentComponent, CardFooterComponent,
|
|
5
|
+
import { DropdownComponent, DropdownContentComponent, InputComponent, ButtonComponent, cn, EllipsisIcon, ChevronRightIcon, MenuComponent, MenuContentComponent, MenuItemComponent, BadgeComponent, DialogComponent, DialogHeaderComponent, DialogTitleComponent, DialogContentComponent, DialogFooterComponent, ListItemComponent, SwitchComponent, SelectOptionDirective, DialogService, TabsComponent, TabsListComponent, TabComponent, ChevronLeftIconComponent, ChevronsLeftIconComponent, ChevronsRightIconComponent, Separator, SheetCloseDirective, SheetService, ButtonGroup, InputGroupInput, InputGroupComponent, InputGroupAddonComponent, SearchIcon, FilterIcon, DateRangePickerDirective, LoadingCircleIconComponent, CircleCheckIconComponent, PopoverComponent, HorizontalDividerComponent, CardComponent, CardHeaderComponent, CardContentComponent, CardFooterComponent, PopoverContentComponent, BookmarkIcon, UserIcon, TrashIcon, FolderIcon, VerticalDividerComponent, BreakpointObserverService, FlagEnglishIconComponent, FlagFrenchIconComponent, EditIcon, UndoIcon, AvatarComponent, AvatarFallbackComponent, AvatarImageComponent } from '@sinequa/ui';
|
|
6
6
|
import highlightWords from 'highlight-words';
|
|
7
7
|
import { ActivatedRoute, Router, NavigationEnd, RouterLink, 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 { globalConfig, EngineType, extraColumns, sysLang, getQueryParamsFromUrl, logout, login, info, warn, notify, error, setGlobalConfig, addConcepts, queryParamsFromUrl, patchUserSettings, deleteUserSettings, fetchUserSettings, buildPathsAndLevels, escapeExpr, isAuthenticated, isExpired, fetchSuggest, isObject, Audit, getMetadata, bisect, isNotInputEvent, fetchSponsoredLinks, fetchQuery, translateAggregationToDateOptions, aggItemRegex, parseValueAndOperatorFromItem,
|
|
11
|
-
import { HttpClient, HttpParams, httpResource, HttpResponse, HttpHeaders } from '@angular/common/http';
|
|
10
|
+
import { globalConfig, EngineType, extraColumns, sysLang, getQueryParamsFromUrl, logout, login, info, warn, notify, error, setGlobalConfig, addConcepts, queryParamsFromUrl, patchUserSettings, deleteUserSettings, fetchUserSettings, buildPathsAndLevels, escapeExpr, isAuthenticated, isExpired, debug, fetchSuggest, isObject, Audit, getMetadata, bisect, isNotInputEvent, fetchSponsoredLinks, fetchQuery, translateAggregationToDateOptions, aggItemRegex, parseValueAndOperatorFromItem, fetchSuggestField, fetchSimilarDocuments, fetchChangePassword, fetchSendPasswordResetEmail, expiresSoon, suggestionsToTreeAggregationNodes, labels, fetchLabels, guid, getRelativeDate, createUserProfile, deleteUserProfileProperty, patchUserProfile, isJsonable, addAuditAdditionalInfo, getToken, setToken, createHeaders } from '@sinequa/atomic';
|
|
11
|
+
import { HttpClient, HttpParams, httpResource, HttpResponse, HttpHeaders, HttpContextToken } from '@angular/common/http';
|
|
12
12
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
13
13
|
import { DatePipe, DATE_PIPE_DEFAULT_TIMEZONE, DATE_PIPE_DEFAULT_OPTIONS, Location, NgTemplateOutlet, NgStyle, NgClass, NgComponentOutlet } from '@angular/common';
|
|
14
14
|
import { Title, DomSanitizer } from '@angular/platform-browser';
|
|
15
15
|
import { cva } from 'class-variance-authority';
|
|
16
16
|
import * as i1 from '@angular/forms';
|
|
17
17
|
import { FormsModule, NonNullableFormBuilder, ReactiveFormsModule, FormGroup, FormControl } from '@angular/forms';
|
|
18
|
+
import { injectVirtualizer } from '@tanstack/angular-virtual';
|
|
18
19
|
import * as i1$1 from '@angular/cdk/drag-drop';
|
|
19
20
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
|
20
21
|
import * as i2 from '@angular/cdk/a11y';
|
|
@@ -321,7 +322,7 @@ function withAppFeatures() {
|
|
|
321
322
|
}
|
|
322
323
|
});
|
|
323
324
|
// Also include columns from the default index "_"
|
|
324
|
-
const schema = app.indexes._
|
|
325
|
+
const schema = app.indexes._?.columns;
|
|
325
326
|
if (schema) {
|
|
326
327
|
Object.keys(schema).forEach((schemaKey) => {
|
|
327
328
|
const col = schema[schemaKey.toLocaleLowerCase()];
|
|
@@ -335,29 +336,29 @@ function withAppFeatures() {
|
|
|
335
336
|
});
|
|
336
337
|
}
|
|
337
338
|
});
|
|
339
|
+
// Include columns from all queries
|
|
340
|
+
Object.keys(app.queries).forEach((queryKey) => {
|
|
341
|
+
const query = app.queries[queryKey];
|
|
342
|
+
if (query.columnsInfo?.columns) {
|
|
343
|
+
Object.values(query.columnsInfo.columns).forEach((c) => {
|
|
344
|
+
const col = schema[c.name.toLocaleLowerCase()];
|
|
345
|
+
if (!col)
|
|
346
|
+
return;
|
|
347
|
+
columnMap[c.name.toLocaleLowerCase()] = col;
|
|
348
|
+
// Add aliases to the map
|
|
349
|
+
if (c.aliases) {
|
|
350
|
+
c.aliases
|
|
351
|
+
.split(/[,;|]/)
|
|
352
|
+
.map((a) => a.trim())
|
|
353
|
+
.forEach((alias) => {
|
|
354
|
+
columnMap[alias.toLocaleLowerCase()] = col;
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
patchState(store, { columnMap });
|
|
338
361
|
}
|
|
339
|
-
// Include columns from all queries
|
|
340
|
-
Object.keys(app.queries).forEach((queryKey) => {
|
|
341
|
-
const query = app.queries[queryKey];
|
|
342
|
-
if (query.columnsInfo?.columns) {
|
|
343
|
-
Object.values(query.columnsInfo.columns).forEach((c) => {
|
|
344
|
-
const col = schema[c.name.toLocaleLowerCase()];
|
|
345
|
-
if (!col)
|
|
346
|
-
return;
|
|
347
|
-
columnMap[c.name.toLocaleLowerCase()] = col;
|
|
348
|
-
// Add aliases to the map
|
|
349
|
-
if (c.aliases) {
|
|
350
|
-
c.aliases
|
|
351
|
-
.split(/[,;|]/)
|
|
352
|
-
.map((a) => a.trim())
|
|
353
|
-
.forEach((alias) => {
|
|
354
|
-
columnMap[alias.toLocaleLowerCase()] = col;
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
patchState(store, { columnMap });
|
|
361
362
|
})));
|
|
362
363
|
},
|
|
363
364
|
/**
|
|
@@ -373,7 +374,7 @@ function withAppFeatures() {
|
|
|
373
374
|
/**
|
|
374
375
|
* Updates the application state with the provided CCApp object.
|
|
375
376
|
*
|
|
376
|
-
* @param {
|
|
377
|
+
* @param {Partial<CCAppState>} app - The application object containing the new state values.
|
|
377
378
|
*/
|
|
378
379
|
update(app) {
|
|
379
380
|
patchState(store, (state) => {
|
|
@@ -631,7 +632,8 @@ function withAppFeatures() {
|
|
|
631
632
|
sources: computed(() => parseCustomJson("sources", {})),
|
|
632
633
|
filters: computed(() => parseCustomJson("filters", [])),
|
|
633
634
|
general: computed(() => parseCustomJson("general", {})),
|
|
634
|
-
assistants: computed(() => parseCustomJson("assistants", {}))
|
|
635
|
+
assistants: computed(() => parseCustomJson("assistants", {})),
|
|
636
|
+
agents: computed(() => parseCustomJson("agents", {}))
|
|
635
637
|
};
|
|
636
638
|
}), withMethods((store) => ({
|
|
637
639
|
/**
|
|
@@ -737,6 +739,12 @@ function withAppFeatures() {
|
|
|
737
739
|
if (assistants === undefined || Object.keys(assistants).length === 0)
|
|
738
740
|
return false;
|
|
739
741
|
return !!assistants[assistantName]?.defaultValues?.service_id;
|
|
742
|
+
},
|
|
743
|
+
isAgentAllowed(agentName) {
|
|
744
|
+
const agents = store.agents();
|
|
745
|
+
if (agents === undefined || Object.keys(agents).length === 0)
|
|
746
|
+
return false;
|
|
747
|
+
return !!agents[agentName]?.defaultAgent;
|
|
740
748
|
}
|
|
741
749
|
})));
|
|
742
750
|
}
|
|
@@ -1105,7 +1113,7 @@ class DropdownInputComponent {
|
|
|
1105
1113
|
}
|
|
1106
1114
|
</DropdownContent>
|
|
1107
1115
|
</Dropdown>
|
|
1108
|
-
`, isInline: true, dependencies: [{ kind: "component", type: DropdownComponent, selector: "dropdown, Dropdown", inputs: ["disabled"] }, { kind: "component", type: DropdownListComponent, selector: "dropdown-list, DropdownList", inputs: ["items"], outputs: ["onClick"] }, { kind: "directive", type: DropdownContentComponent, selector: "dropdown-content, dropdowncontent, DropdownContent", inputs: ["class", "position", "strategy", "offset"] }, { 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\"]", inputs: ["class", "variant", "decoration"] }, { kind: "pipe", type: SyslangPipe, name: "syslang" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
1116
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DropdownComponent, selector: "dropdown, Dropdown", inputs: ["disabled"] }, { kind: "component", type: DropdownListComponent, selector: "dropdown-list, DropdownList", inputs: ["items"], outputs: ["onClick"] }, { kind: "directive", type: DropdownContentComponent, selector: "dropdown-content, dropdowncontent, DropdownContent", inputs: ["class", "position", "strategy", "offset"] }, { 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: "pipe", type: SyslangPipe, name: "syslang" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
1109
1117
|
}
|
|
1110
1118
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DropdownInputComponent, decorators: [{
|
|
1111
1119
|
type: Component,
|
|
@@ -1221,82 +1229,81 @@ function withExtractsFeatures() {
|
|
|
1221
1229
|
})));
|
|
1222
1230
|
}
|
|
1223
1231
|
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1232
|
+
const initialPrincipal = {
|
|
1233
|
+
id: "",
|
|
1234
|
+
id2: "",
|
|
1235
|
+
id3: "",
|
|
1236
|
+
id4: "",
|
|
1237
|
+
id5: "",
|
|
1238
|
+
email: "",
|
|
1239
|
+
name: "",
|
|
1240
|
+
longName: "",
|
|
1241
|
+
userId: "",
|
|
1242
|
+
fullName: "",
|
|
1243
|
+
isAdministrator: false,
|
|
1244
|
+
isDelegatedAdmin: false,
|
|
1245
|
+
description: "",
|
|
1246
|
+
param1: "",
|
|
1247
|
+
param2: "",
|
|
1248
|
+
param3: "",
|
|
1249
|
+
param4: "",
|
|
1250
|
+
param5: "",
|
|
1251
|
+
param6: "",
|
|
1252
|
+
param7: "",
|
|
1253
|
+
param8: "",
|
|
1254
|
+
param9: "",
|
|
1255
|
+
param10: "",
|
|
1256
|
+
userOverrideActive: false,
|
|
1257
|
+
state: 'initial'
|
|
1258
|
+
};
|
|
1259
|
+
const PrincipalStore = signalStore({ providedIn: 'root' }, withDevtools('Principal'), withState(initialPrincipal), withComputed((store) => ({
|
|
1260
|
+
allowUserOverride: computed(() => store.isAdministrator() && !store.userOverrideActive()),
|
|
1261
|
+
isOverridingUser: computed(() => store.userOverrideActive()),
|
|
1262
|
+
initials: computed(() => {
|
|
1263
|
+
const fullName = store.fullName?.();
|
|
1264
|
+
if (!fullName)
|
|
1265
|
+
return '';
|
|
1266
|
+
return fullName
|
|
1267
|
+
.split(' ')
|
|
1268
|
+
.map(word => word[0]?.toUpperCase())
|
|
1269
|
+
.join('');
|
|
1270
|
+
}),
|
|
1271
|
+
})), withMethods((store) => ({
|
|
1272
|
+
/**
|
|
1273
|
+
* @deprecated use getState(PrincipalStore))
|
|
1274
|
+
*/
|
|
1275
|
+
principal() {
|
|
1276
|
+
const { userOverrideActive: _, ...rest } = getState(store);
|
|
1277
|
+
// this block cannot be put in a computed due to dynamic construction object with signals, which is not supported in computed.
|
|
1278
|
+
// using a method allows us to construct the principal object on each call, ensuring that the signals are properly evaluated and returned as values.
|
|
1279
|
+
const principal = Object.entries(rest).reduce((acc, [k, v]) => {
|
|
1280
|
+
acc[k] = v;
|
|
1281
|
+
return acc;
|
|
1282
|
+
}, {});
|
|
1283
|
+
return principal;
|
|
1251
1284
|
}
|
|
1252
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: PrincipalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1253
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: PrincipalService, providedIn: 'root' });
|
|
1254
|
-
}
|
|
1255
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: PrincipalService, decorators: [{
|
|
1256
|
-
type: Injectable,
|
|
1257
|
-
args: [{
|
|
1258
|
-
providedIn: 'root'
|
|
1259
|
-
}]
|
|
1260
|
-
}] });
|
|
1261
|
-
|
|
1262
|
-
const PrincipalStore = signalStore({ providedIn: 'root' }, withDevtools('Principal'), withState({ principal: {}, userOverrideActive: false }), withComputed(({ principal, userOverrideActive }) => ({
|
|
1263
|
-
allowUserOverride: computed(() => principal().isAdministrator && userOverrideActive() === false),
|
|
1264
|
-
isOverridingUser: computed(() => userOverrideActive() === true)
|
|
1265
1285
|
})), withPrincipalFeatures());
|
|
1266
|
-
/**
|
|
1267
|
-
* Enhances the store with principal-related features.
|
|
1268
|
-
*
|
|
1269
|
-
* This function integrates state, methods, and computed properties related to the principal.
|
|
1270
|
-
*
|
|
1271
|
-
* @returns A feature that can be added to a signal store.
|
|
1272
|
-
*
|
|
1273
|
-
* @feature
|
|
1274
|
-
* - State:
|
|
1275
|
-
* - `principal`: An object representing the principal.
|
|
1276
|
-
* - `userOverrideActive`: A boolean indicating if user override is active.
|
|
1277
|
-
*
|
|
1278
|
-
* - Methods:
|
|
1279
|
-
* - `initialize()`: Initializes the principal state by fetching the principal data from the `PrincipalService`.
|
|
1280
|
-
*
|
|
1281
|
-
* - Computed Properties:
|
|
1282
|
-
* - `allowUserOverride`: A computed boolean indicating if user override is allowed based on the principal's administrator status and the `userOverrideActive` state.
|
|
1283
|
-
* - `isOverridingUser`: A computed boolean indicating if the user override is currently active.
|
|
1284
|
-
*/
|
|
1285
1286
|
function withPrincipalFeatures() {
|
|
1286
|
-
return signalStoreFeature(withMethods((store
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1287
|
+
return signalStoreFeature(withMethods((store) => {
|
|
1288
|
+
const http = inject(HttpClient);
|
|
1289
|
+
const API_URL = `${globalConfig.backendUrl}/api/v1`;
|
|
1290
|
+
return {
|
|
1291
|
+
initialize() {
|
|
1292
|
+
patchState(store, { state: 'loading' });
|
|
1293
|
+
const params = new HttpParams()
|
|
1294
|
+
.set('action', 'get')
|
|
1295
|
+
.set('noAuthentication', 'true');
|
|
1296
|
+
return firstValueFrom(http.get(`${API_URL}/principal`, { params }).pipe(catchError((error) => {
|
|
1297
|
+
console.error('Principal fetch failed', error);
|
|
1298
|
+
patchState(store, { state: 'error' });
|
|
1299
|
+
throw error;
|
|
1300
|
+
}), map((principal) => {
|
|
1301
|
+
const { userOverrideActive = false } = globalConfig;
|
|
1302
|
+
patchState(store, { ...initialPrincipal, ...principal, userOverrideActive, state: 'loaded' });
|
|
1303
|
+
})));
|
|
1304
|
+
}
|
|
1305
|
+
};
|
|
1306
|
+
}));
|
|
1300
1307
|
}
|
|
1301
1308
|
|
|
1302
1309
|
/**
|
|
@@ -1403,7 +1410,7 @@ function getQueryNameFromRoute() {
|
|
|
1403
1410
|
const { t, n } = route.snapshot.queryParams;
|
|
1404
1411
|
// ! This is a workaround for the case where the queryName is not defined in the route data
|
|
1405
1412
|
// "search" is the parent route of the search results, and "t" is the query parameter for the tab
|
|
1406
|
-
const { queryName } = routes.config.filter(p => p.path === 'search')[0]
|
|
1413
|
+
const { queryName } = routes.config.filter(p => p.path === 'search')[0]?.children?.find(route => route.path === t)?.data || n || {};
|
|
1407
1414
|
if (queryName)
|
|
1408
1415
|
return queryName;
|
|
1409
1416
|
// fallback to queryName from route data
|
|
@@ -1766,7 +1773,7 @@ function withQueryParamsFeatures() {
|
|
|
1766
1773
|
/**
|
|
1767
1774
|
* Updates the filter in the store's state.
|
|
1768
1775
|
*
|
|
1769
|
-
* @param
|
|
1776
|
+
* @param newFilter - The filter to be updated. If the filter is `undefined`, the state remains unchanged.
|
|
1770
1777
|
*
|
|
1771
1778
|
* The function performs the following operations:
|
|
1772
1779
|
* - Adds the filter to the state if it doesn't already exist and has values.
|
|
@@ -1780,66 +1787,75 @@ function withQueryParamsFeatures() {
|
|
|
1780
1787
|
* - If the filter's operator is not 'between' and `filters`, `value`, and `values` are all `undefined`, the filter is removed.
|
|
1781
1788
|
* - If the filter already exists, its values are updated.
|
|
1782
1789
|
*/
|
|
1783
|
-
updateFilter(
|
|
1790
|
+
updateFilter(newFilter, audit) {
|
|
1784
1791
|
patchState(store, (state) => {
|
|
1785
|
-
if (
|
|
1792
|
+
if (newFilter === undefined)
|
|
1786
1793
|
return state;
|
|
1787
|
-
|
|
1794
|
+
// is filter contains values, check if the operator is set, otherwise set operator to "in" by default
|
|
1795
|
+
if (newFilter.values && !newFilter.operator) {
|
|
1796
|
+
newFilter.operator = "in";
|
|
1797
|
+
}
|
|
1798
|
+
const currentActiveFilters = state.filters || [];
|
|
1788
1799
|
let newState;
|
|
1789
|
-
const
|
|
1800
|
+
const filterFound = currentActiveFilters.find((f) => f.field === newFilter.field);
|
|
1790
1801
|
// if the filter has no name (might be coming from a metadata appliance)
|
|
1791
|
-
if (!
|
|
1802
|
+
if (!newFilter.name || !filterFound?.name) {
|
|
1792
1803
|
// cancel if a filter is found for the current field which contains the value we want to add
|
|
1793
|
-
if (
|
|
1794
|
-
|
|
1795
|
-
(
|
|
1796
|
-
|
|
1804
|
+
if (filterFound &&
|
|
1805
|
+
newFilter.value &&
|
|
1806
|
+
(filterFound.value?.toLowerCase() === newFilter.value?.toLowerCase() ||
|
|
1807
|
+
filterFound.values?.some((v) => v?.toLowerCase() === newFilter.value?.toLowerCase()))) {
|
|
1797
1808
|
return state;
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
if
|
|
1804
|
-
|
|
1809
|
+
}
|
|
1810
|
+
if (filterFound) {
|
|
1811
|
+
// if filter found for the field without a value property we want to add, merge the filters and values
|
|
1812
|
+
// but merge values without duplicates
|
|
1813
|
+
const values = Array.from(new Set([...(filterFound.values || []), ...(newFilter.values || [])].filter(Boolean)));
|
|
1814
|
+
// add the new value if it exists and is not already in the existing values
|
|
1815
|
+
if (newFilter.value)
|
|
1816
|
+
values.push(newFilter.value);
|
|
1817
|
+
if (filterFound.value)
|
|
1818
|
+
values.push(filterFound.value);
|
|
1805
1819
|
const filterToApply = {
|
|
1806
|
-
field:
|
|
1820
|
+
field: newFilter.field,
|
|
1807
1821
|
values,
|
|
1808
|
-
name:
|
|
1822
|
+
name: filterFound.name || newFilter.name,
|
|
1823
|
+
operator: newFilter.operator || filterFound.operator || "in"
|
|
1809
1824
|
};
|
|
1810
|
-
|
|
1811
|
-
const
|
|
1825
|
+
// find the index of the existing filter for the field and replace it with the merged filter
|
|
1826
|
+
const existingIndex = currentActiveFilters.findIndex((f) => f.field === newFilter.field);
|
|
1827
|
+
const filters = currentActiveFilters.toSpliced(existingIndex, 1, filterToApply);
|
|
1812
1828
|
newState = { ...state, filters };
|
|
1813
1829
|
}
|
|
1814
1830
|
else {
|
|
1815
1831
|
// Add filter if it doesn't exist and has values
|
|
1816
|
-
if (!
|
|
1817
|
-
newState = { ...state, filters: [...(
|
|
1832
|
+
if (!filterFound) {
|
|
1833
|
+
newState = { ...state, filters: [...(currentActiveFilters || []), newFilter] };
|
|
1818
1834
|
}
|
|
1819
1835
|
}
|
|
1820
1836
|
}
|
|
1821
1837
|
else {
|
|
1822
1838
|
// search filter by name not by field because many filters can have the same field but different names
|
|
1823
|
-
const existing =
|
|
1839
|
+
const existing = currentActiveFilters.findIndex((f) => f.name === newFilter.name);
|
|
1824
1840
|
// Add filter if it doesn't exist and has values
|
|
1825
1841
|
if (existing === -1) {
|
|
1826
|
-
newState = { ...state, filters: [...(
|
|
1842
|
+
newState = { ...state, filters: [...(currentActiveFilters || []), newFilter] };
|
|
1827
1843
|
}
|
|
1828
1844
|
if (existing >= 0) {
|
|
1829
1845
|
// remove filter if between operator is selected but no values are selected
|
|
1830
|
-
if (
|
|
1831
|
-
newState = { ...state, filters:
|
|
1846
|
+
if (newFilter.operator === "between" && newFilter.start === undefined && newFilter.end === undefined) {
|
|
1847
|
+
newState = { ...state, filters: currentActiveFilters.filter((f) => f.name !== newFilter.name) };
|
|
1832
1848
|
}
|
|
1833
1849
|
// Remove filter if no values are selected
|
|
1834
|
-
if (
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
newState = { ...state, filters: (
|
|
1850
|
+
if (newFilter.operator !== "between" &&
|
|
1851
|
+
newFilter.filters === undefined &&
|
|
1852
|
+
newFilter.value === undefined &&
|
|
1853
|
+
newFilter.values === undefined) {
|
|
1854
|
+
newState = { ...state, filters: (currentActiveFilters || []).filter((f) => f?.name !== newFilter.name) };
|
|
1839
1855
|
}
|
|
1840
1856
|
// Update filter values
|
|
1841
1857
|
if (existing >= 0) {
|
|
1842
|
-
const filters =
|
|
1858
|
+
const filters = currentActiveFilters.toSpliced(existing, 1, newFilter);
|
|
1843
1859
|
newState = { ...state, filters };
|
|
1844
1860
|
}
|
|
1845
1861
|
}
|
|
@@ -1992,7 +2008,8 @@ function withQueryParamsFeatures() {
|
|
|
1992
2008
|
let { name } = getState(store);
|
|
1993
2009
|
if (!name) {
|
|
1994
2010
|
const routeData = route.snapshot.firstChild?.children[0]?.data;
|
|
1995
|
-
|
|
2011
|
+
const queryName = routeData?.["queryName"];
|
|
2012
|
+
name = queryName || appStore.getDefaultQuery()?.name || "";
|
|
1996
2013
|
}
|
|
1997
2014
|
let text = queryText;
|
|
1998
2015
|
// remove concepts filters from the query to add them in the query expression
|
|
@@ -2811,6 +2828,7 @@ class QueryService {
|
|
|
2811
2828
|
queryParamsStore = inject(QueryParamsStore);
|
|
2812
2829
|
transloco = inject(TranslocoService);
|
|
2813
2830
|
API_URL = `${globalConfig.backendUrl}/api/v1`;
|
|
2831
|
+
API_V2_URL = `${globalConfig.backendUrl}/api/v2`;
|
|
2814
2832
|
// Represents the last result of a search operation with getResult().
|
|
2815
2833
|
result = signal({}, ...(ngDevMode ? [{ debugName: "result" }] : []));
|
|
2816
2834
|
audit;
|
|
@@ -2968,6 +2986,34 @@ class QueryService {
|
|
|
2968
2986
|
};
|
|
2969
2987
|
this.queryParamsStore.patch({ page }, audit);
|
|
2970
2988
|
}
|
|
2989
|
+
/**
|
|
2990
|
+
* Get the pages associated to some text locations.
|
|
2991
|
+
*
|
|
2992
|
+
* @param docs - all documents offsets we want to get the pages from.
|
|
2993
|
+
* @returns An Observable that emits the found pages.
|
|
2994
|
+
*/
|
|
2995
|
+
getDocPages(docs) {
|
|
2996
|
+
const queryName = this.appStore.getDefaultQuery()?.name || "_default";
|
|
2997
|
+
const appName = globalConfig.app;
|
|
2998
|
+
return this.http.post(`${this.API_V2_URL}/app/${appName}/query/${queryName}/doc-pages`, { docs }).pipe(map(({ items }) => items), catchError(err => {
|
|
2999
|
+
error('queryService.getDocPages failure - error: ', err);
|
|
3000
|
+
return [];
|
|
3001
|
+
}));
|
|
3002
|
+
}
|
|
3003
|
+
/**
|
|
3004
|
+
* Get the page associated to a single text location.
|
|
3005
|
+
*
|
|
3006
|
+
* @param id - the document id.
|
|
3007
|
+
* @param offset - offset from the passage location.
|
|
3008
|
+
* @param length - length from the passage location.
|
|
3009
|
+
* @returns An Observable that emits the found page.
|
|
3010
|
+
*/
|
|
3011
|
+
getDocPage(id, offset, length) {
|
|
3012
|
+
return this.getDocPages([{ id, textLocations: [{ offset, length }] }]).pipe(map((response) => response[0].pages[0].pageNumber), catchError(err => {
|
|
3013
|
+
error('queryService.getDocPage failure - error: ', err);
|
|
3014
|
+
return [];
|
|
3015
|
+
}));
|
|
3016
|
+
}
|
|
2971
3017
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: QueryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2972
3018
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: QueryService, providedIn: "root" });
|
|
2973
3019
|
}
|
|
@@ -3066,11 +3112,14 @@ class AggregationsService {
|
|
|
3066
3112
|
const agg = this.getAggregation(name, column);
|
|
3067
3113
|
if (!agg)
|
|
3068
3114
|
return null;
|
|
3069
|
-
const { items = [], display = agg.name, icon, hidden, searchable = true } = this.appStore.getAggregationCustomization(agg.column, agg.name) || {};
|
|
3115
|
+
const { items = [], display = agg.name, icon, hidden, expandedLevel, searchable = true } = this.appStore.getAggregationCustomization(agg.column, agg.name) || {};
|
|
3070
3116
|
agg.display = display;
|
|
3071
3117
|
agg.icon = icon;
|
|
3072
3118
|
agg.hidden = hidden;
|
|
3073
3119
|
agg.searchable = searchable;
|
|
3120
|
+
if (agg.isTree && expandedLevel) {
|
|
3121
|
+
agg.expandedLevel = expandedLevel;
|
|
3122
|
+
}
|
|
3074
3123
|
// if the aggregation's column is a "concepts", disable the search
|
|
3075
3124
|
if (this.nonSearchableColumns.some((column) => column === agg.column)) {
|
|
3076
3125
|
agg.searchable = false;
|
|
@@ -3283,6 +3332,7 @@ function AuthGuard() {
|
|
|
3283
3332
|
const { loginPath, useCredentials, useSSO } = globalConfig;
|
|
3284
3333
|
if (state.url.startsWith("/login"))
|
|
3285
3334
|
return true;
|
|
3335
|
+
// If the user is not authenticated, navigate to the login page or loading page based on the authentication method
|
|
3286
3336
|
if (!isAuthenticated() && !useSSO) {
|
|
3287
3337
|
if (useCredentials) {
|
|
3288
3338
|
router.navigate([loginPath], { queryParams: { returnUrl: state.url } });
|
|
@@ -3292,80 +3342,41 @@ function AuthGuard() {
|
|
|
3292
3342
|
}
|
|
3293
3343
|
return false;
|
|
3294
3344
|
}
|
|
3345
|
+
// If the user is authenticated, initialize the principal store if it's in the initial state
|
|
3295
3346
|
try {
|
|
3296
|
-
if (
|
|
3347
|
+
if (principalStore.state() === "initial") {
|
|
3297
3348
|
await principalStore.initialize();
|
|
3298
3349
|
}
|
|
3299
3350
|
}
|
|
3300
3351
|
catch {
|
|
3352
|
+
error("Failed to initialize PrincipalStore. Redirecting to login.");
|
|
3353
|
+
// TODO: we might want to navigate to an error page instead of the login page in SSO mode, as the issue is not related to authentication but rather to fetching user details
|
|
3301
3354
|
router.navigate([loginPath], { queryParams: { returnUrl: state.url } });
|
|
3302
3355
|
return false;
|
|
3303
3356
|
}
|
|
3304
|
-
|
|
3305
|
-
|
|
3357
|
+
// check if the "princiapl" infos are loaded, if not navigate to the login page
|
|
3358
|
+
const { passwordExpirationDate, editablePartition, name, state: principalState } = getState(principalStore);
|
|
3359
|
+
if (principalState !== "loaded") {
|
|
3360
|
+
info("PrincipalStore state is still initial after initialization attempt. Redirecting to login.");
|
|
3306
3361
|
router.navigate([loginPath], { queryParams: { returnUrl: state.url } });
|
|
3307
3362
|
return false;
|
|
3308
3363
|
}
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
const
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
/**
|
|
3324
|
-
* Password-expiry guard: blocks navigation when the password is expired
|
|
3325
|
-
* and redirects to the change-password .
|
|
3326
|
-
*/
|
|
3327
|
-
function PasswordExpiryGuard() {
|
|
3328
|
-
return async (_, state) => {
|
|
3329
|
-
const router = inject(Router);
|
|
3330
|
-
const principalStore = inject(PrincipalStore);
|
|
3331
|
-
const { loginPath, useCredentials, useSSO } = globalConfig;
|
|
3332
|
-
if (!isAuthenticated() && !useSSO) {
|
|
3333
|
-
if (useCredentials) {
|
|
3334
|
-
router.navigate([loginPath], { queryParams: { returnUrl: state.url } });
|
|
3335
|
-
}
|
|
3336
|
-
else {
|
|
3337
|
-
router.navigate(["loading"], { queryParams: { returnUrl: state.url } });
|
|
3338
|
-
}
|
|
3339
|
-
return false;
|
|
3340
|
-
}
|
|
3341
|
-
try {
|
|
3342
|
-
if (!principalStore.principal?.()) {
|
|
3343
|
-
await principalStore.initialize();
|
|
3364
|
+
// check if the password is expired and if the partition is editable
|
|
3365
|
+
// changing password is only possible when user use credentials to auhtenticate
|
|
3366
|
+
// only in credentials mode
|
|
3367
|
+
if (useCredentials) {
|
|
3368
|
+
const exp = passwordExpirationDate;
|
|
3369
|
+
const editable = !!editablePartition;
|
|
3370
|
+
if (editable && exp && isExpired(exp)) {
|
|
3371
|
+
sessionStorage.setItem("passwordExpiredFlow", "true");
|
|
3372
|
+
const username = (name || "").trim();
|
|
3373
|
+
router.navigate(["/login"], {
|
|
3374
|
+
queryParams: { mode: "changepassword", alert: "passwordExpired", username, returnUrl: state.url }
|
|
3375
|
+
});
|
|
3376
|
+
return false;
|
|
3344
3377
|
}
|
|
3345
3378
|
}
|
|
3346
|
-
|
|
3347
|
-
router.navigate([loginPath], { queryParams: { returnUrl: state.url } });
|
|
3348
|
-
return false;
|
|
3349
|
-
}
|
|
3350
|
-
const p = principalStore.principal?.();
|
|
3351
|
-
if (!p) {
|
|
3352
|
-
router.navigate([loginPath], { queryParams: { returnUrl: state.url } });
|
|
3353
|
-
return false;
|
|
3354
|
-
}
|
|
3355
|
-
const exp = p.passwordExpirationDate;
|
|
3356
|
-
const editable = !!p.editablePartition;
|
|
3357
|
-
if (!editable || !exp) {
|
|
3358
|
-
router.navigate([loginPath], { queryParams: { returnUrl: state.url } });
|
|
3359
|
-
return false;
|
|
3360
|
-
}
|
|
3361
|
-
if (isExpired(exp)) {
|
|
3362
|
-
sessionStorage.setItem("passwordExpiredFlow", "true");
|
|
3363
|
-
const username = (p.name || "").trim();
|
|
3364
|
-
router.navigate(["/login"], {
|
|
3365
|
-
queryParams: { mode: "changepassword", alert: "passwordExpired", username, returnUrl: state.url }
|
|
3366
|
-
});
|
|
3367
|
-
return false;
|
|
3368
|
-
}
|
|
3379
|
+
debug("User is authenticated and principal store is initialized. Access granted.");
|
|
3369
3380
|
return true;
|
|
3370
3381
|
};
|
|
3371
3382
|
}
|
|
@@ -3547,7 +3558,7 @@ class ApplicationService {
|
|
|
3547
3558
|
const searchPath = this.router.config.find((route) => route.path === "search") || {
|
|
3548
3559
|
path: "search",
|
|
3549
3560
|
component: this.components.find((c) => c.path === "search")?.component || this.defaultLayoutComponent(),
|
|
3550
|
-
canActivate: [AuthGuard()
|
|
3561
|
+
canActivate: [AuthGuard()],
|
|
3551
3562
|
children: []
|
|
3552
3563
|
};
|
|
3553
3564
|
const wildcardPath = searchPath.children?.find((route) => route.path === "**") || {
|
|
@@ -4018,6 +4029,7 @@ class PreviewService {
|
|
|
4018
4029
|
highlights = inject(HIGHLIGHTS);
|
|
4019
4030
|
previewData;
|
|
4020
4031
|
iframe;
|
|
4032
|
+
passageOffset = signal(undefined, ...(ngDevMode ? [{ debugName: "passageOffset" }] : []));
|
|
4021
4033
|
highlightCategory = "extractslocations";
|
|
4022
4034
|
extracts = ["matchlocations", "extractslocations", "matchingpassages"];
|
|
4023
4035
|
entities = ["company", "geo", "person"];
|
|
@@ -4135,11 +4147,12 @@ class PreviewService {
|
|
|
4135
4147
|
}
|
|
4136
4148
|
/**
|
|
4137
4149
|
* Closes the preview with the specified ID and updates the audit log.
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4150
|
+
*
|
|
4151
|
+
* @param id - The ID of the preview to close.
|
|
4152
|
+
* @param query - The partial query object used to retrieve the preview detail.
|
|
4153
|
+
*/
|
|
4142
4154
|
close(id, query) {
|
|
4155
|
+
this.passageOffset.set(undefined);
|
|
4143
4156
|
const detail = this.getAuditPreviewDetail(id, query);
|
|
4144
4157
|
const auditEvent = {
|
|
4145
4158
|
type: "Preview_Close" /* AuditEventType.Preview_Close */,
|
|
@@ -4390,6 +4403,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
4390
4403
|
}]
|
|
4391
4404
|
}], ctorParameters: () => [{ type: i0.DestroyRef }] });
|
|
4392
4405
|
|
|
4406
|
+
/**
|
|
4407
|
+
* @deprecated This service is deprecated and should not be used directly. Please use the PrincipalStore instead.
|
|
4408
|
+
*/
|
|
4409
|
+
class PrincipalService {
|
|
4410
|
+
http = inject(HttpClient);
|
|
4411
|
+
API_URL = `${globalConfig.backendUrl}/api/v1`;
|
|
4412
|
+
/**
|
|
4413
|
+
* Retrieves the principal information from the server.
|
|
4414
|
+
*
|
|
4415
|
+
* @returns Observable<Principal> An observable that emits the principal information.
|
|
4416
|
+
*
|
|
4417
|
+
* @remarks
|
|
4418
|
+
* This method sends a GET request to the API endpoint to fetch the principal data.
|
|
4419
|
+
* It includes query parameters to specify the action and to indicate that no authentication is required.
|
|
4420
|
+
* In case of an error, it logs the error to the console and returns an empty observable.
|
|
4421
|
+
*
|
|
4422
|
+
* @example
|
|
4423
|
+
* ```typescript
|
|
4424
|
+
* principalService.getPrincipal().subscribe(principal => {
|
|
4425
|
+
* console.log(principal);
|
|
4426
|
+
* });
|
|
4427
|
+
* ```
|
|
4428
|
+
*/
|
|
4429
|
+
getPrincipal() {
|
|
4430
|
+
const params = new HttpParams().set('action', 'get');
|
|
4431
|
+
params.append('noAuthentication', 'true');
|
|
4432
|
+
return this.http.get(this.API_URL + '/principal', { params }).pipe(catchError(error => {
|
|
4433
|
+
console.error('PrincipalService.getPrincipal failure - error: ', error);
|
|
4434
|
+
return EMPTY;
|
|
4435
|
+
}));
|
|
4436
|
+
}
|
|
4437
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: PrincipalService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4438
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: PrincipalService, providedIn: 'root' });
|
|
4439
|
+
}
|
|
4440
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: PrincipalService, decorators: [{
|
|
4441
|
+
type: Injectable,
|
|
4442
|
+
args: [{
|
|
4443
|
+
providedIn: 'root'
|
|
4444
|
+
}]
|
|
4445
|
+
}] });
|
|
4446
|
+
|
|
4393
4447
|
const SAVED_SEARCHES_MAX_STORAGE = 100;
|
|
4394
4448
|
class SavedSearchesService {
|
|
4395
4449
|
userSettingsStore = inject(UserSettingsStore);
|
|
@@ -5271,7 +5325,7 @@ class LoadingComponent {
|
|
|
5271
5325
|
}
|
|
5272
5326
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: LoadingComponent, deps: [{ token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
5273
5327
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.16", type: LoadingComponent, isStandalone: true, selector: "app-wait", ngImport: i0, template: `
|
|
5274
|
-
<div class="flex h-
|
|
5328
|
+
<div class="flex h-dvh w-full items-center justify-center">
|
|
5275
5329
|
<div class="flex flex-col items-center space-y-4">
|
|
5276
5330
|
<span class="loader"></span>
|
|
5277
5331
|
</div>
|
|
@@ -5281,7 +5335,7 @@ class LoadingComponent {
|
|
|
5281
5335
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: LoadingComponent, decorators: [{
|
|
5282
5336
|
type: Component,
|
|
5283
5337
|
args: [{ selector: 'app-wait', standalone: true, imports: [], template: `
|
|
5284
|
-
<div class="flex h-
|
|
5338
|
+
<div class="flex h-dvh w-full items-center justify-center">
|
|
5285
5339
|
<div class="flex flex-col items-center space-y-4">
|
|
5286
5340
|
<span class="loader"></span>
|
|
5287
5341
|
</div>
|
|
@@ -5522,7 +5576,7 @@ class CollectionsDialog {
|
|
|
5522
5576
|
</DialogFooter>
|
|
5523
5577
|
</DialogContent>
|
|
5524
5578
|
</dialog>
|
|
5525
|
-
`, isInline: true, 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", "size"] }, { kind: "component", type: DialogComponent, selector: "dialog, [dialog]", outputs: ["closed"], exportAs: ["dialog"] }, { kind: "component", type: DialogHeaderComponent, selector: "DialogHeader" }, { kind: "directive", type: DialogTitleComponent, selector: "DialogTitle", inputs: ["class"] }, { kind: "directive", type: DialogContentComponent, selector: "DialogContent", inputs: ["class"] }, { kind: "directive", type: DialogFooterComponent, selector: "DialogFooter", inputs: ["class"] }, { kind: "directive", type: ListItemComponent, selector: "[role=\"listitem\"], [role=\"option\"]", inputs: ["class", "variant", "decoration"] }, { 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\"]", inputs: ["class", "variant", "decoration"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
5579
|
+
`, isInline: true, 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", "size"] }, { kind: "component", type: DialogComponent, selector: "dialog, [dialog]", outputs: ["closed"], exportAs: ["dialog"] }, { kind: "component", type: DialogHeaderComponent, selector: "DialogHeader" }, { kind: "directive", type: DialogTitleComponent, selector: "DialogTitle", inputs: ["class"] }, { kind: "directive", type: DialogContentComponent, selector: "DialogContent", inputs: ["class"] }, { kind: "directive", type: DialogFooterComponent, selector: "DialogFooter", inputs: ["class"] }, { kind: "directive", type: ListItemComponent, selector: "[role=\"listitem\"], [role=\"option\"]", inputs: ["class", "variant", "decoration"] }, { 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: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
5526
5580
|
}
|
|
5527
5581
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: CollectionsDialog, decorators: [{
|
|
5528
5582
|
type: Component,
|
|
@@ -5730,7 +5784,7 @@ class ExportDialog {
|
|
|
5730
5784
|
destroyRef = inject(DestroyRef);
|
|
5731
5785
|
open(selection) {
|
|
5732
5786
|
const app = getState(this.appStore);
|
|
5733
|
-
const webservice = app.queryExport;
|
|
5787
|
+
const webservice = app.queryExport.toLocaleLowerCase();
|
|
5734
5788
|
let queryExport;
|
|
5735
5789
|
if (selection && selection.length > 0)
|
|
5736
5790
|
this.selection.set(selection);
|
|
@@ -7339,7 +7393,7 @@ class PreviewNavigator {
|
|
|
7339
7393
|
[disabled]="isLast()">
|
|
7340
7394
|
<chevrons-right />
|
|
7341
7395
|
</button>
|
|
7342
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { 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\"]", inputs: ["class", "variant", "decoration"] }, { kind: "component", type: ChevronLeftIconComponent, selector: "chevron-left, ChevronLeft, chevronleft", inputs: ["class"] }, { kind: "component", type: ChevronsLeftIconComponent, selector: "chevrons-left, ChevronsLeft, chevronsleft", inputs: ["class"] }, { kind: "component", type: ChevronsRightIconComponent, selector: "chevrons-right, ChevronsRight, chevronsright", inputs: ["class"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon", inputs: ["class"] }] });
|
|
7396
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { 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: "component", type: ChevronLeftIconComponent, selector: "chevron-left, ChevronLeft, chevronleft", inputs: ["class"] }, { kind: "component", type: ChevronsLeftIconComponent, selector: "chevrons-left, ChevronsLeft, chevronsleft", inputs: ["class"] }, { kind: "component", type: ChevronsRightIconComponent, selector: "chevrons-right, ChevronsRight, chevronsright", inputs: ["class"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon", inputs: ["class"] }] });
|
|
7343
7397
|
}
|
|
7344
7398
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: PreviewNavigator, decorators: [{
|
|
7345
7399
|
type: Component,
|
|
@@ -8023,7 +8077,7 @@ class AdvancedFiltersComponent {
|
|
|
8023
8077
|
return res;
|
|
8024
8078
|
}
|
|
8025
8079
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AdvancedFiltersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8026
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AdvancedFiltersComponent, isStandalone: true, selector: "advanced-filters", host: { classAttribute: "contents" }, providers: [SyslangPipe, TranslocoPipe, provideTranslocoScope("drawers")], ngImport: i0, template: "<div class=\"flex h-full flex-col overflow-auto\">\n <sheet-navbar class=\"border-foreground/10 block border-b pb-2\">\n <button [attr.title]=\"'drawers.search' | transloco\" (click)=\"onSearch()\">\n {{ 'drawers.search' | transloco }}\n </button>\n </sheet-navbar>\n\n <div class=\"flex h-full grow flex-col overflow-auto p-4\">\n <section class=\"flex flex-col gap-4\" [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", 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: 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", "size"] }, { 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\"]", inputs: ["class", "variant", "decoration"] }, { kind: "directive", type: TabsComponent, selector: "tabs, Tabs", inputs: ["class"] }, { kind: "directive", type: TabComponent, selector: "tab, Tab", inputs: ["class", "variant", "size", "value", "active"], outputs: ["clicked"] }, { kind: "directive", type: TabsListComponent, selector: "tabs-list, TabsList", inputs: ["class", "variant", "size"] }, { kind: "component", type: SheetNavbarComponent, selector: "SheetNavbar, sheetnavbar, sheet-navbar" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
|
|
8080
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AdvancedFiltersComponent, isStandalone: true, selector: "advanced-filters", host: { classAttribute: "contents" }, providers: [SyslangPipe, TranslocoPipe, provideTranslocoScope("drawers")], ngImport: i0, template: "<div class=\"flex h-full flex-col overflow-auto\">\n <sheet-navbar class=\"border-foreground/10 block border-b pb-2\">\n <button [attr.title]=\"'drawers.search' | transloco\" (click)=\"onSearch()\">\n {{ 'drawers.search' | transloco }}\n </button>\n </sheet-navbar>\n\n <div class=\"flex h-full grow flex-col overflow-auto p-4\">\n <section class=\"flex flex-col gap-4\" [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", 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: 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", "size"] }, { 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", "value", "active"], outputs: ["clicked"] }, { kind: "directive", type: TabsListComponent, selector: "tabs-list, TabsList", inputs: ["class", "variant", "size"] }, { kind: "component", type: SheetNavbarComponent, selector: "SheetNavbar, sheetnavbar, sheet-navbar" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
|
|
8027
8081
|
}
|
|
8028
8082
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AdvancedFiltersComponent, decorators: [{
|
|
8029
8083
|
type: Component,
|
|
@@ -8043,57 +8097,777 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
8043
8097
|
}, template: "<div class=\"flex h-full flex-col overflow-auto\">\n <sheet-navbar class=\"border-foreground/10 block border-b pb-2\">\n <button [attr.title]=\"'drawers.search' | transloco\" (click)=\"onSearch()\">\n {{ 'drawers.search' | transloco }}\n </button>\n </sheet-navbar>\n\n <div class=\"flex h-full grow flex-col overflow-auto p-4\">\n <section class=\"flex flex-col gap-4\" [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" }]
|
|
8044
8098
|
}], ctorParameters: () => [] });
|
|
8045
8099
|
|
|
8046
|
-
|
|
8100
|
+
/**
|
|
8101
|
+
* Injection token that indicates whether custom date ranges are allowed.
|
|
8102
|
+
*
|
|
8103
|
+
* @remarks
|
|
8104
|
+
* This token is used to configure the date component to allow users to select custom date ranges.
|
|
8105
|
+
*
|
|
8106
|
+
* @example
|
|
8107
|
+
* ```typescript
|
|
8108
|
+
* providers: [
|
|
8109
|
+
* { provide: FILTER_DATE_ALLOW_CUSTOM_RANGE, useValue: false }
|
|
8110
|
+
* ]
|
|
8111
|
+
* ```
|
|
8112
|
+
*
|
|
8113
|
+
* @public
|
|
8114
|
+
*/
|
|
8115
|
+
const FILTER_DATE_ALLOW_CUSTOM_RANGE = new InjectionToken("date allow custom range", {
|
|
8116
|
+
factory: () => true
|
|
8117
|
+
});
|
|
8118
|
+
|
|
8119
|
+
class AggregationListItemComponent {
|
|
8047
8120
|
cn = cn;
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
article = input.required(...(ngDevMode ? [{ debugName: "article" }] : []));
|
|
8051
|
-
previewHighlights = computed(() => {
|
|
8052
|
-
const highlights = this.appStore.getWebServiceByType("preview")?.highlights
|
|
8053
|
-
?.split(",")
|
|
8054
|
-
.filter((h) => h !== "extractslocations" && h !== "matchlocations" && h !== "matchingpassages");
|
|
8055
|
-
return highlights?.map((highlight) => ({
|
|
8056
|
-
name: highlight,
|
|
8057
|
-
entity: highlight,
|
|
8058
|
-
metadata: this.getMetadata(this.article(), highlight)
|
|
8059
|
-
}));
|
|
8060
|
-
}, ...(ngDevMode ? [{ debugName: "previewHighlights" }] : []));
|
|
8061
|
-
getMetadata(article, entity) {
|
|
8062
|
-
const m = article[entity];
|
|
8063
|
-
return m ?? [];
|
|
8121
|
+
get disabled() {
|
|
8122
|
+
return this.node().count === 0 ? "disabled" : null;
|
|
8064
8123
|
}
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8124
|
+
onSelect = output();
|
|
8125
|
+
onOpen = output();
|
|
8126
|
+
onFilter = output();
|
|
8127
|
+
node = input.required(...(ngDevMode ? [{ debugName: "node" }] : []));
|
|
8128
|
+
field = input(...(ngDevMode ? [undefined, { debugName: "field" }] : []));
|
|
8129
|
+
appStore = inject(AppStore);
|
|
8130
|
+
queryParamsStore = inject(QueryParamsStore);
|
|
8131
|
+
searchText = inject(AggregationListComponent).searchText;
|
|
8132
|
+
// is the count of items displayed, default to false
|
|
8133
|
+
showCount = computed(() => this.appStore.general()?.features?.showAggregationItemCount ?? false, ...(ngDevMode ? [{ debugName: "showCount" }] : []));
|
|
8134
|
+
quickFilter = computed(() => this.appStore.general()?.features?.quickFilter, ...(ngDevMode ? [{ debugName: "quickFilter" }] : []));
|
|
8135
|
+
linkChildren = computed(() => this.appStore.general()?.features?.filterLinkChildren, ...(ngDevMode ? [{ debugName: "linkChildren" }] : []));
|
|
8136
|
+
isFiltered = computed(() => {
|
|
8137
|
+
const filters = this.queryParamsStore.getFilter({ field: this.field(), name: this.name() });
|
|
8138
|
+
if (!filters)
|
|
8139
|
+
return false;
|
|
8140
|
+
const values = [this.node().value]; // to also consider the treepath value
|
|
8141
|
+
return (values.some((v) => v === filters.value) ||
|
|
8142
|
+
(filters.values?.length && filters.values.some((value) => values.some((v) => v === value))));
|
|
8143
|
+
}, ...(ngDevMode ? [{ debugName: "isFiltered" }] : []));
|
|
8144
|
+
name = computed(() => {
|
|
8145
|
+
const value = this.node().display || this.node().value;
|
|
8146
|
+
return typeof value === "string" ? value : `${value}`;
|
|
8147
|
+
}, ...(ngDevMode ? [{ debugName: "name" }] : []));
|
|
8148
|
+
select(item, e) {
|
|
8149
|
+
e?.stopImmediatePropagation();
|
|
8150
|
+
const selected = !item.$selected;
|
|
8151
|
+
item.$selected = selected;
|
|
8152
|
+
this.onSelect.emit(item);
|
|
8073
8153
|
}
|
|
8074
|
-
|
|
8075
|
-
if (
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
}
|
|
8079
|
-
const currentNavigation = this.navigation();
|
|
8080
|
-
const isCurrentValue = currentNavigation?.value === data.value;
|
|
8081
|
-
let newIndex;
|
|
8082
|
-
if (direction === "next") {
|
|
8083
|
-
newIndex = isCurrentValue && currentNavigation.index < data.count ? currentNavigation.index + 1 : 1;
|
|
8084
|
-
}
|
|
8085
|
-
else {
|
|
8086
|
-
newIndex = isCurrentValue && currentNavigation.index > 1 ? currentNavigation.index - 1 : data.count;
|
|
8154
|
+
onTextClick(event) {
|
|
8155
|
+
if (this.quickFilter()) {
|
|
8156
|
+
this.select(this.node(), event);
|
|
8157
|
+
this.onFilter.emit();
|
|
8087
8158
|
}
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
index:
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8159
|
+
}
|
|
8160
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationListItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8161
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AggregationListItemComponent, isStandalone: true, selector: "aggregation-list-item, AggregationListItem, aggregationlistitem", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", onOpen: "onOpen", onFilter: "onFilter" }, host: { properties: { "attr.disabled": "this.disabled" } }, ngImport: i0, template: "<a\r\n role=\"listitem\"\r\n [attr.aria-selected]=\"node().$selected\"\r\n [attr.aria-label]=\"name() | syslang\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n node().count === 0 && 'disabled pointer-events-none text-neutral-300',\r\n node().$selected &&\r\n 'bg-primary-100 text-primary dark:text-primary-foreground'\r\n )\r\n \"\r\n (click)=\"select(node(), $event)\">\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ node().value }}\"\r\n [attr.disabled]=\"node().count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"node().count === 0\"\r\n (keydown.enter)=\"select(node(), $event)\"\r\n [checked]=\"node().$selected\" />\r\n\r\n @if (node().icon) {\r\n <i\r\n class=\"fa-fw {{ node().icon }} self-center justify-self-center\"\r\n aria-hidden=\"true\"></i>\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isFiltered() ? 'filters.removeFilter' : 'filters.addFilter')\r\n | transloco) +\r\n ': ' +\r\n (name() | syslang)\r\n : (name() | syslang)\r\n \"\r\n (click)=\"onTextClick($event)\">\r\n @for (\r\n chunk of (name() | syslang) ?? \"\" | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && node().count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n node().count\r\n }}</span>\r\n }\r\n</a>\r\n", styles: [":host{display:block;-webkit-user-select:none;user-select:none}:host a{padding-left:var(--agg-tree-indent, .5rem)}a{line-height:var(--agg-item-height, inherit)}\n"], dependencies: [{ kind: "directive", type: ListItemComponent, selector: "[role=\"listitem\"], [role=\"option\"]", inputs: ["class", "variant", "decoration"] }, { kind: "pipe", type: HighlightWordPipe, name: "highlightWord" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
8162
|
+
}
|
|
8163
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationListItemComponent, decorators: [{
|
|
8164
|
+
type: Component,
|
|
8165
|
+
args: [{ selector: "aggregation-list-item, AggregationListItem, aggregationlistitem", standalone: true, imports: [HighlightWordPipe, ListItemComponent, SyslangPipe, TranslocoPipe], template: "<a\r\n role=\"listitem\"\r\n [attr.aria-selected]=\"node().$selected\"\r\n [attr.aria-label]=\"name() | syslang\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n node().count === 0 && 'disabled pointer-events-none text-neutral-300',\r\n node().$selected &&\r\n 'bg-primary-100 text-primary dark:text-primary-foreground'\r\n )\r\n \"\r\n (click)=\"select(node(), $event)\">\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ node().value }}\"\r\n [attr.disabled]=\"node().count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"node().count === 0\"\r\n (keydown.enter)=\"select(node(), $event)\"\r\n [checked]=\"node().$selected\" />\r\n\r\n @if (node().icon) {\r\n <i\r\n class=\"fa-fw {{ node().icon }} self-center justify-self-center\"\r\n aria-hidden=\"true\"></i>\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isFiltered() ? 'filters.removeFilter' : 'filters.addFilter')\r\n | transloco) +\r\n ': ' +\r\n (name() | syslang)\r\n : (name() | syslang)\r\n \"\r\n (click)=\"onTextClick($event)\">\r\n @for (\r\n chunk of (name() | syslang) ?? \"\" | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && node().count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n node().count\r\n }}</span>\r\n }\r\n</a>\r\n", styles: [":host{display:block;-webkit-user-select:none;user-select:none}:host a{padding-left:var(--agg-tree-indent, .5rem)}a{line-height:var(--agg-item-height, inherit)}\n"] }]
|
|
8166
|
+
}], propDecorators: { disabled: [{
|
|
8167
|
+
type: HostBinding,
|
|
8168
|
+
args: ["attr.disabled"]
|
|
8169
|
+
}], onSelect: [{ type: i0.Output, args: ["onSelect"] }], onOpen: [{ type: i0.Output, args: ["onOpen"] }], onFilter: [{ type: i0.Output, args: ["onFilter"] }], node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }] } });
|
|
8170
|
+
|
|
8171
|
+
class AggregationListComponent {
|
|
8172
|
+
cn = cn;
|
|
8173
|
+
cdr = inject(ChangeDetectorRef);
|
|
8174
|
+
/* virtualizer */
|
|
8175
|
+
scrollElement = viewChild("scrollElement", ...(ngDevMode ? [{ debugName: "scrollElement" }] : []));
|
|
8176
|
+
virtualizer = injectVirtualizer(() => ({
|
|
8177
|
+
count: this.items().length,
|
|
8178
|
+
estimateSize: () => 32,
|
|
8179
|
+
scrollElement: this.scrollElement()
|
|
8180
|
+
}));
|
|
8181
|
+
searchInput = viewChild("searchInput", ...(ngDevMode ? [{ debugName: "searchInput" }] : []));
|
|
8182
|
+
/* stores */
|
|
8183
|
+
aggregationsStore = inject(AggregationsStore);
|
|
8184
|
+
queryParamsStore = inject(QueryParamsStore);
|
|
8185
|
+
appStore = inject(AppStore);
|
|
8186
|
+
/* services */
|
|
8187
|
+
aggregationsService = inject(AggregationsService);
|
|
8188
|
+
el = inject(ElementRef);
|
|
8189
|
+
injector = inject(Injector);
|
|
8190
|
+
class = input("", ...(ngDevMode ? [{ debugName: "class" }] : []));
|
|
8191
|
+
/**
|
|
8192
|
+
* The name of the <details> element. When you provide the same id, the component work as an accordion
|
|
8193
|
+
* @defaultValue null
|
|
8194
|
+
*/
|
|
8195
|
+
id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
8196
|
+
name = input.required(...(ngDevMode ? [{ debugName: "name" }] : []));
|
|
8197
|
+
column = input.required(...(ngDevMode ? [{ debugName: "column" }] : []));
|
|
8198
|
+
onSelect = output();
|
|
8199
|
+
/**
|
|
8200
|
+
* Determines whether the aggregation component can be collapsed or expanded.
|
|
8201
|
+
* When true, the component will display collapse/expand controls allowing users
|
|
8202
|
+
* to show or hide the aggregation content.
|
|
8203
|
+
*
|
|
8204
|
+
* @default false
|
|
8205
|
+
*/
|
|
8206
|
+
collapsible = input(false, ...(ngDevMode ? [{ debugName: "collapsible" }] : []));
|
|
8207
|
+
/**
|
|
8208
|
+
* Controls whether the aggregation component is in a collapsed state.
|
|
8209
|
+
* When true, the component will be visually collapsed/hidden.
|
|
8210
|
+
* When false, the component will be expanded/visible.
|
|
8211
|
+
*
|
|
8212
|
+
* @default false
|
|
8213
|
+
*/
|
|
8214
|
+
collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : []));
|
|
8215
|
+
/**
|
|
8216
|
+
* A computed signal that tracks the collapsed state of the component.
|
|
8217
|
+
* This signal is linked to the `collapsed()` signal and automatically updates
|
|
8218
|
+
* when the collapsed state changes.
|
|
8219
|
+
*/
|
|
8220
|
+
isCollapsed = linkedSignal(() => this.collapsed(), ...(ngDevMode ? [{ debugName: "isCollapsed" }] : []));
|
|
8221
|
+
/**
|
|
8222
|
+
* Computed property that returns an empty string when the component is not collapsed,
|
|
8223
|
+
* or null when the component is collapsed. This is typically used to control
|
|
8224
|
+
* expansion state in UI components with conditional rendering or styling.
|
|
8225
|
+
*
|
|
8226
|
+
* @returns empty string if not collapsed, null if collapsed
|
|
8227
|
+
*/
|
|
8228
|
+
expanded = computed(() => (this.isCollapsed() ? null : ""), ...(ngDevMode ? [{ debugName: "expanded" }] : []));
|
|
8229
|
+
/**
|
|
8230
|
+
* A boolean flag indicating whether the component is searchable.
|
|
8231
|
+
* This property is initialized to `undefined` by default.
|
|
8232
|
+
* "Undefined" and not "false" because this input overrides the custom json settings
|
|
8233
|
+
*/
|
|
8234
|
+
searchable = input(undefined, ...(ngDevMode ? [{ debugName: "searchable" }] : []));
|
|
8235
|
+
selection = signal(false, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
8236
|
+
/**
|
|
8237
|
+
* A boolean flag indicating whether we want to see the filters count when some is applied
|
|
8238
|
+
* This property is initialized to `false` by default.
|
|
8239
|
+
*/
|
|
8240
|
+
showFiltersCount = input(null, ...(ngDevMode ? [{ debugName: "showFiltersCount", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
8241
|
+
/* aggregation */
|
|
8242
|
+
aggregation = computed(() => {
|
|
8243
|
+
// when the aggegation store updates, we need to check if the aggregation is still valid
|
|
8244
|
+
getState(this.aggregationsStore);
|
|
8245
|
+
const name = this.name();
|
|
8246
|
+
const column = this.column();
|
|
8247
|
+
if (name !== null) {
|
|
8248
|
+
const agg = this.aggregationsService.processAggregation(name, column);
|
|
8249
|
+
if (agg) {
|
|
8250
|
+
if (agg.isTree) {
|
|
8251
|
+
error("The aggregation component does not support tree aggregations. Please use the <AggregationTree /> component instead.");
|
|
8252
|
+
}
|
|
8253
|
+
// overrides "searchable" properties with the input if any
|
|
8254
|
+
agg.searchable = this.searchable() ?? agg.searchable;
|
|
8255
|
+
return agg;
|
|
8256
|
+
}
|
|
8257
|
+
}
|
|
8258
|
+
return null;
|
|
8259
|
+
}, ...(ngDevMode ? [{ debugName: "aggregation" }] : []));
|
|
8260
|
+
/* items of the aggregation */
|
|
8261
|
+
items = computed(() => {
|
|
8262
|
+
// when the aggegation store updates, we need to check if the aggregation is still valid
|
|
8263
|
+
getState(this.aggregationsStore);
|
|
8264
|
+
const agg = this.aggregation();
|
|
8265
|
+
const searchedItems = this.searchedItems();
|
|
8266
|
+
if (searchedItems.length > 0) {
|
|
8267
|
+
return searchedItems;
|
|
8268
|
+
}
|
|
8269
|
+
else if (agg?.items) {
|
|
8270
|
+
const res = this.addCurrentFiltersToItems();
|
|
8271
|
+
return res;
|
|
8272
|
+
}
|
|
8273
|
+
return [];
|
|
8274
|
+
}, ...(ngDevMode ? [{ debugName: "items" }] : []));
|
|
8275
|
+
/**
|
|
8276
|
+
* Computed signal that determines whether the items collection is empty.
|
|
8277
|
+
* @returns True if the items array has no elements, false otherwise.
|
|
8278
|
+
*/
|
|
8279
|
+
isEmpty = computed(() => this.items().length === 0, ...(ngDevMode ? [{ debugName: "isEmpty" }] : []));
|
|
8280
|
+
/**
|
|
8281
|
+
* A computed property that determines whether there are active filters
|
|
8282
|
+
* for the current aggregation column.
|
|
8283
|
+
*
|
|
8284
|
+
* if True, the clear button is shown.
|
|
8285
|
+
*
|
|
8286
|
+
* @returns {boolean} `true` if the filter count for the aggregation column is greater than 0, otherwise `false`.
|
|
8287
|
+
*/
|
|
8288
|
+
hasFilters = computed(() => {
|
|
8289
|
+
const { count = 0 } = this.queryParamsStore.getFilter({ field: this.aggregation()?.column, name: this.aggregation()?.name }) || {};
|
|
8290
|
+
return count > 0;
|
|
8291
|
+
}, ...(ngDevMode ? [{ debugName: "hasFilters" }] : []));
|
|
8292
|
+
/**
|
|
8293
|
+
* A computed property that returns the number of items of this aggregation applied in the active filters
|
|
8294
|
+
*
|
|
8295
|
+
* if more than 0 and the showCount input is set as True, the count number is shown.
|
|
8296
|
+
*
|
|
8297
|
+
* @returns {number} the filters count.
|
|
8298
|
+
*/
|
|
8299
|
+
filtersCount = computed(() => {
|
|
8300
|
+
const { count = 0 } = this.queryParamsStore.getFilter({ field: this.aggregation()?.column, name: this.aggregation()?.name }) || {};
|
|
8301
|
+
return count;
|
|
8302
|
+
}, ...(ngDevMode ? [{ debugName: "filtersCount" }] : []));
|
|
8303
|
+
isAllSelected = signal(false, ...(ngDevMode ? [{ debugName: "isAllSelected" }] : []));
|
|
8304
|
+
/* search feature */
|
|
8305
|
+
searchText = model("", ...(ngDevMode ? [{ debugName: "searchText" }] : []));
|
|
8306
|
+
debouncedSearchText = debouncedSignal(this.searchText, 300);
|
|
8307
|
+
normalizedSearchText = computed(() => this.debouncedSearchText()
|
|
8308
|
+
.normalize("NFD")
|
|
8309
|
+
.replace(/[\u0300-\u036f]/g, ""), ...(ngDevMode ? [{ debugName: "normalizedSearchText" }] : []));
|
|
8310
|
+
/* suggestions */
|
|
8311
|
+
suggests = signal([], ...(ngDevMode ? [{ debugName: "suggests" }] : []));
|
|
8312
|
+
/* searched items */
|
|
8313
|
+
searchedItems = computed(() => {
|
|
8314
|
+
if (!this.suggests())
|
|
8315
|
+
return [];
|
|
8316
|
+
// if the aggregation is not a tree, we return the suggestions as is
|
|
8317
|
+
return this.suggests()?.map(suggest => {
|
|
8318
|
+
const column = this.appStore.getColumn(suggest.category);
|
|
8319
|
+
const item = {
|
|
8320
|
+
name: this.name(),
|
|
8321
|
+
value: suggest.normalized || suggest.display || "",
|
|
8322
|
+
display: suggest.display,
|
|
8323
|
+
column: column?.name ?? suggest.category,
|
|
8324
|
+
count: Number(suggest.frequency),
|
|
8325
|
+
$selected: false
|
|
8326
|
+
};
|
|
8327
|
+
// if the column is of type boolean, we need to convert the value to a boolean
|
|
8328
|
+
if (column?.eType === EngineType.bool) {
|
|
8329
|
+
item.value = Boolean(item.value);
|
|
8330
|
+
}
|
|
8331
|
+
return item;
|
|
8332
|
+
});
|
|
8333
|
+
}, ...(ngDevMode ? [{ debugName: "searchedItems" }] : []));
|
|
8334
|
+
query;
|
|
8335
|
+
filters = signal([], ...(ngDevMode ? [{ debugName: "filters" }] : []));
|
|
8336
|
+
constructor() {
|
|
8337
|
+
this.query = buildQuery();
|
|
8338
|
+
effect(() => {
|
|
8339
|
+
// focus the search input when expanded
|
|
8340
|
+
if (this.searchInput()?.nativeElement && this.expanded() !== null) {
|
|
8341
|
+
setTimeout(() => {
|
|
8342
|
+
this.searchInput()?.nativeElement.focus();
|
|
8343
|
+
}, 0);
|
|
8344
|
+
}
|
|
8345
|
+
});
|
|
8346
|
+
effect(async () => {
|
|
8347
|
+
if (this.debouncedSearchText() === "" || this.aggregation() === null) {
|
|
8348
|
+
this.suggests.set([]);
|
|
8349
|
+
return;
|
|
8350
|
+
}
|
|
8351
|
+
const suggests = (await withFetch(() => fetchSuggestField(this.normalizedSearchText(), [this.aggregation()?.column || ""]), this.injector)) || [];
|
|
8352
|
+
this.suggests.set(suggests);
|
|
8353
|
+
});
|
|
8354
|
+
}
|
|
8355
|
+
/**
|
|
8356
|
+
* Clears the current filter for the aggregation column.
|
|
8357
|
+
*
|
|
8358
|
+
* This method updates the filter in the `queryParamsStore` by setting the display value
|
|
8359
|
+
* of the current aggregation column to an empty string.
|
|
8360
|
+
*/
|
|
8361
|
+
clear() {
|
|
8362
|
+
const agg = this.aggregation();
|
|
8363
|
+
if (agg) {
|
|
8364
|
+
this.queryParamsStore.removeFilterByName(agg.name, agg.column);
|
|
8365
|
+
this.selection.set(false);
|
|
8366
|
+
this.isAllSelected.set(false);
|
|
8367
|
+
}
|
|
8368
|
+
this.onSelect.emit([]);
|
|
8369
|
+
}
|
|
8370
|
+
/**
|
|
8371
|
+
* Select all filters for the aggregation column.
|
|
8372
|
+
*/
|
|
8373
|
+
selectAll() {
|
|
8374
|
+
if (this.items().length) {
|
|
8375
|
+
this.selectItems(this.items(), true);
|
|
8376
|
+
this.selection.set(true);
|
|
8377
|
+
this.isAllSelected.set(true);
|
|
8378
|
+
}
|
|
8379
|
+
}
|
|
8380
|
+
/**
|
|
8381
|
+
* Unselect all filters for the aggregation column.
|
|
8382
|
+
*/
|
|
8383
|
+
unselectAll() {
|
|
8384
|
+
if (this.items().length) {
|
|
8385
|
+
this.selectItems(this.items(), false);
|
|
8386
|
+
this.selection.set(true);
|
|
8387
|
+
this.isAllSelected.set(false);
|
|
8388
|
+
}
|
|
8389
|
+
}
|
|
8390
|
+
/**
|
|
8391
|
+
* Applies the current filters to the query parameters store.
|
|
8392
|
+
*
|
|
8393
|
+
* - If there are multiple filters, they are wrapped in an "or" filter.
|
|
8394
|
+
* - If the aggregation is not a distribution, the filters are merged into a single filter with an "in" operator.
|
|
8395
|
+
* - If there is only one filter, it is directly applied.
|
|
8396
|
+
* - If there are no filters, the current filters are cleared.
|
|
8397
|
+
*
|
|
8398
|
+
* After applying the filters, the search text is reset.
|
|
8399
|
+
*/
|
|
8400
|
+
apply() {
|
|
8401
|
+
const filters = this.getFilters();
|
|
8402
|
+
const agg = this.aggregation();
|
|
8403
|
+
if (!agg)
|
|
8404
|
+
return;
|
|
8405
|
+
const { name, column: field } = agg;
|
|
8406
|
+
// if filters length > 1, we need to wrap them in an "or" filter
|
|
8407
|
+
if (filters.length > 1) {
|
|
8408
|
+
const display = filters[0].display;
|
|
8409
|
+
// if aggregation not a distribution, we need to merge the filters into a single filter with an in operator
|
|
8410
|
+
// with the values of the filters
|
|
8411
|
+
if (this.aggregation()?.isDistribution) {
|
|
8412
|
+
this.queryParamsStore.updateFilter({
|
|
8413
|
+
operator: "or",
|
|
8414
|
+
filters,
|
|
8415
|
+
name,
|
|
8416
|
+
field,
|
|
8417
|
+
display
|
|
8418
|
+
});
|
|
8419
|
+
}
|
|
8420
|
+
else {
|
|
8421
|
+
const values = filters.map(filter => filter.value);
|
|
8422
|
+
this.queryParamsStore.updateFilter({
|
|
8423
|
+
operator: "in",
|
|
8424
|
+
name,
|
|
8425
|
+
field,
|
|
8426
|
+
values,
|
|
8427
|
+
display,
|
|
8428
|
+
filters
|
|
8429
|
+
});
|
|
8430
|
+
}
|
|
8431
|
+
}
|
|
8432
|
+
else if (filters.length === 1) {
|
|
8433
|
+
this.queryParamsStore.updateFilter(filters[0]);
|
|
8434
|
+
}
|
|
8435
|
+
else {
|
|
8436
|
+
this.clear();
|
|
8437
|
+
}
|
|
8438
|
+
this.searchText.set("");
|
|
8439
|
+
this.selection.set(false);
|
|
8440
|
+
}
|
|
8441
|
+
loadMore() {
|
|
8442
|
+
const q = this.queryParamsStore.getQuery();
|
|
8443
|
+
this.aggregationsService.loadMore(q, this.aggregation()).subscribe(aggregation => {
|
|
8444
|
+
this.aggregationsStore.updateAggregation(aggregation);
|
|
8445
|
+
this.cdr.detectChanges();
|
|
8446
|
+
});
|
|
8447
|
+
}
|
|
8448
|
+
/**
|
|
8449
|
+
* Updates the selected state of the given item in the aggregation list.
|
|
8450
|
+
*
|
|
8451
|
+
* @param item - The item to be selected or deselected.
|
|
8452
|
+
*
|
|
8453
|
+
* This method iterates through the items in the aggregation list and updates
|
|
8454
|
+
* the `$selected` property of the item that matches the value of the given item.
|
|
8455
|
+
*
|
|
8456
|
+
* If the item is selected, the selection count is incremented by 1.
|
|
8457
|
+
* If the item is deselected, the selection count is decremented by 1.
|
|
8458
|
+
*/
|
|
8459
|
+
select() {
|
|
8460
|
+
this.onSelect.emit(this.items().filter(item => item.$selected));
|
|
8461
|
+
this.selection.set(true);
|
|
8462
|
+
}
|
|
8463
|
+
/**
|
|
8464
|
+
* Updates the collapsed status on header click if the component is collapsible.
|
|
8465
|
+
*/
|
|
8466
|
+
onHeaderClick(event) {
|
|
8467
|
+
event.preventDefault();
|
|
8468
|
+
const isDate = this.aggregationsService.appStore.isDateColumn(this.aggregation()?.column || "");
|
|
8469
|
+
// prevent header click if no items are present
|
|
8470
|
+
if (!isDate && this.isEmpty()) {
|
|
8471
|
+
return;
|
|
8472
|
+
}
|
|
8473
|
+
if (this.collapsible()) {
|
|
8474
|
+
this.isCollapsed.update(value => !value);
|
|
8475
|
+
}
|
|
8476
|
+
}
|
|
8477
|
+
/**
|
|
8478
|
+
* Retrieves a list of filters based on the selected items.
|
|
8479
|
+
*
|
|
8480
|
+
* This method filters the items to include only those that are selected,
|
|
8481
|
+
* and then maps each selected item to a filter using the `toFilter` method.
|
|
8482
|
+
*
|
|
8483
|
+
* @returns {LegacyFilter[]} An array of filters corresponding to the selected items.
|
|
8484
|
+
*/
|
|
8485
|
+
getFilters() {
|
|
8486
|
+
const items = this.addCurrentFiltersToItems().filter(item => item.$selected);
|
|
8487
|
+
const searchedItems = this.searchedItems().filter(item => item.$selected);
|
|
8488
|
+
const currentItems = [...items, ...searchedItems];
|
|
8489
|
+
const { column, name, isDistribution = false } = this.aggregation() || {};
|
|
8490
|
+
const selectedItems = currentItems.map(item => this.aggregationsService.toFilter(item, column, name, isDistribution));
|
|
8491
|
+
return selectedItems;
|
|
8492
|
+
}
|
|
8493
|
+
addCurrentFiltersToItems() {
|
|
8494
|
+
const aggItems = (this.aggregation()?.items) || [];
|
|
8495
|
+
// add the current filters to the current items only if they are not already present
|
|
8496
|
+
if (!this.aggregation()?.isTree && (!this.aggregation()?.isDistribution || this.aggregation()?.isDistribution === false)) {
|
|
8497
|
+
// get all active filters for the current aggregation/column
|
|
8498
|
+
const activeFilters = this.queryParamsStore.getFilter({
|
|
8499
|
+
field: this.aggregation()?.column,
|
|
8500
|
+
name: this.aggregation()?.name
|
|
8501
|
+
});
|
|
8502
|
+
// if there are active filters, we need to add them to the current items
|
|
8503
|
+
if (activeFilters) {
|
|
8504
|
+
// multiples filters
|
|
8505
|
+
if (activeFilters.filters) {
|
|
8506
|
+
activeFilters.filters.forEach((filter) => {
|
|
8507
|
+
// check if the filter is already present in the current items
|
|
8508
|
+
// if not, add it to the current items
|
|
8509
|
+
const found = aggItems.find(item => item.value && item.value.toLocaleString().toLocaleLowerCase() === filter.value?.toLocaleLowerCase());
|
|
8510
|
+
if (!found) {
|
|
8511
|
+
// add it to the current items
|
|
8512
|
+
aggItems.unshift({
|
|
8513
|
+
value: filter.value,
|
|
8514
|
+
display: filter.display,
|
|
8515
|
+
$selected: true
|
|
8516
|
+
});
|
|
8517
|
+
}
|
|
8518
|
+
});
|
|
8519
|
+
}
|
|
8520
|
+
else {
|
|
8521
|
+
// single filter
|
|
8522
|
+
const found = aggItems.find(item => item.value?.toString().toLocaleLowerCase() === activeFilters.value?.toLocaleLowerCase());
|
|
8523
|
+
if (!found) {
|
|
8524
|
+
// add it to the current items
|
|
8525
|
+
aggItems.push({
|
|
8526
|
+
value: activeFilters.value,
|
|
8527
|
+
display: activeFilters.display,
|
|
8528
|
+
$selected: true
|
|
8529
|
+
});
|
|
8530
|
+
}
|
|
8531
|
+
}
|
|
8532
|
+
}
|
|
8533
|
+
}
|
|
8534
|
+
return aggItems;
|
|
8535
|
+
}
|
|
8536
|
+
/**
|
|
8537
|
+
* Update the $selected property to the selected parameter to all items
|
|
8538
|
+
*
|
|
8539
|
+
* @param items the items to apply to
|
|
8540
|
+
* @param selected the selected status
|
|
8541
|
+
*/
|
|
8542
|
+
selectItems(items, selected) {
|
|
8543
|
+
items.forEach(item => {
|
|
8544
|
+
// don't select disabled items
|
|
8545
|
+
if (item.count > 0) {
|
|
8546
|
+
item.$selected = selected;
|
|
8547
|
+
}
|
|
8548
|
+
});
|
|
8549
|
+
}
|
|
8550
|
+
onToggle(event) {
|
|
8551
|
+
const e = event;
|
|
8552
|
+
this.isCollapsed.set(e.newState === "closed");
|
|
8553
|
+
}
|
|
8554
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
8555
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AggregationListComponent, isStandalone: true, selector: "AggregationList, aggregation-list, aggregationlist", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, showFiltersCount: { classPropertyName: "showFiltersCount", publicName: "showFiltersCount", isSignal: true, isRequired: false, transformFunction: null }, searchText: { classPropertyName: "searchText", publicName: "searchText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", searchText: "searchTextChange" }, host: { properties: { "class": "cn(\"block h-[inherit] max-h-[inherit]\",class())" } }, viewQueries: [{ propertyName: "scrollElement", first: true, predicate: ["scrollElement"], descendants: true, isSignal: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <i class=\"fa-fw fas fa-triangle-exclamation mr-1\"></i>\r\n The aggregation component no longer supports tree aggregations. Please use\r\n the <AggregationTree /> component instead.\r\n </div>\r\n}\r\n<details\r\n [attr.open]=\"expanded()\"\r\n [attr.name]=\"id()\"\r\n class=\"group space-y-2\"\r\n (toggle)=\"onToggle($event)\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible() && !isEmpty()\"\r\n [class.text-muted-foreground]=\"isEmpty()\"\r\n class=\"m-0 mt-1 flex h-8 w-full items-center gap-1 pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @if (aggregation()?.icon) {\r\n <i class=\"fa-fw {{ aggregation()?.icon }} mr-1\" aria-hidden=\"true\"></i>\r\n }\r\n <span class=\"grow truncate\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (showFiltersCount() && filtersCount() > 0) {\r\n <!-- count -->\r\n <Badge size=\"xs\" class=\"ml-1 pb-0.5\">\r\n {{ filtersCount() }}\r\n </Badge>\r\n }\r\n <!-- apply filter block -->\r\n @if (!isCollapsed()) {\r\n <ButtonGroup>\r\n @if (hasFilters()) {\r\n <button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n class=\"size-6\"\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); clear()\">\r\n <i class=\"fa-fw far fa-filter-circle-xmark\"></i>\r\n <span class=\"sr-only\">{{\r\n \"filters.clearFilters\" | transloco\r\n }}</span>\r\n </button>\r\n }\r\n @if (selection()) {\r\n <button\r\n variant=\"primary\"\r\n size=\"xs\"\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); apply()\"\r\n class=\"h-4 px-1\">\r\n <FilterIcon class=\"size-4\" />\r\n {{ \"filters.apply\" | transloco }}\r\n </button>\r\n }\r\n\r\n <!-- select / unselect all -->\r\n @if (isAllSelected()) {\r\n <button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n class=\"size-6\"\r\n [attr.title]=\"'filters.unselectAllFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.unselectAllFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); unselectAll()\">\r\n <i class=\"fa-fw far fa-check-square\"></i>\r\n <span class=\"sr-only\">{{\r\n \"filters.unselectAllFilters\" | transloco\r\n }}</span>\r\n </button>\r\n } @else {\r\n <button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n class=\"size-6\"\r\n [attr.title]=\"'filters.selectAllFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.selectAllFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); selectAll()\">\r\n <i class=\"fa-fw far fa-square\"></i>\r\n <span class=\"sr-only\">{{\r\n \"filters.selectAllFilters\" | transloco\r\n }}</span>\r\n </button>\r\n }\r\n </ButtonGroup>\r\n }\r\n\r\n @if (collapsible()) {\r\n <button\r\n variant=\"none\"\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n <span class=\"sr-only\">{{ \"filters.toggle\" | transloco }}</span>\r\n </button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n @if (aggregation()?.searchable && items().length) {\r\n <InputGroup class=\"group/item mt-1\">\r\n <input\r\n #searchInput\r\n input-group\r\n id=\"aggregation-input-{{ column() }}\"\r\n type=\"text\"\r\n [attr.placeholder]=\"'search' | transloco\"\r\n [(ngModel)]=\"searchText\"\r\n class=\"mt-1\" />\r\n <InputGroupAddon>\r\n <SearchIcon\r\n class=\"text-foreground size-4 rotate-0 transition-[rotate] duration-500 group-focus-within/item:rotate-90\" />\r\n </InputGroupAddon>\r\n </InputGroup>\r\n }\r\n\r\n <div\r\n #scrollElement\r\n class=\"scrollbar-thin max-h-[calc(var(--height,100%)-100px)] w-full overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"list\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div\r\n class=\"absolute w-full\"\r\n [style.transform]=\"'translateY(' + vItem.start + 'px)'\"\r\n role=\"listitem\">\r\n <AggregationListItem\r\n [node]=\"item\"\r\n [field]=\"aggregation()?.column\"\r\n (onSelect)=\"select()\"\r\n (onFilter)=\"apply()\" />\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n @if (aggregation()?.$hasMore && this.searchedItems().length === 0) {\r\n <button\r\n variant=\"link\"\r\n class=\"mt-1 flex w-full justify-center\"\r\n [attr.aria-label]=\"'loadMore' | transloco\"\r\n (click)=\"loadMore()\">\r\n {{ \"loadMore\" | transloco }}\r\n </button>\r\n }\r\n</details>\r\n", styles: ["AggregationItem:has(+AggregationItem){margin-bottom:var(--agg-item-gap, 0)}\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: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "component", type: AggregationListItemComponent, selector: "aggregation-list-item, AggregationListItem, aggregationlistitem", inputs: ["node", "field"], outputs: ["onSelect", "onOpen", "onFilter"] }, { kind: "directive", type: BadgeComponent, selector: "badge, Badge", inputs: ["class", "variant", "size"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon", inputs: ["class"] }, { kind: "directive", type: ButtonGroup, selector: "button-group, ButtonGroup", inputs: ["class", "orientation"] }, { kind: "directive", type: InputGroupInput, selector: "input[input-group]", inputs: ["class", "type", "placeholder", "disabled"] }, { kind: "directive", type: InputGroupComponent, selector: "input-group, inputgroup, InputGroup", inputs: ["class"] }, { kind: "directive", type: InputGroupAddonComponent, selector: "input-group-addon, inputgroupaddon, InputGroupAddon", inputs: ["class", "align"] }, { kind: "component", type: SearchIcon, selector: "SearchIcon", inputs: ["class"] }, { kind: "component", type: FilterIcon, selector: "filter-icon, FilterIcon", inputs: ["class"] }, { kind: "pipe", type: SyslangPipe, name: "syslang" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
8556
|
+
}
|
|
8557
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationListComponent, decorators: [{
|
|
8558
|
+
type: Component,
|
|
8559
|
+
args: [{ selector: "AggregationList, aggregation-list, aggregationlist", imports: [
|
|
8560
|
+
FormsModule,
|
|
8561
|
+
ReactiveFormsModule,
|
|
8562
|
+
ButtonComponent,
|
|
8563
|
+
AggregationListItemComponent,
|
|
8564
|
+
SyslangPipe,
|
|
8565
|
+
TranslocoPipe,
|
|
8566
|
+
BadgeComponent,
|
|
8567
|
+
ChevronRightIcon,
|
|
8568
|
+
ButtonGroup,
|
|
8569
|
+
InputGroupInput,
|
|
8570
|
+
InputGroupComponent,
|
|
8571
|
+
InputGroupAddonComponent,
|
|
8572
|
+
SearchIcon,
|
|
8573
|
+
FilterIcon,
|
|
8574
|
+
AggregationListItemComponent
|
|
8575
|
+
], standalone: true, host: {
|
|
8576
|
+
"[class]": 'cn("block h-[inherit] max-h-[inherit]",class())'
|
|
8577
|
+
}, template: "@if (aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <i class=\"fa-fw fas fa-triangle-exclamation mr-1\"></i>\r\n The aggregation component no longer supports tree aggregations. Please use\r\n the <AggregationTree /> component instead.\r\n </div>\r\n}\r\n<details\r\n [attr.open]=\"expanded()\"\r\n [attr.name]=\"id()\"\r\n class=\"group space-y-2\"\r\n (toggle)=\"onToggle($event)\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible() && !isEmpty()\"\r\n [class.text-muted-foreground]=\"isEmpty()\"\r\n class=\"m-0 mt-1 flex h-8 w-full items-center gap-1 pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @if (aggregation()?.icon) {\r\n <i class=\"fa-fw {{ aggregation()?.icon }} mr-1\" aria-hidden=\"true\"></i>\r\n }\r\n <span class=\"grow truncate\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (showFiltersCount() && filtersCount() > 0) {\r\n <!-- count -->\r\n <Badge size=\"xs\" class=\"ml-1 pb-0.5\">\r\n {{ filtersCount() }}\r\n </Badge>\r\n }\r\n <!-- apply filter block -->\r\n @if (!isCollapsed()) {\r\n <ButtonGroup>\r\n @if (hasFilters()) {\r\n <button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n class=\"size-6\"\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); clear()\">\r\n <i class=\"fa-fw far fa-filter-circle-xmark\"></i>\r\n <span class=\"sr-only\">{{\r\n \"filters.clearFilters\" | transloco\r\n }}</span>\r\n </button>\r\n }\r\n @if (selection()) {\r\n <button\r\n variant=\"primary\"\r\n size=\"xs\"\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); apply()\"\r\n class=\"h-4 px-1\">\r\n <FilterIcon class=\"size-4\" />\r\n {{ \"filters.apply\" | transloco }}\r\n </button>\r\n }\r\n\r\n <!-- select / unselect all -->\r\n @if (isAllSelected()) {\r\n <button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n class=\"size-6\"\r\n [attr.title]=\"'filters.unselectAllFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.unselectAllFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); unselectAll()\">\r\n <i class=\"fa-fw far fa-check-square\"></i>\r\n <span class=\"sr-only\">{{\r\n \"filters.unselectAllFilters\" | transloco\r\n }}</span>\r\n </button>\r\n } @else {\r\n <button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n class=\"size-6\"\r\n [attr.title]=\"'filters.selectAllFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.selectAllFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); selectAll()\">\r\n <i class=\"fa-fw far fa-square\"></i>\r\n <span class=\"sr-only\">{{\r\n \"filters.selectAllFilters\" | transloco\r\n }}</span>\r\n </button>\r\n }\r\n </ButtonGroup>\r\n }\r\n\r\n @if (collapsible()) {\r\n <button\r\n variant=\"none\"\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n <span class=\"sr-only\">{{ \"filters.toggle\" | transloco }}</span>\r\n </button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n @if (aggregation()?.searchable && items().length) {\r\n <InputGroup class=\"group/item mt-1\">\r\n <input\r\n #searchInput\r\n input-group\r\n id=\"aggregation-input-{{ column() }}\"\r\n type=\"text\"\r\n [attr.placeholder]=\"'search' | transloco\"\r\n [(ngModel)]=\"searchText\"\r\n class=\"mt-1\" />\r\n <InputGroupAddon>\r\n <SearchIcon\r\n class=\"text-foreground size-4 rotate-0 transition-[rotate] duration-500 group-focus-within/item:rotate-90\" />\r\n </InputGroupAddon>\r\n </InputGroup>\r\n }\r\n\r\n <div\r\n #scrollElement\r\n class=\"scrollbar-thin max-h-[calc(var(--height,100%)-100px)] w-full overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"list\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div\r\n class=\"absolute w-full\"\r\n [style.transform]=\"'translateY(' + vItem.start + 'px)'\"\r\n role=\"listitem\">\r\n <AggregationListItem\r\n [node]=\"item\"\r\n [field]=\"aggregation()?.column\"\r\n (onSelect)=\"select()\"\r\n (onFilter)=\"apply()\" />\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n @if (aggregation()?.$hasMore && this.searchedItems().length === 0) {\r\n <button\r\n variant=\"link\"\r\n class=\"mt-1 flex w-full justify-center\"\r\n [attr.aria-label]=\"'loadMore' | transloco\"\r\n (click)=\"loadMore()\">\r\n {{ \"loadMore\" | transloco }}\r\n </button>\r\n }\r\n</details>\r\n", styles: ["AggregationItem:has(+AggregationItem){margin-bottom:var(--agg-item-gap, 0)}\n"] }]
|
|
8578
|
+
}], ctorParameters: () => [], propDecorators: { scrollElement: [{ type: i0.ViewChild, args: ["scrollElement", { isSignal: true }] }], searchInput: [{ type: i0.ViewChild, args: ["searchInput", { isSignal: true }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], 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 }] }], searchText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchText", required: false }] }, { type: i0.Output, args: ["searchTextChange"] }] } });
|
|
8579
|
+
|
|
8580
|
+
class AggregationDateComponent extends AggregationListComponent {
|
|
8581
|
+
destroyRef;
|
|
8582
|
+
datepicker = viewChild(DateRangePickerDirective, ...(ngDevMode ? [{ debugName: "datepicker" }] : []));
|
|
8583
|
+
title = input({ label: "Date", icon: "far fa-calendar-day" }, ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
8584
|
+
displayEmptyDistributionIntervals = input(false, ...(ngDevMode ? [{ debugName: "displayEmptyDistributionIntervals" }] : []));
|
|
8585
|
+
allowCustomRange = inject(FILTER_DATE_ALLOW_CUSTOM_RANGE);
|
|
8586
|
+
transloco = inject(TranslocoService);
|
|
8587
|
+
dateOptions = computed(() => translateAggregationToDateOptions(this.aggregation(), this.displayEmptyDistributionIntervals()), ...(ngDevMode ? [{ debugName: "dateOptions" }] : []));
|
|
8588
|
+
form = new FormGroup({
|
|
8589
|
+
option: new FormControl(null),
|
|
8590
|
+
customRange: new FormGroup({
|
|
8591
|
+
from: new FormControl(null),
|
|
8592
|
+
to: new FormControl(null)
|
|
8593
|
+
})
|
|
8594
|
+
});
|
|
8595
|
+
today = new Date();
|
|
8596
|
+
lang = signal(this.transloco.getActiveLang(), ...(ngDevMode ? [{ debugName: "lang" }] : []));
|
|
8597
|
+
validSelection = signal(false, ...(ngDevMode ? [{ debugName: "validSelection" }] : []));
|
|
8598
|
+
constructor(destroyRef) {
|
|
8599
|
+
super();
|
|
8600
|
+
this.destroyRef = destroyRef;
|
|
8601
|
+
this.transloco.langChanges$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((lang) => {
|
|
8602
|
+
this.lang.set(lang);
|
|
8603
|
+
this.datepicker()?.setOptions({ language: lang });
|
|
8604
|
+
});
|
|
8605
|
+
const abortController = new AbortController();
|
|
8606
|
+
addEventListener("changeDate", this.onDateChange.bind(this), {
|
|
8607
|
+
signal: abortController.signal
|
|
8608
|
+
});
|
|
8609
|
+
// apply current date filter from queryParamsStore
|
|
8610
|
+
effect(() => {
|
|
8611
|
+
this.updateForm(this.queryParamsStore.getFilter({
|
|
8612
|
+
field: this.aggregation()?.column,
|
|
8613
|
+
name: this.aggregation()?.name
|
|
8614
|
+
}));
|
|
8615
|
+
});
|
|
8616
|
+
this.form.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((changes) => {
|
|
8617
|
+
this.validSelection.set(!!changes.option &&
|
|
8618
|
+
(changes.option !== "custom-range" || changes.customRange?.from !== null || changes.customRange?.to !== null));
|
|
8619
|
+
});
|
|
8620
|
+
destroyRef.onDestroy(() => {
|
|
8621
|
+
abortController.abort();
|
|
8622
|
+
});
|
|
8623
|
+
}
|
|
8624
|
+
aggregation = computed(() => {
|
|
8625
|
+
const name = this.name();
|
|
8626
|
+
if (name !== null) {
|
|
8627
|
+
const agg = this.aggregationsService.processAggregation(name, this.column());
|
|
8628
|
+
return {
|
|
8629
|
+
...agg,
|
|
8630
|
+
items: agg?.items?.filter((item) => item.display !== "custom-range") ?? []
|
|
8631
|
+
};
|
|
8632
|
+
}
|
|
8633
|
+
return null;
|
|
8634
|
+
}, ...(ngDevMode ? [{ debugName: "aggregation" }] : []));
|
|
8635
|
+
apply() {
|
|
8636
|
+
try {
|
|
8637
|
+
const filter = this.getFormValueFilter();
|
|
8638
|
+
this.queryParamsStore.updateFilter(filter);
|
|
8639
|
+
this.selection.set(false);
|
|
8640
|
+
}
|
|
8641
|
+
catch (err) {
|
|
8642
|
+
warn("Error applying date filter:", err);
|
|
8643
|
+
if (err instanceof Error) {
|
|
8644
|
+
notify.error(this.transloco.translate(err.message));
|
|
8645
|
+
}
|
|
8646
|
+
}
|
|
8647
|
+
}
|
|
8648
|
+
clear(notify = true) {
|
|
8649
|
+
this.form.setValue({
|
|
8650
|
+
option: null,
|
|
8651
|
+
customRange: {
|
|
8652
|
+
from: null,
|
|
8653
|
+
to: null
|
|
8654
|
+
}
|
|
8655
|
+
});
|
|
8656
|
+
const agg = this.aggregation();
|
|
8657
|
+
if (agg && notify) {
|
|
8658
|
+
this.queryParamsStore.removeFilterByName(agg.name);
|
|
8659
|
+
}
|
|
8660
|
+
}
|
|
8661
|
+
updateForm(filter) {
|
|
8662
|
+
if (!filter) {
|
|
8663
|
+
this.clear(false);
|
|
8664
|
+
return;
|
|
8665
|
+
}
|
|
8666
|
+
const { operator, value } = filter;
|
|
8667
|
+
const code = this.dateOptions().find((option) => option.operator === operator && option.value === value)
|
|
8668
|
+
?.display ?? "custom-range";
|
|
8669
|
+
let from = null, to = null;
|
|
8670
|
+
if (code === "custom-range") {
|
|
8671
|
+
switch (operator) {
|
|
8672
|
+
case "lte":
|
|
8673
|
+
to = filter.value;
|
|
8674
|
+
break;
|
|
8675
|
+
case "gte":
|
|
8676
|
+
from = filter.value;
|
|
8677
|
+
break;
|
|
8678
|
+
case "between":
|
|
8679
|
+
from = filter.start;
|
|
8680
|
+
to = filter.end;
|
|
8681
|
+
break;
|
|
8682
|
+
}
|
|
8683
|
+
}
|
|
8684
|
+
const formValue = {
|
|
8685
|
+
option: code,
|
|
8686
|
+
customRange: {
|
|
8687
|
+
from: code === "custom-range" ? (from ?? null) : null,
|
|
8688
|
+
to: code === "custom-range" ? (to ?? null) : null
|
|
8689
|
+
}
|
|
8690
|
+
};
|
|
8691
|
+
this.form.setValue(formValue);
|
|
8692
|
+
// Update the date range picker
|
|
8693
|
+
const datepicker = this.datepicker();
|
|
8694
|
+
if (datepicker) {
|
|
8695
|
+
const start = new Date(formValue.customRange.from || "");
|
|
8696
|
+
const end = new Date(formValue.customRange.to || "");
|
|
8697
|
+
setTimeout(() => {
|
|
8698
|
+
datepicker.rangeDatepicker?.setDates(start, end);
|
|
8699
|
+
}, 1000);
|
|
8700
|
+
}
|
|
8701
|
+
}
|
|
8702
|
+
getFormValueFilter() {
|
|
8703
|
+
const value = this.form.value;
|
|
8704
|
+
// value.option is null
|
|
8705
|
+
if (!value.option) {
|
|
8706
|
+
throw new Error("filters.selectionInvalid");
|
|
8707
|
+
}
|
|
8708
|
+
if (value.option !== "custom-range") {
|
|
8709
|
+
const dateOption = this.dateOptions().find((option) => option.display === value.option);
|
|
8710
|
+
const aggregation = this.aggregation();
|
|
8711
|
+
if (!aggregation) {
|
|
8712
|
+
throw new Error("filters.aggregationNotFound");
|
|
8713
|
+
}
|
|
8714
|
+
return {
|
|
8715
|
+
name: aggregation.name,
|
|
8716
|
+
operator: dateOption?.operator || "eq",
|
|
8717
|
+
field: aggregation.column,
|
|
8718
|
+
display: dateOption?.display ?? "",
|
|
8719
|
+
filters: dateOption?.filters,
|
|
8720
|
+
value: dateOption?.value
|
|
8721
|
+
};
|
|
8722
|
+
}
|
|
8723
|
+
else if (value.customRange) {
|
|
8724
|
+
// if to is null, operator is gte
|
|
8725
|
+
// if from is null, operator is lte
|
|
8726
|
+
// if both are not null, operator is between
|
|
8727
|
+
const aggregation = this.aggregation();
|
|
8728
|
+
if (!aggregation) {
|
|
8729
|
+
throw new Error("filters.aggregationNotFound");
|
|
8730
|
+
}
|
|
8731
|
+
const filter = {
|
|
8732
|
+
name: aggregation.name,
|
|
8733
|
+
field: aggregation.column,
|
|
8734
|
+
display: value.option
|
|
8735
|
+
};
|
|
8736
|
+
if (value.customRange.from && value.customRange.to) {
|
|
8737
|
+
filter.operator = "between";
|
|
8738
|
+
filter.start = value.customRange.from;
|
|
8739
|
+
filter.end = value.customRange.to;
|
|
8740
|
+
}
|
|
8741
|
+
else if (value.customRange.from) {
|
|
8742
|
+
filter.operator = "gte";
|
|
8743
|
+
filter.value = value.customRange.from;
|
|
8744
|
+
}
|
|
8745
|
+
else if (value.customRange.to) {
|
|
8746
|
+
filter.operator = "lte";
|
|
8747
|
+
filter.value = value.customRange.to;
|
|
8748
|
+
}
|
|
8749
|
+
else {
|
|
8750
|
+
throw new Error("filters.customRangeInvalid");
|
|
8751
|
+
}
|
|
8752
|
+
return filter;
|
|
8753
|
+
}
|
|
8754
|
+
throw new Error("filters.filterInvalid");
|
|
8755
|
+
}
|
|
8756
|
+
onDateChange() {
|
|
8757
|
+
// Get the selected dates
|
|
8758
|
+
const [start, end] = this.datepicker()?.getDates() || [];
|
|
8759
|
+
// If both start and end are not selected, reset the datepicker options
|
|
8760
|
+
if (!start && !end) {
|
|
8761
|
+
this.datepicker()?.setOptions({
|
|
8762
|
+
minDate: undefined,
|
|
8763
|
+
maxDate: this.today
|
|
8764
|
+
});
|
|
8765
|
+
}
|
|
8766
|
+
// Update the form with the selected dates
|
|
8767
|
+
if (start && end) {
|
|
8768
|
+
this.form.patchValue({
|
|
8769
|
+
customRange: {
|
|
8770
|
+
from: start.toISOString(),
|
|
8771
|
+
to: end.toISOString()
|
|
8772
|
+
}
|
|
8773
|
+
});
|
|
8774
|
+
}
|
|
8775
|
+
}
|
|
8776
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationDateComponent, deps: [{ token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
8777
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AggregationDateComponent, isStandalone: true, selector: "aggregation-date, AggregationDate, aggregationdate", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, displayEmptyDistributionIntervals: { classPropertyName: "displayEmptyDistributionIntervals", publicName: "displayEmptyDistributionIntervals", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "@container" }, providers: [provideTranslocoScope("filters")], viewQueries: [{ propertyName: "datepicker", first: true, predicate: DateRangePickerDirective, descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible()\"\r\n class=\"m-0 flex h-8 w-full items-center pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @if (aggregation()?.icon) {\r\n <i class=\"fa-fw {{ aggregation()?.icon }} mr-1\" aria-hidden=\"true\"></i>\r\n }\r\n <span class=\"grow\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (hasFilters()) {\r\n <button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"clear()\"\r\n (keydown.enter)=\"clear()\">\r\n <i class=\"fa-fw far fa-filter-circle-xmark\" aria-hidden=\"true\"></i>\r\n <span class=\"sr-only\">{{ \"filters.clearFilters\" | transloco }}</span>\r\n </button>\r\n }\r\n\r\n @if (selection() && validSelection()) {\r\n <button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"apply()\"\r\n (keydown.enter)=\"apply()\">\r\n <i class=\"fa-fw far fa-filter\" aria-hidden=\"true\"></i>\r\n <span class=\"sr-only\">{{ \"filters.applyFilters\" | transloco }}</span>\r\n </button>\r\n }\r\n @if (collapsible()) {\r\n <button\r\n variant=\"none\"\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n </button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n <form [formGroup]=\"form\">\r\n <ul\r\n class=\"scrollbar-thin flex max-h-[calc(var(--height,100%)-100px)] snap-y snap-start flex-col gap-1 overflow-auto pt-2\"\r\n role=\"list\">\r\n @for (option of dateOptions(); track $index) {\r\n <li\r\n role=\"listitem\"\r\n tabindex=\"0\"\r\n (click)=\"radio.click()\"\r\n [attr.aria-label]=\"option.display | syslang | transloco\"\r\n [class]=\"\r\n cn(\r\n 'flex p-0 px-2 leading-7',\r\n form.get('option')?.value === option.display && 'bg-accent',\r\n option.hidden && 'hidden',\r\n option.disabled && 'disabled pointer-events-none text-neutral-300'\r\n )\r\n \"\r\n [attr.aria-hidden]=\"option.disabled\">\r\n <input\r\n #radio\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-{{ option.display }}\"\r\n [attr.disabled]=\"option.disabled ? true : null\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n (click)=\"select()\"\r\n value=\"{{ option.display }}\" />\r\n\r\n <label\r\n for=\"date-filter-{{ option.display }}\"\r\n class=\"grow cursor-pointer p-1\">\r\n {{ option.display | syslang | transloco }}\r\n </label>\r\n </li>\r\n }\r\n\r\n @if (allowCustomRange) {\r\n <li\r\n role=\"listitem\"\r\n aria-label=\"custom range\"\r\n class=\"flex px-2 leading-7\"\r\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\r\n <input\r\n #radiorange\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-custom-range\"\r\n value=\"custom-range\"\r\n (click)=\"select()\" />\r\n\r\n <div\r\n class=\"@container flex grow gap-1 p-1 @max-[340px]:flex-wrap\"\r\n daterangepicker\r\n datepicker-buttons\r\n [datepicker-language]=\"lang()\"\r\n [datepicker-min]=\"form.get('customRange.from')?.value || undefined\"\r\n [datepicker-max]=\"\r\n form.get('customRange.to')?.value ?? today.toISOString()\r\n \"\r\n (click)=\"radiorange.click()\">\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-start\" class=\"min-w-10\">{{\r\n \"from\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-start\"\r\n name=\"start\"\r\n type=\"text\"\r\n class=\"h-8 min-w-[13ch]\" />\r\n </div>\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-end\" class=\"min-w-10 text-right\">{{\r\n \"to\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-end\"\r\n name=\"end\"\r\n type=\"text\"\r\n class=\"h-8 min-w-[13ch]\" />\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n </ul>\r\n </form>\r\n</details>\r\n", styles: [":host{display:block;min-width:200px}ul[role=list]{scrollbar-width:thin}\n"], dependencies: [{ kind: "directive", type: InputComponent, selector: "input[type=\"text\"], input[type=\"email\"], input[type=\"number\"], input[type=\"password\"], input[type=\"tel\"], input[type=\"url\"], input[type=\"time\"], input[type=\"file\"]", inputs: ["class", "variant", "decoration"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "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: "directive", type: DateRangePickerDirective, selector: "[daterangepicker]", inputs: ["datepicker-title", "datepicker-autohide", "datepicker-clear", "datepicker-today", "datepicker-buttons", "datepicker-today-highlight", "datepicker-language", "datepicker-format", "datepicker-max", "datepicker-min", "datepicker-orientation"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
|
|
8778
|
+
}
|
|
8779
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationDateComponent, decorators: [{
|
|
8780
|
+
type: Component,
|
|
8781
|
+
args: [{ selector: "aggregation-date, AggregationDate, aggregationdate", standalone: true, providers: [provideTranslocoScope("filters")], imports: [
|
|
8782
|
+
InputComponent,
|
|
8783
|
+
ButtonComponent,
|
|
8784
|
+
ListItemComponent,
|
|
8785
|
+
ReactiveFormsModule,
|
|
8786
|
+
TranslocoPipe,
|
|
8787
|
+
SyslangPipe,
|
|
8788
|
+
DateRangePickerDirective,
|
|
8789
|
+
ChevronRightIcon
|
|
8790
|
+
], host: {
|
|
8791
|
+
class: "@container"
|
|
8792
|
+
}, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible()\"\r\n class=\"m-0 flex h-8 w-full items-center pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @if (aggregation()?.icon) {\r\n <i class=\"fa-fw {{ aggregation()?.icon }} mr-1\" aria-hidden=\"true\"></i>\r\n }\r\n <span class=\"grow\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (hasFilters()) {\r\n <button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"clear()\"\r\n (keydown.enter)=\"clear()\">\r\n <i class=\"fa-fw far fa-filter-circle-xmark\" aria-hidden=\"true\"></i>\r\n <span class=\"sr-only\">{{ \"filters.clearFilters\" | transloco }}</span>\r\n </button>\r\n }\r\n\r\n @if (selection() && validSelection()) {\r\n <button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"apply()\"\r\n (keydown.enter)=\"apply()\">\r\n <i class=\"fa-fw far fa-filter\" aria-hidden=\"true\"></i>\r\n <span class=\"sr-only\">{{ \"filters.applyFilters\" | transloco }}</span>\r\n </button>\r\n }\r\n @if (collapsible()) {\r\n <button\r\n variant=\"none\"\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n </button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n <form [formGroup]=\"form\">\r\n <ul\r\n class=\"scrollbar-thin flex max-h-[calc(var(--height,100%)-100px)] snap-y snap-start flex-col gap-1 overflow-auto pt-2\"\r\n role=\"list\">\r\n @for (option of dateOptions(); track $index) {\r\n <li\r\n role=\"listitem\"\r\n tabindex=\"0\"\r\n (click)=\"radio.click()\"\r\n [attr.aria-label]=\"option.display | syslang | transloco\"\r\n [class]=\"\r\n cn(\r\n 'flex p-0 px-2 leading-7',\r\n form.get('option')?.value === option.display && 'bg-accent',\r\n option.hidden && 'hidden',\r\n option.disabled && 'disabled pointer-events-none text-neutral-300'\r\n )\r\n \"\r\n [attr.aria-hidden]=\"option.disabled\">\r\n <input\r\n #radio\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-{{ option.display }}\"\r\n [attr.disabled]=\"option.disabled ? true : null\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n (click)=\"select()\"\r\n value=\"{{ option.display }}\" />\r\n\r\n <label\r\n for=\"date-filter-{{ option.display }}\"\r\n class=\"grow cursor-pointer p-1\">\r\n {{ option.display | syslang | transloco }}\r\n </label>\r\n </li>\r\n }\r\n\r\n @if (allowCustomRange) {\r\n <li\r\n role=\"listitem\"\r\n aria-label=\"custom range\"\r\n class=\"flex px-2 leading-7\"\r\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\r\n <input\r\n #radiorange\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-custom-range\"\r\n value=\"custom-range\"\r\n (click)=\"select()\" />\r\n\r\n <div\r\n class=\"@container flex grow gap-1 p-1 @max-[340px]:flex-wrap\"\r\n daterangepicker\r\n datepicker-buttons\r\n [datepicker-language]=\"lang()\"\r\n [datepicker-min]=\"form.get('customRange.from')?.value || undefined\"\r\n [datepicker-max]=\"\r\n form.get('customRange.to')?.value ?? today.toISOString()\r\n \"\r\n (click)=\"radiorange.click()\">\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-start\" class=\"min-w-10\">{{\r\n \"from\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-start\"\r\n name=\"start\"\r\n type=\"text\"\r\n class=\"h-8 min-w-[13ch]\" />\r\n </div>\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-end\" class=\"min-w-10 text-right\">{{\r\n \"to\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-end\"\r\n name=\"end\"\r\n type=\"text\"\r\n class=\"h-8 min-w-[13ch]\" />\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n </ul>\r\n </form>\r\n</details>\r\n", styles: [":host{display:block;min-width:200px}ul[role=list]{scrollbar-width:thin}\n"] }]
|
|
8793
|
+
}], ctorParameters: () => [{ type: i0.DestroyRef }], propDecorators: { datepicker: [{ type: i0.ViewChild, args: [i0.forwardRef(() => DateRangePickerDirective), { isSignal: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], displayEmptyDistributionIntervals: [{ type: i0.Input, args: [{ isSignal: true, alias: "displayEmptyDistributionIntervals", required: false }] }] } });
|
|
8794
|
+
|
|
8795
|
+
/**
|
|
8796
|
+
* Component that allows users to select a date or a date range for filtering search results.
|
|
8797
|
+
*
|
|
8798
|
+
* @deprecated since version 4. Use `AggregationDateComponent` instead.
|
|
8799
|
+
*/
|
|
8800
|
+
class DateComponent extends AggregationDateComponent {
|
|
8801
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DateComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
8802
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: DateComponent, isStandalone: true, selector: "date-filter,DateFilter", host: { classAttribute: "@container" }, providers: [provideTranslocoScope("filters")], usesInheritance: true, ngImport: i0, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible()\"\r\n class=\"m-0 flex h-8 w-full items-center pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @if (aggregation()?.icon) {\r\n <i class=\"fa-fw {{ aggregation()?.icon }} mr-1\" aria-hidden=\"true\"></i>\r\n }\r\n <span class=\"grow\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (hasFilters()) {\r\n <button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"clear()\"\r\n (keydown.enter)=\"clear()\">\r\n <i class=\"fa-fw far fa-filter-circle-xmark\" aria-hidden=\"true\"></i>\r\n <span class=\"sr-only\">{{ \"filters.clearFilters\" | transloco }}</span>\r\n </button>\r\n }\r\n\r\n @if (selection() && validSelection()) {\r\n <button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"apply()\"\r\n (keydown.enter)=\"apply()\">\r\n <i class=\"fa-fw far fa-filter\" aria-hidden=\"true\"></i>\r\n <span class=\"sr-only\">{{ \"filters.applyFilters\" | transloco }}</span>\r\n </button>\r\n }\r\n @if (collapsible()) {\r\n <button\r\n variant=\"none\"\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n </button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n <form [formGroup]=\"form\">\r\n <ul\r\n class=\"scrollbar-thin flex max-h-[calc(var(--height,100%)-100px)] snap-y snap-start flex-col gap-1 overflow-auto pt-2\"\r\n role=\"list\">\r\n @for (option of dateOptions(); track $index) {\r\n <li\r\n role=\"listitem\"\r\n tabindex=\"0\"\r\n (click)=\"radio.click()\"\r\n [attr.aria-label]=\"option.display | syslang | transloco\"\r\n [class]=\"\r\n cn(\r\n 'flex p-0 px-2 leading-7',\r\n form.get('option')?.value === option.display && 'bg-accent',\r\n option.hidden && 'hidden',\r\n option.disabled && 'disabled pointer-events-none text-neutral-300'\r\n )\r\n \"\r\n [attr.aria-hidden]=\"option.disabled\">\r\n <input\r\n #radio\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-{{ option.display }}\"\r\n [attr.disabled]=\"option.disabled ? true : null\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n (click)=\"select()\"\r\n value=\"{{ option.display }}\" />\r\n\r\n <label\r\n for=\"date-filter-{{ option.display }}\"\r\n class=\"grow cursor-pointer p-1\">\r\n {{ option.display | syslang | transloco }}\r\n </label>\r\n </li>\r\n }\r\n\r\n @if (allowCustomRange) {\r\n <li\r\n role=\"listitem\"\r\n aria-label=\"custom range\"\r\n class=\"flex px-2 leading-7\"\r\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\r\n <input\r\n #radiorange\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-custom-range\"\r\n value=\"custom-range\"\r\n (click)=\"select()\" />\r\n\r\n <div\r\n class=\"@container flex grow gap-1 p-1 @max-[340px]:flex-wrap\"\r\n daterangepicker\r\n datepicker-buttons\r\n [datepicker-language]=\"lang()\"\r\n [datepicker-min]=\"form.get('customRange.from')?.value || undefined\"\r\n [datepicker-max]=\"\r\n form.get('customRange.to')?.value ?? today.toISOString()\r\n \"\r\n (click)=\"radiorange.click()\">\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-start\" class=\"min-w-10\">{{\r\n \"from\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-start\"\r\n name=\"start\"\r\n type=\"text\"\r\n class=\"h-8 min-w-[13ch]\" />\r\n </div>\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-end\" class=\"min-w-10 text-right\">{{\r\n \"to\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-end\"\r\n name=\"end\"\r\n type=\"text\"\r\n class=\"h-8 min-w-[13ch]\" />\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n </ul>\r\n </form>\r\n</details>\r\n", styles: [":host{display:block;min-width:200px}ul[role=list]{scrollbar-width:thin}\n"], dependencies: [{ kind: "directive", type: InputComponent, selector: "input[type=\"text\"], input[type=\"email\"], input[type=\"number\"], input[type=\"password\"], input[type=\"tel\"], input[type=\"url\"], input[type=\"time\"], input[type=\"file\"]", inputs: ["class", "variant", "decoration"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "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: "directive", type: DateRangePickerDirective, selector: "[daterangepicker]", inputs: ["datepicker-title", "datepicker-autohide", "datepicker-clear", "datepicker-today", "datepicker-buttons", "datepicker-today-highlight", "datepicker-language", "datepicker-format", "datepicker-max", "datepicker-min", "datepicker-orientation"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
|
|
8803
|
+
}
|
|
8804
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DateComponent, decorators: [{
|
|
8805
|
+
type: Component,
|
|
8806
|
+
args: [{ selector: "date-filter,DateFilter", standalone: true, providers: [provideTranslocoScope("filters")], imports: [
|
|
8807
|
+
InputComponent,
|
|
8808
|
+
ButtonComponent,
|
|
8809
|
+
ListItemComponent,
|
|
8810
|
+
ReactiveFormsModule,
|
|
8811
|
+
TranslocoPipe,
|
|
8812
|
+
SyslangPipe,
|
|
8813
|
+
DateRangePickerDirective,
|
|
8814
|
+
ChevronRightIcon
|
|
8815
|
+
], host: {
|
|
8816
|
+
class: "@container"
|
|
8817
|
+
}, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible()\"\r\n class=\"m-0 flex h-8 w-full items-center pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @if (aggregation()?.icon) {\r\n <i class=\"fa-fw {{ aggregation()?.icon }} mr-1\" aria-hidden=\"true\"></i>\r\n }\r\n <span class=\"grow\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (hasFilters()) {\r\n <button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"clear()\"\r\n (keydown.enter)=\"clear()\">\r\n <i class=\"fa-fw far fa-filter-circle-xmark\" aria-hidden=\"true\"></i>\r\n <span class=\"sr-only\">{{ \"filters.clearFilters\" | transloco }}</span>\r\n </button>\r\n }\r\n\r\n @if (selection() && validSelection()) {\r\n <button\r\n variant=\"ghost\"\r\n size=\"icon\"\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"apply()\"\r\n (keydown.enter)=\"apply()\">\r\n <i class=\"fa-fw far fa-filter\" aria-hidden=\"true\"></i>\r\n <span class=\"sr-only\">{{ \"filters.applyFilters\" | transloco }}</span>\r\n </button>\r\n }\r\n @if (collapsible()) {\r\n <button\r\n variant=\"none\"\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n </button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n <form [formGroup]=\"form\">\r\n <ul\r\n class=\"scrollbar-thin flex max-h-[calc(var(--height,100%)-100px)] snap-y snap-start flex-col gap-1 overflow-auto pt-2\"\r\n role=\"list\">\r\n @for (option of dateOptions(); track $index) {\r\n <li\r\n role=\"listitem\"\r\n tabindex=\"0\"\r\n (click)=\"radio.click()\"\r\n [attr.aria-label]=\"option.display | syslang | transloco\"\r\n [class]=\"\r\n cn(\r\n 'flex p-0 px-2 leading-7',\r\n form.get('option')?.value === option.display && 'bg-accent',\r\n option.hidden && 'hidden',\r\n option.disabled && 'disabled pointer-events-none text-neutral-300'\r\n )\r\n \"\r\n [attr.aria-hidden]=\"option.disabled\">\r\n <input\r\n #radio\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-{{ option.display }}\"\r\n [attr.disabled]=\"option.disabled ? true : null\"\r\n [attr.aria-disabled]=\"option.disabled\"\r\n (click)=\"select()\"\r\n value=\"{{ option.display }}\" />\r\n\r\n <label\r\n for=\"date-filter-{{ option.display }}\"\r\n class=\"grow cursor-pointer p-1\">\r\n {{ option.display | syslang | transloco }}\r\n </label>\r\n </li>\r\n }\r\n\r\n @if (allowCustomRange) {\r\n <li\r\n role=\"listitem\"\r\n aria-label=\"custom range\"\r\n class=\"flex px-2 leading-7\"\r\n [class.select]=\"form.get('option')?.value === 'custom-range'\">\r\n <input\r\n #radiorange\r\n type=\"radio\"\r\n formControlName=\"option\"\r\n id=\"date-filter-custom-range\"\r\n value=\"custom-range\"\r\n (click)=\"select()\" />\r\n\r\n <div\r\n class=\"@container flex grow gap-1 p-1 @max-[340px]:flex-wrap\"\r\n daterangepicker\r\n datepicker-buttons\r\n [datepicker-language]=\"lang()\"\r\n [datepicker-min]=\"form.get('customRange.from')?.value || undefined\"\r\n [datepicker-max]=\"\r\n form.get('customRange.to')?.value ?? today.toISOString()\r\n \"\r\n (click)=\"radiorange.click()\">\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-start\" class=\"min-w-10\">{{\r\n \"from\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-start\"\r\n name=\"start\"\r\n type=\"text\"\r\n class=\"h-8 min-w-[13ch]\" />\r\n </div>\r\n <div class=\"flex gap-1\">\r\n <label for=\"datepicker-range-end\" class=\"min-w-10 text-right\">{{\r\n \"to\" | transloco\r\n }}</label>\r\n <input\r\n id=\"datepicker-range-end\"\r\n name=\"end\"\r\n type=\"text\"\r\n class=\"h-8 min-w-[13ch]\" />\r\n </div>\r\n </div>\r\n </li>\r\n }\r\n </ul>\r\n </form>\r\n</details>\r\n", styles: [":host{display:block;min-width:200px}ul[role=list]{scrollbar-width:thin}\n"] }]
|
|
8818
|
+
}] });
|
|
8819
|
+
|
|
8820
|
+
class ArticleEntities {
|
|
8821
|
+
cn = cn;
|
|
8822
|
+
appStore = inject(AppStore);
|
|
8823
|
+
previewService = inject(PreviewService);
|
|
8824
|
+
article = input.required(...(ngDevMode ? [{ debugName: "article" }] : []));
|
|
8825
|
+
previewHighlights = computed(() => {
|
|
8826
|
+
const highlights = this.appStore.getWebServiceByType("preview")?.highlights
|
|
8827
|
+
?.split(",")
|
|
8828
|
+
.filter((h) => h !== "extractslocations" && h !== "matchlocations" && h !== "matchingpassages");
|
|
8829
|
+
return highlights?.map((highlight) => ({
|
|
8830
|
+
name: highlight,
|
|
8831
|
+
entity: highlight,
|
|
8832
|
+
metadata: this.getMetadata(this.article(), highlight)
|
|
8833
|
+
}));
|
|
8834
|
+
}, ...(ngDevMode ? [{ debugName: "previewHighlights" }] : []));
|
|
8835
|
+
getMetadata(article, entity) {
|
|
8836
|
+
const m = article[entity];
|
|
8837
|
+
return m ?? [];
|
|
8838
|
+
}
|
|
8839
|
+
navigation = signal(undefined, ...(ngDevMode ? [{ debugName: "navigation" }] : []));
|
|
8840
|
+
hovering = signal(undefined, ...(ngDevMode ? [{ debugName: "hovering" }] : []));
|
|
8841
|
+
hoverIndex = computed(() => this.navigation()?.value === this.hovering() ? (this.navigation()?.index ?? 0) : 0, ...(ngDevMode ? [{ debugName: "hoverIndex" }] : []));
|
|
8842
|
+
scrollTo(type, index, usePassageHighlighter = false) {
|
|
8843
|
+
// if previewData contains "matchingpassages" then we use the passage highlighter otherwise we use "extractslocations"
|
|
8844
|
+
const category = type || this.previewService.highlightCategory;
|
|
8845
|
+
this.previewService.events.set("scrollTo");
|
|
8846
|
+
this.previewService.sendMessage({ action: "select", id: `${category}_${index}`, usePassageHighlighter });
|
|
8847
|
+
}
|
|
8848
|
+
navigate(entity, data, direction) {
|
|
8849
|
+
if (!data.count || data.count <= 0) {
|
|
8850
|
+
warn(`Invalid count for entity ${entity}:`, data.count);
|
|
8851
|
+
return;
|
|
8852
|
+
}
|
|
8853
|
+
const currentNavigation = this.navigation();
|
|
8854
|
+
const isCurrentValue = currentNavigation?.value === data.value;
|
|
8855
|
+
let newIndex;
|
|
8856
|
+
if (direction === "next") {
|
|
8857
|
+
newIndex = isCurrentValue && currentNavigation.index < data.count ? currentNavigation.index + 1 : 1;
|
|
8858
|
+
}
|
|
8859
|
+
else {
|
|
8860
|
+
newIndex = isCurrentValue && currentNavigation.index > 1 ? currentNavigation.index - 1 : data.count;
|
|
8861
|
+
}
|
|
8862
|
+
this.navigation.set({
|
|
8863
|
+
value: data.value,
|
|
8864
|
+
index: newIndex
|
|
8865
|
+
});
|
|
8866
|
+
const id = this.previewService.getEntityId(entity, data.value, newIndex - 1);
|
|
8867
|
+
if (id !== undefined) {
|
|
8868
|
+
this.scrollTo(entity, id);
|
|
8869
|
+
debug(`${direction} navigation:`, {
|
|
8870
|
+
id,
|
|
8097
8871
|
entity,
|
|
8098
8872
|
value: data.value,
|
|
8099
8873
|
index: newIndex,
|
|
@@ -8798,7 +9572,7 @@ class AlertDialog {
|
|
|
8798
9572
|
</DialogFooter>
|
|
8799
9573
|
</DialogContent>
|
|
8800
9574
|
</dialog>
|
|
8801
|
-
`, isInline: true, styles: [".weekdays-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(100px,1fr))}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.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", "size"] }, { kind: "component", type: DialogComponent, selector: "dialog, [dialog]", outputs: ["closed"], exportAs: ["dialog"] }, { kind: "component", type: DialogHeaderComponent, selector: "DialogHeader" }, { kind: "directive", type: DialogTitleComponent, selector: "DialogTitle", inputs: ["class"] }, { kind: "directive", type: DialogContentComponent, selector: "DialogContent", inputs: ["class"] }, { kind: "directive", type: DialogFooterComponent, selector: "DialogFooter", inputs: ["class"] }, { 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\"]", inputs: ["class", "variant", "decoration"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon", inputs: ["class"] }, { kind: "component", type: LoadingCircleIconComponent, selector: "loading-circle, LoadingCircle, loadingcircle", inputs: ["class"] }, { kind: "component", type: CircleCheckIconComponent, selector: "circle-check, CircleCheck, circlecheck", inputs: ["class"] }, { kind: "directive", type: SelectOptionDirective, selector: "option", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
9575
|
+
`, isInline: true, styles: [".weekdays-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(100px,1fr))}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.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", "size"] }, { kind: "component", type: DialogComponent, selector: "dialog, [dialog]", outputs: ["closed"], exportAs: ["dialog"] }, { kind: "component", type: DialogHeaderComponent, selector: "DialogHeader" }, { kind: "directive", type: DialogTitleComponent, selector: "DialogTitle", inputs: ["class"] }, { kind: "directive", type: DialogContentComponent, selector: "DialogContent", inputs: ["class"] }, { kind: "directive", type: DialogFooterComponent, selector: "DialogFooter", inputs: ["class"] }, { 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: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon", inputs: ["class"] }, { kind: "component", type: LoadingCircleIconComponent, selector: "loading-circle, LoadingCircle, loadingcircle", inputs: ["class"] }, { kind: "component", type: CircleCheckIconComponent, selector: "circle-check, CircleCheck, circlecheck", inputs: ["class"] }, { kind: "directive", type: SelectOptionDirective, selector: "option", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
8802
9576
|
}
|
|
8803
9577
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AlertDialog, decorators: [{
|
|
8804
9578
|
type: Component,
|
|
@@ -8987,6 +9761,7 @@ class ChangePasswordComponent {
|
|
|
8987
9761
|
username = input(null, ...(ngDevMode ? [{ debugName: "username" }] : []));
|
|
8988
9762
|
alert = input(undefined, ...(ngDevMode ? [{ debugName: "alert" }] : []));
|
|
8989
9763
|
redirectAfterSuccess = input(true, ...(ngDevMode ? [{ debugName: "redirectAfterSuccess" }] : []));
|
|
9764
|
+
redirectAfterCancel = input(true, ...(ngDevMode ? [{ debugName: "redirectAfterCancel" }] : []));
|
|
8990
9765
|
transloco = inject(TranslocoService);
|
|
8991
9766
|
router = inject(Router);
|
|
8992
9767
|
principalStore = inject(PrincipalStore);
|
|
@@ -9010,8 +9785,8 @@ class ChangePasswordComponent {
|
|
|
9010
9785
|
if (fromInput?.trim())
|
|
9011
9786
|
return fromInput.trim();
|
|
9012
9787
|
if (isAuthenticated()) {
|
|
9013
|
-
const p = this.principalStore
|
|
9014
|
-
const name = p
|
|
9788
|
+
const p = getState(this.principalStore);
|
|
9789
|
+
const name = p.name.trim();
|
|
9015
9790
|
if (name)
|
|
9016
9791
|
return name;
|
|
9017
9792
|
}
|
|
@@ -9046,7 +9821,6 @@ class ChangePasswordComponent {
|
|
|
9046
9821
|
if (!username) {
|
|
9047
9822
|
notify.info(this.transloco.translate("login.loginAfterPasswordChangeMissingUser"), { duration: 3000 });
|
|
9048
9823
|
this.router.navigate(["/login"]);
|
|
9049
|
-
this.success.emit();
|
|
9050
9824
|
return;
|
|
9051
9825
|
}
|
|
9052
9826
|
await this.clearLocalAuthState();
|
|
@@ -9087,10 +9861,12 @@ class ChangePasswordComponent {
|
|
|
9087
9861
|
this.pending.set(false);
|
|
9088
9862
|
this.errorMsg.set(null);
|
|
9089
9863
|
this.cancel.emit();
|
|
9090
|
-
this.
|
|
9864
|
+
if (this.redirectAfterCancel()) {
|
|
9865
|
+
this.location.back();
|
|
9866
|
+
}
|
|
9091
9867
|
}
|
|
9092
9868
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ChangePasswordComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9093
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: ChangePasswordComponent, isStandalone: true, selector: "change-password, ChangePassword, changepassword", inputs: { username: { classPropertyName: "username", publicName: "username", isSignal: true, isRequired: false, transformFunction: null }, alert: { classPropertyName: "alert", publicName: "alert", isSignal: true, isRequired: false, transformFunction: null }, redirectAfterSuccess: { classPropertyName: "redirectAfterSuccess", publicName: "redirectAfterSuccess", isSignal: true, isRequired: false, transformFunction: null }, currentPassword: { classPropertyName: "currentPassword", publicName: "currentPassword", isSignal: true, isRequired: false, transformFunction: null }, newPassword: { classPropertyName: "newPassword", publicName: "newPassword", isSignal: true, isRequired: false, transformFunction: null }, confirmPassword: { classPropertyName: "confirmPassword", publicName: "confirmPassword", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { success: "success", cancel: "cancel", currentPassword: "currentPasswordChange", newPassword: "newPasswordChange", confirmPassword: "confirmPasswordChange" }, providers: [provideTranslocoScope("login")], ngImport: i0, template: `
|
|
9869
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: ChangePasswordComponent, isStandalone: true, selector: "change-password, ChangePassword, changepassword", inputs: { username: { classPropertyName: "username", publicName: "username", isSignal: true, isRequired: false, transformFunction: null }, alert: { classPropertyName: "alert", publicName: "alert", isSignal: true, isRequired: false, transformFunction: null }, redirectAfterSuccess: { classPropertyName: "redirectAfterSuccess", publicName: "redirectAfterSuccess", isSignal: true, isRequired: false, transformFunction: null }, redirectAfterCancel: { classPropertyName: "redirectAfterCancel", publicName: "redirectAfterCancel", isSignal: true, isRequired: false, transformFunction: null }, currentPassword: { classPropertyName: "currentPassword", publicName: "currentPassword", isSignal: true, isRequired: false, transformFunction: null }, newPassword: { classPropertyName: "newPassword", publicName: "newPassword", isSignal: true, isRequired: false, transformFunction: null }, confirmPassword: { classPropertyName: "confirmPassword", publicName: "confirmPassword", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { success: "success", cancel: "cancel", currentPassword: "currentPasswordChange", newPassword: "newPasswordChange", confirmPassword: "confirmPasswordChange" }, providers: [provideTranslocoScope("login")], ngImport: i0, template: `
|
|
9094
9870
|
<Card hover="no" cdkTrapFocus cdkTrapFocusAutoCapture="true" class="bg-card rounded-xl shadow-sm">
|
|
9095
9871
|
<CardHeader class="flex flex-col items-center gap-3">
|
|
9096
9872
|
<img class="h-12 content-(--logo-large)" alt="logo" />
|
|
@@ -9198,14 +9974,11 @@ class ChangePasswordComponent {
|
|
|
9198
9974
|
</button>
|
|
9199
9975
|
</CardFooter>
|
|
9200
9976
|
</Card>
|
|
9201
|
-
`, isInline: true, 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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "directive", type: CardComponent, selector: ".card, card, Card", inputs: ["class", "variant", "hover"] }, { kind: "directive", type: CardHeaderComponent, selector: ".card-header, card-header, CardHeader, cardheader", inputs: ["class"] }, { kind: "directive", type: CardContentComponent, selector: ".card-content, card-content, CardContent, cardcontent", inputs: ["class"] }, { kind: "directive", type: CardFooterComponent, selector: ".card-footer, card-footer, CardFooter, cardfooter", inputs: ["class"] }, { kind: "directive", type: InputGroupInput, selector: "input[input-group]", inputs: ["class", "type", "placeholder", "disabled"] }, { kind: "directive", type: InputGroupComponent, selector: "input-group, inputgroup, InputGroup", inputs: ["class"] }, { kind: "directive", type: InputGroupAddonComponent, selector: "input-group-addon, inputgroupaddon, InputGroupAddon", inputs: ["class", "align"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
9977
|
+
`, isInline: true, styles: ["input::-ms-reveal{display: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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "directive", type: CardComponent, selector: ".card, card, Card", inputs: ["class", "variant", "hover"] }, { kind: "directive", type: CardHeaderComponent, selector: ".card-header, card-header, CardHeader, cardheader", inputs: ["class"] }, { kind: "directive", type: CardContentComponent, selector: ".card-content, card-content, CardContent, cardcontent", inputs: ["class"] }, { kind: "directive", type: CardFooterComponent, selector: ".card-footer, card-footer, CardFooter, cardfooter", inputs: ["class"] }, { kind: "directive", type: InputGroupInput, selector: "input[input-group]", inputs: ["class", "type", "placeholder", "disabled"] }, { kind: "directive", type: InputGroupComponent, selector: "input-group, inputgroup, InputGroup", inputs: ["class"] }, { kind: "directive", type: InputGroupAddonComponent, selector: "input-group-addon, inputgroupaddon, InputGroupAddon", inputs: ["class", "align"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
9202
9978
|
}
|
|
9203
9979
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ChangePasswordComponent, decorators: [{
|
|
9204
9980
|
type: Component,
|
|
9205
|
-
args: [{
|
|
9206
|
-
selector: "change-password, ChangePassword, changepassword",
|
|
9207
|
-
providers: [provideTranslocoScope("login")],
|
|
9208
|
-
imports: [
|
|
9981
|
+
args: [{ selector: "change-password, ChangePassword, changepassword", providers: [provideTranslocoScope("login")], imports: [
|
|
9209
9982
|
FormsModule,
|
|
9210
9983
|
A11yModule,
|
|
9211
9984
|
TranslocoPipe,
|
|
@@ -9217,8 +9990,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
9217
9990
|
InputGroupInput,
|
|
9218
9991
|
InputGroupComponent,
|
|
9219
9992
|
InputGroupAddonComponent
|
|
9220
|
-
],
|
|
9221
|
-
template: `
|
|
9993
|
+
], template: `
|
|
9222
9994
|
<Card hover="no" cdkTrapFocus cdkTrapFocusAutoCapture="true" class="bg-card rounded-xl shadow-sm">
|
|
9223
9995
|
<CardHeader class="flex flex-col items-center gap-3">
|
|
9224
9996
|
<img class="h-12 content-(--logo-large)" alt="logo" />
|
|
@@ -9326,9 +10098,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
9326
10098
|
</button>
|
|
9327
10099
|
</CardFooter>
|
|
9328
10100
|
</Card>
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
}], propDecorators: { success: [{ type: i0.Output, args: ["success"] }], cancel: [{ type: i0.Output, args: ["cancel"] }], username: [{ type: i0.Input, args: [{ isSignal: true, alias: "username", required: false }] }], alert: [{ type: i0.Input, args: [{ isSignal: true, alias: "alert", required: false }] }], redirectAfterSuccess: [{ type: i0.Input, args: [{ isSignal: true, alias: "redirectAfterSuccess", required: false }] }], currentPassword: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPassword", required: false }] }, { type: i0.Output, args: ["currentPasswordChange"] }], newPassword: [{ type: i0.Input, args: [{ isSignal: true, alias: "newPassword", required: false }] }, { type: i0.Output, args: ["newPasswordChange"] }], confirmPassword: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmPassword", required: false }] }, { type: i0.Output, args: ["confirmPasswordChange"] }] } });
|
|
10101
|
+
`, styles: ["input::-ms-reveal{display:none}\n"] }]
|
|
10102
|
+
}], propDecorators: { success: [{ type: i0.Output, args: ["success"] }], cancel: [{ type: i0.Output, args: ["cancel"] }], username: [{ type: i0.Input, args: [{ isSignal: true, alias: "username", required: false }] }], alert: [{ type: i0.Input, args: [{ isSignal: true, alias: "alert", required: false }] }], redirectAfterSuccess: [{ type: i0.Input, args: [{ isSignal: true, alias: "redirectAfterSuccess", required: false }] }], redirectAfterCancel: [{ type: i0.Input, args: [{ isSignal: true, alias: "redirectAfterCancel", required: false }] }], currentPassword: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentPassword", required: false }] }, { type: i0.Output, args: ["currentPasswordChange"] }], newPassword: [{ type: i0.Input, args: [{ isSignal: true, alias: "newPassword", required: false }] }, { type: i0.Output, args: ["newPasswordChange"] }], confirmPassword: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmPassword", required: false }] }, { type: i0.Output, args: ["confirmPasswordChange"] }] } });
|
|
9332
10103
|
|
|
9333
10104
|
class ForgotPasswordComponent {
|
|
9334
10105
|
cancel = output();
|
|
@@ -9400,7 +10171,7 @@ class ForgotPasswordComponent {
|
|
|
9400
10171
|
|
|
9401
10172
|
</CardFooter>
|
|
9402
10173
|
</Card>
|
|
9403
|
-
`, isInline: true, 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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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\"]", inputs: ["class", "variant", "decoration"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "directive", type: CardComponent, selector: ".card, card, Card", inputs: ["class", "variant", "hover"] }, { kind: "directive", type: CardHeaderComponent, selector: ".card-header, card-header, CardHeader, cardheader", inputs: ["class"] }, { kind: "directive", type: CardContentComponent, selector: ".card-content, card-content, CardContent, cardcontent", inputs: ["class"] }, { kind: "directive", type: CardFooterComponent, selector: ".card-footer, card-footer, CardFooter, cardfooter", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
10174
|
+
`, isInline: true, 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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { 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: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "directive", type: CardComponent, selector: ".card, card, Card", inputs: ["class", "variant", "hover"] }, { kind: "directive", type: CardHeaderComponent, selector: ".card-header, card-header, CardHeader, cardheader", inputs: ["class"] }, { kind: "directive", type: CardContentComponent, selector: ".card-content, card-content, CardContent, cardcontent", inputs: ["class"] }, { kind: "directive", type: CardFooterComponent, selector: ".card-footer, card-footer, CardFooter, cardfooter", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
9404
10175
|
}
|
|
9405
10176
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: ForgotPasswordComponent, decorators: [{
|
|
9406
10177
|
type: Component,
|
|
@@ -9475,55 +10246,40 @@ class SignInComponent {
|
|
|
9475
10246
|
username: this.username(),
|
|
9476
10247
|
password: this.password()
|
|
9477
10248
|
}), ...(ngDevMode ? [{ debugName: "credentials" }] : []));
|
|
9478
|
-
isValid = computed(() => !!this.credentials().username?.length &&
|
|
9479
|
-
|
|
9480
|
-
user = signal(null, ...(ngDevMode ? [{ debugName: "user" }] : []));
|
|
9481
|
-
returnUrl = signal(null, ...(ngDevMode ? [{ debugName: "returnUrl" }] : []));
|
|
10249
|
+
isValid = computed(() => !!this.credentials().username?.length &&
|
|
10250
|
+
!!this.credentials().password?.length, ...(ngDevMode ? [{ debugName: "isValid" }] : []));
|
|
9482
10251
|
router = inject(Router);
|
|
9483
10252
|
route = inject(ActivatedRoute);
|
|
9484
|
-
principalService = inject(PrincipalService);
|
|
9485
10253
|
applicationService = inject(ApplicationService);
|
|
9486
10254
|
principalStore = inject(PrincipalStore);
|
|
9487
10255
|
transloco = inject(TranslocoService);
|
|
10256
|
+
authenticated = signal(isAuthenticated(), ...(ngDevMode ? [{ debugName: "authenticated" }] : []));
|
|
10257
|
+
user = signal(getState(this.principalStore), ...(ngDevMode ? [{ debugName: "user" }] : []));
|
|
10258
|
+
expiresSoonNotified = signal(false, ...(ngDevMode ? [{ debugName: "expiresSoonNotified" }] : []));
|
|
9488
10259
|
constructor(destroyRef) {
|
|
9489
10260
|
this.destroyRef = destroyRef;
|
|
9490
10261
|
effect(() => {
|
|
9491
|
-
|
|
9492
|
-
|
|
9493
|
-
|
|
9494
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
9495
|
-
.subscribe((principal) => this.user.set(principal));
|
|
9496
|
-
}
|
|
9497
|
-
});
|
|
9498
|
-
effect(() => {
|
|
9499
|
-
const principal = this.user();
|
|
9500
|
-
if (this.authenticated() && principal) {
|
|
9501
|
-
this.checkPasswordExpiresSoon(principal);
|
|
9502
|
-
}
|
|
9503
|
-
});
|
|
9504
|
-
effect(() => {
|
|
9505
|
-
if (this.returnUrl() !== null) {
|
|
9506
|
-
const [url] = this.returnUrl() || ["/"];
|
|
9507
|
-
this.router.navigateByUrl(url);
|
|
10262
|
+
const principal = getState(this.principalStore);
|
|
10263
|
+
if (this.authenticated() && principal && !this.expiresSoonNotified()) {
|
|
10264
|
+
this.checkPasswordExpiresSoon();
|
|
9508
10265
|
}
|
|
9509
10266
|
});
|
|
9510
10267
|
fromEvent(window, "authenticated")
|
|
9511
10268
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
9512
|
-
.subscribe(
|
|
10269
|
+
.subscribe(event => {
|
|
9513
10270
|
this.authenticated.set(event.detail.authenticated);
|
|
9514
10271
|
const url = this.route.snapshot.queryParams["returnUrl"] || null;
|
|
9515
|
-
if (url !== null)
|
|
9516
|
-
this.returnUrl.set([url]);
|
|
9517
|
-
if (isAuthenticated()) {
|
|
10272
|
+
if (url !== null) {
|
|
9518
10273
|
this.router.navigateByUrl(url ?? "/");
|
|
9519
10274
|
}
|
|
9520
10275
|
});
|
|
9521
10276
|
}
|
|
9522
|
-
checkPasswordExpiresSoon(
|
|
9523
|
-
|
|
10277
|
+
checkPasswordExpiresSoon() {
|
|
10278
|
+
const { editablePartition, passwordExpirationDate } = getState(this.principalStore);
|
|
10279
|
+
if (!editablePartition || !passwordExpirationDate) {
|
|
9524
10280
|
return;
|
|
9525
10281
|
}
|
|
9526
|
-
if (expiresSoon(
|
|
10282
|
+
if (expiresSoon(passwordExpirationDate, 7)) {
|
|
9527
10283
|
notify.warning(this.transloco.translate("login.passwordExpiresSoon"), {
|
|
9528
10284
|
duration: Number.POSITIVE_INFINITY,
|
|
9529
10285
|
action: {
|
|
@@ -9538,17 +10294,16 @@ class SignInComponent {
|
|
|
9538
10294
|
}
|
|
9539
10295
|
}
|
|
9540
10296
|
});
|
|
10297
|
+
this.expiresSoonNotified.set(true);
|
|
9541
10298
|
}
|
|
9542
10299
|
}
|
|
9543
10300
|
async handleLogout() {
|
|
9544
10301
|
await logout();
|
|
9545
10302
|
this.authenticated.set(false);
|
|
9546
|
-
this.user.set(null);
|
|
9547
|
-
this.returnUrl.set(null);
|
|
9548
10303
|
this.router.navigate(["/login"]);
|
|
9549
10304
|
}
|
|
9550
10305
|
async handleLogin() {
|
|
9551
|
-
login().catch(
|
|
10306
|
+
login().catch(error => {
|
|
9552
10307
|
warn("An error occurred while logging in", error);
|
|
9553
10308
|
this.router.navigate(["error"]);
|
|
9554
10309
|
});
|
|
@@ -9562,14 +10317,14 @@ class SignInComponent {
|
|
|
9562
10317
|
return;
|
|
9563
10318
|
const { createRoutes = false } = globalConfig;
|
|
9564
10319
|
await this.applicationService.initialize(createRoutes);
|
|
9565
|
-
|
|
9566
|
-
this.checkPasswordExpiresSoon(p ?? null);
|
|
10320
|
+
this.checkPasswordExpiresSoon();
|
|
9567
10321
|
const url = this.route.snapshot.queryParams["returnUrl"] || "/";
|
|
9568
10322
|
this.router.navigateByUrl(url);
|
|
9569
10323
|
}
|
|
9570
10324
|
catch (err) {
|
|
9571
10325
|
const { status, errorMessage } = err;
|
|
9572
|
-
if (status === 401 &&
|
|
10326
|
+
if (status === 401 &&
|
|
10327
|
+
errorMessage?.toLowerCase().includes("password expired")) {
|
|
9573
10328
|
sessionStorage.setItem("passwordExpiredFlow", "true");
|
|
9574
10329
|
this.router.navigate(["/login"], {
|
|
9575
10330
|
queryParams: {
|
|
@@ -9582,94 +10337,82 @@ class SignInComponent {
|
|
|
9582
10337
|
}
|
|
9583
10338
|
notify.error("Login", { description: errorMessage });
|
|
9584
10339
|
}
|
|
9585
|
-
}
|
|
9586
|
-
handleBack() {
|
|
9587
|
-
this.authenticated.set(false);
|
|
9588
|
-
this.
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
}
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
<
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
</button>
|
|
9619
|
-
<button type="button" (click)="handleBack()">{{ 'login.back' | transloco }}</button>
|
|
9620
|
-
</CardFooter>
|
|
9621
|
-
} @else {
|
|
9622
|
-
<CardHeader class="flex flex-col items-center gap-3 text-center">
|
|
9623
|
-
<img class="content-(--logo-large) h-12" alt="logo" />
|
|
9624
|
-
</CardHeader>
|
|
9625
|
-
|
|
9626
|
-
<CardContent class="grid gap-4">
|
|
9627
|
-
@if (!config.autoOAuthProvider && !config.autoSAMLProvider) {
|
|
9628
|
-
<div class="grid gap-2">
|
|
9629
|
-
<label class="text-sm font-medium" for="username">{{
|
|
9630
|
-
'login.username' | transloco
|
|
9631
|
-
}}</label>
|
|
9632
|
-
<input
|
|
9633
|
-
id="username"
|
|
9634
|
-
type="text"
|
|
9635
|
-
required
|
|
9636
|
-
[(ngModel)]="username"
|
|
9637
|
-
(keydown.enter)="handleLoginWithCredentials()"
|
|
9638
|
-
/>
|
|
9639
|
-
</div>
|
|
10340
|
+
}
|
|
10341
|
+
handleBack() {
|
|
10342
|
+
this.authenticated.set(false);
|
|
10343
|
+
this.router.navigate(["/"]);
|
|
10344
|
+
}
|
|
10345
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: SignInComponent, deps: [{ token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
10346
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: SignInComponent, isStandalone: true, selector: "signIn, signin, sign-in", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, username: { classPropertyName: "username", publicName: "username", isSignal: true, isRequired: false, transformFunction: null }, password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { forgotPassword: "forgotPassword", username: "usernameChange", password: "passwordChange" }, host: { properties: { "class": "cn('grid h-dvh w-full place-content-center', class())" } }, providers: [provideTranslocoScope("login")], ngImport: i0, template: `
|
|
10347
|
+
@if (!authenticated()) {
|
|
10348
|
+
<Card
|
|
10349
|
+
hover="no"
|
|
10350
|
+
cdkTrapFocus
|
|
10351
|
+
cdkTrapFocusAutoCapture="true"
|
|
10352
|
+
class="bg-card rounded-xl shadow-sm">
|
|
10353
|
+
<CardHeader class="flex flex-col items-center gap-3 text-center">
|
|
10354
|
+
<img class="h-12 content-(--logo-large)" alt="logo" />
|
|
10355
|
+
</CardHeader>
|
|
10356
|
+
|
|
10357
|
+
<CardContent class="grid gap-4">
|
|
10358
|
+
@let useCredentials =
|
|
10359
|
+
!config.autoOAuthProvider && !config.autoSAMLProvider;
|
|
10360
|
+
@if (useCredentials) {
|
|
10361
|
+
<!-- authentication using credentials -->
|
|
10362
|
+
<div class="grid gap-2">
|
|
10363
|
+
<label class="text-sm font-medium" for="username">{{
|
|
10364
|
+
"login.username" | transloco
|
|
10365
|
+
}}</label>
|
|
10366
|
+
<input
|
|
10367
|
+
id="username"
|
|
10368
|
+
type="text"
|
|
10369
|
+
required
|
|
10370
|
+
[(ngModel)]="username"
|
|
10371
|
+
(keydown.enter)="handleLoginWithCredentials()" />
|
|
10372
|
+
</div>
|
|
9640
10373
|
|
|
9641
10374
|
<div class="grid gap-2">
|
|
9642
|
-
<label class="text-sm font-medium" for="password">{{
|
|
9643
|
-
|
|
10375
|
+
<label class="text-sm font-medium" for="password">{{
|
|
10376
|
+
"login.password" | transloco
|
|
10377
|
+
}}</label>
|
|
10378
|
+
<input
|
|
10379
|
+
id="password"
|
|
10380
|
+
type="password"
|
|
10381
|
+
required
|
|
10382
|
+
[(ngModel)]="password"
|
|
10383
|
+
(keydown.enter)="handleLoginWithCredentials()" />
|
|
9644
10384
|
</div>
|
|
9645
10385
|
|
|
9646
|
-
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
|
|
10386
|
+
<span
|
|
10387
|
+
class="text-muted-foreground cursor-pointer justify-self-start text-xs hover:underline"
|
|
10388
|
+
role="button"
|
|
10389
|
+
tabindex="0"
|
|
9650
10390
|
(click)="forgotPassword.emit()"
|
|
9651
|
-
|
|
9652
|
-
|
|
9653
|
-
|
|
9654
|
-
<button
|
|
9655
|
-
|
|
10391
|
+
(keydown.enter)="forgotPassword.emit()">
|
|
10392
|
+
{{ "login.forgotPassword" | transloco }}
|
|
10393
|
+
</span>
|
|
10394
|
+
<button
|
|
10395
|
+
[disabled]="!isValid()"
|
|
10396
|
+
(click)="handleLoginWithCredentials()">
|
|
10397
|
+
{{ "login.connect" | transloco }}
|
|
9656
10398
|
</button>
|
|
9657
|
-
|
|
9658
|
-
|
|
10399
|
+
}
|
|
10400
|
+
@else {
|
|
10401
|
+
<!-- authentication using OAuth or SAML provider -->
|
|
9659
10402
|
<button (click)="handleLogin()">
|
|
9660
|
-
{{
|
|
10403
|
+
{{ "login.SignInWith" | transloco : { provider: config.autoOAuthProvider ? "OAuth" : "SAML" } }}
|
|
9661
10404
|
</button>
|
|
9662
10405
|
}
|
|
9663
10406
|
</CardContent>
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
10407
|
+
</Card>
|
|
10408
|
+
} @else {
|
|
10409
|
+
<app-wait />
|
|
10410
|
+
}
|
|
10411
|
+
`, isInline: true, styles: ["input{background-color:var(--background)}\n"], dependencies: [{ kind: "ngmodule", type: RouterModule }, { 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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: A11yModule }, { kind: "directive", type: i2.CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { 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: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "directive", type: CardComponent, selector: ".card, card, Card", inputs: ["class", "variant", "hover"] }, { kind: "directive", type: CardHeaderComponent, selector: ".card-header, card-header, CardHeader, cardheader", inputs: ["class"] }, { kind: "directive", type: CardContentComponent, selector: ".card-content, card-content, CardContent, cardcontent", inputs: ["class"] }, { kind: "component", type: LoadingComponent, selector: "app-wait" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
9667
10412
|
}
|
|
9668
10413
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: SignInComponent, decorators: [{
|
|
9669
10414
|
type: Component,
|
|
9670
|
-
args: [{
|
|
9671
|
-
selector: "signIn, signin, sign-in",
|
|
9672
|
-
imports: [
|
|
10415
|
+
args: [{ selector: "signIn, signin, sign-in", imports: [
|
|
9673
10416
|
RouterModule,
|
|
9674
10417
|
FormsModule,
|
|
9675
10418
|
A11yModule,
|
|
@@ -9679,87 +10422,75 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
9679
10422
|
CardComponent,
|
|
9680
10423
|
CardHeaderComponent,
|
|
9681
10424
|
CardContentComponent,
|
|
9682
|
-
|
|
9683
|
-
],
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
<
|
|
9695
|
-
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
</button>
|
|
9711
|
-
<button type="button" (click)="handleBack()">{{ 'login.back' | transloco }}</button>
|
|
9712
|
-
</CardFooter>
|
|
9713
|
-
} @else {
|
|
9714
|
-
<CardHeader class="flex flex-col items-center gap-3 text-center">
|
|
9715
|
-
<img class="content-(--logo-large) h-12" alt="logo" />
|
|
9716
|
-
</CardHeader>
|
|
9717
|
-
|
|
9718
|
-
<CardContent class="grid gap-4">
|
|
9719
|
-
@if (!config.autoOAuthProvider && !config.autoSAMLProvider) {
|
|
9720
|
-
<div class="grid gap-2">
|
|
9721
|
-
<label class="text-sm font-medium" for="username">{{
|
|
9722
|
-
'login.username' | transloco
|
|
9723
|
-
}}</label>
|
|
9724
|
-
<input
|
|
9725
|
-
id="username"
|
|
9726
|
-
type="text"
|
|
9727
|
-
required
|
|
9728
|
-
[(ngModel)]="username"
|
|
9729
|
-
(keydown.enter)="handleLoginWithCredentials()"
|
|
9730
|
-
/>
|
|
9731
|
-
</div>
|
|
10425
|
+
LoadingComponent
|
|
10426
|
+
], providers: [provideTranslocoScope("login")], template: `
|
|
10427
|
+
@if (!authenticated()) {
|
|
10428
|
+
<Card
|
|
10429
|
+
hover="no"
|
|
10430
|
+
cdkTrapFocus
|
|
10431
|
+
cdkTrapFocusAutoCapture="true"
|
|
10432
|
+
class="bg-card rounded-xl shadow-sm">
|
|
10433
|
+
<CardHeader class="flex flex-col items-center gap-3 text-center">
|
|
10434
|
+
<img class="h-12 content-(--logo-large)" alt="logo" />
|
|
10435
|
+
</CardHeader>
|
|
10436
|
+
|
|
10437
|
+
<CardContent class="grid gap-4">
|
|
10438
|
+
@let useCredentials =
|
|
10439
|
+
!config.autoOAuthProvider && !config.autoSAMLProvider;
|
|
10440
|
+
@if (useCredentials) {
|
|
10441
|
+
<!-- authentication using credentials -->
|
|
10442
|
+
<div class="grid gap-2">
|
|
10443
|
+
<label class="text-sm font-medium" for="username">{{
|
|
10444
|
+
"login.username" | transloco
|
|
10445
|
+
}}</label>
|
|
10446
|
+
<input
|
|
10447
|
+
id="username"
|
|
10448
|
+
type="text"
|
|
10449
|
+
required
|
|
10450
|
+
[(ngModel)]="username"
|
|
10451
|
+
(keydown.enter)="handleLoginWithCredentials()" />
|
|
10452
|
+
</div>
|
|
9732
10453
|
|
|
9733
10454
|
<div class="grid gap-2">
|
|
9734
|
-
<label class="text-sm font-medium" for="password">{{
|
|
9735
|
-
|
|
10455
|
+
<label class="text-sm font-medium" for="password">{{
|
|
10456
|
+
"login.password" | transloco
|
|
10457
|
+
}}</label>
|
|
10458
|
+
<input
|
|
10459
|
+
id="password"
|
|
10460
|
+
type="password"
|
|
10461
|
+
required
|
|
10462
|
+
[(ngModel)]="password"
|
|
10463
|
+
(keydown.enter)="handleLoginWithCredentials()" />
|
|
9736
10464
|
</div>
|
|
9737
10465
|
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
10466
|
+
<span
|
|
10467
|
+
class="text-muted-foreground cursor-pointer justify-self-start text-xs hover:underline"
|
|
10468
|
+
role="button"
|
|
10469
|
+
tabindex="0"
|
|
9742
10470
|
(click)="forgotPassword.emit()"
|
|
9743
|
-
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
<button
|
|
9747
|
-
|
|
10471
|
+
(keydown.enter)="forgotPassword.emit()">
|
|
10472
|
+
{{ "login.forgotPassword" | transloco }}
|
|
10473
|
+
</span>
|
|
10474
|
+
<button
|
|
10475
|
+
[disabled]="!isValid()"
|
|
10476
|
+
(click)="handleLoginWithCredentials()">
|
|
10477
|
+
{{ "login.connect" | transloco }}
|
|
9748
10478
|
</button>
|
|
9749
|
-
|
|
9750
|
-
|
|
10479
|
+
}
|
|
10480
|
+
@else {
|
|
10481
|
+
<!-- authentication using OAuth or SAML provider -->
|
|
9751
10482
|
<button (click)="handleLogin()">
|
|
9752
|
-
{{
|
|
10483
|
+
{{ "login.SignInWith" | transloco : { provider: config.autoOAuthProvider ? "OAuth" : "SAML" } }}
|
|
9753
10484
|
</button>
|
|
9754
10485
|
}
|
|
9755
10486
|
</CardContent>
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
10487
|
+
</Card>
|
|
10488
|
+
} @else {
|
|
10489
|
+
<app-wait />
|
|
10490
|
+
}
|
|
10491
|
+
`, host: {
|
|
9760
10492
|
"[class]": "cn('grid h-dvh w-full place-content-center', class())"
|
|
9761
|
-
}
|
|
9762
|
-
}]
|
|
10493
|
+
}, styles: ["input{background-color:var(--background)}\n"] }]
|
|
9763
10494
|
}], ctorParameters: () => [{ type: i0.DestroyRef }], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], forgotPassword: [{ type: i0.Output, args: ["forgotPassword"] }], username: [{ type: i0.Input, args: [{ isSignal: true, alias: "username", required: false }] }, { type: i0.Output, args: ["usernameChange"] }], password: [{ type: i0.Input, args: [{ isSignal: true, alias: "password", required: false }] }, { type: i0.Output, args: ["passwordChange"] }] } });
|
|
9764
10495
|
|
|
9765
10496
|
class AuthPageComponent {
|
|
@@ -9801,7 +10532,7 @@ class AuthPageComponent {
|
|
|
9801
10532
|
/>
|
|
9802
10533
|
}
|
|
9803
10534
|
</div>
|
|
9804
|
-
`, isInline: true, dependencies: [{ kind: "component", type: SignInComponent, selector: "signIn, signin, sign-in", inputs: ["class", "username", "password"], outputs: ["forgotPassword", "usernameChange", "passwordChange"] }, { kind: "component", type: ChangePasswordComponent, selector: "change-password, ChangePassword, changepassword", inputs: ["username", "alert", "redirectAfterSuccess", "currentPassword", "newPassword", "confirmPassword"], outputs: ["success", "cancel", "currentPasswordChange", "newPasswordChange", "confirmPasswordChange"] }, { kind: "component", type: ForgotPasswordComponent, selector: "forgot-password, ForgotPassword, forgotpassword", inputs: ["userName"], outputs: ["cancel", "success", "userNameChange"] }] });
|
|
10535
|
+
`, isInline: true, dependencies: [{ kind: "component", type: SignInComponent, selector: "signIn, signin, sign-in", inputs: ["class", "username", "password"], outputs: ["forgotPassword", "usernameChange", "passwordChange"] }, { kind: "component", type: ChangePasswordComponent, selector: "change-password, ChangePassword, changepassword", inputs: ["username", "alert", "redirectAfterSuccess", "redirectAfterCancel", "currentPassword", "newPassword", "confirmPassword"], outputs: ["success", "cancel", "currentPasswordChange", "newPasswordChange", "confirmPasswordChange"] }, { kind: "component", type: ForgotPasswordComponent, selector: "forgot-password, ForgotPassword, forgotpassword", inputs: ["userName"], outputs: ["cancel", "success", "userNameChange"] }] });
|
|
9805
10536
|
}
|
|
9806
10537
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AuthPageComponent, decorators: [{
|
|
9807
10538
|
type: Component,
|
|
@@ -10154,7 +10885,7 @@ class OverrideUserDialogComponent {
|
|
|
10154
10885
|
this.appService
|
|
10155
10886
|
.initialize(createRoutes)
|
|
10156
10887
|
.then(() => {
|
|
10157
|
-
const
|
|
10888
|
+
const fullName = this.principalStore.fullName();
|
|
10158
10889
|
notify.success(`Welcome back ${fullName}!`, { duration: 2000 });
|
|
10159
10890
|
})
|
|
10160
10891
|
.catch((err) => {
|
|
@@ -10170,12 +10901,11 @@ class OverrideUserDialogComponent {
|
|
|
10170
10901
|
this.appService
|
|
10171
10902
|
.initialize(createRoutes)
|
|
10172
10903
|
.then(() => {
|
|
10173
|
-
const
|
|
10904
|
+
const fullName = this.principalStore.fullName();
|
|
10174
10905
|
notify.success(`Welcome back ${fullName}!`, { duration: 2000 });
|
|
10175
10906
|
})
|
|
10176
10907
|
.catch((err) => {
|
|
10177
10908
|
error("An error occured while overriding (initialize)", err);
|
|
10178
|
-
notify.error("An error occured while overriding (initialize)", { duration: 2000 });
|
|
10179
10909
|
setGlobalConfig({ userOverrideActive: false, userOverride: undefined });
|
|
10180
10910
|
});
|
|
10181
10911
|
}
|
|
@@ -10234,7 +10964,7 @@ class OverrideUserDialogComponent {
|
|
|
10234
10964
|
</DialogFooter>
|
|
10235
10965
|
</DialogContent>
|
|
10236
10966
|
</dialog>
|
|
10237
|
-
`, isInline: true, 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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { 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", "size"] }, { 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\"]", inputs: ["class", "variant", "decoration"] }, { kind: "component", type: DialogComponent, selector: "dialog, [dialog]", outputs: ["closed"], exportAs: ["dialog"] }, { kind: "directive", type: DialogContentComponent, selector: "DialogContent", inputs: ["class"] }, { kind: "directive", type: DialogTitleComponent, selector: "DialogTitle", inputs: ["class"] }, { kind: "component", type: DialogHeaderComponent, selector: "DialogHeader" }, { kind: "directive", type: DialogFooterComponent, selector: "DialogFooter", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
10967
|
+
`, isInline: true, 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.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { 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", "size"] }, { 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: "component", type: DialogComponent, selector: "dialog, [dialog]", outputs: ["closed"], exportAs: ["dialog"] }, { kind: "directive", type: DialogContentComponent, selector: "DialogContent", inputs: ["class"] }, { kind: "directive", type: DialogTitleComponent, selector: "DialogTitle", inputs: ["class"] }, { kind: "component", type: DialogHeaderComponent, selector: "DialogHeader" }, { kind: "directive", type: DialogFooterComponent, selector: "DialogFooter", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
10238
10968
|
}
|
|
10239
10969
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: OverrideUserDialogComponent, decorators: [{
|
|
10240
10970
|
type: Component,
|
|
@@ -10970,7 +11700,7 @@ class DrawerAdvancedFiltersComponent extends DrawerComponent {
|
|
|
10970
11700
|
return res;
|
|
10971
11701
|
}
|
|
10972
11702
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DrawerAdvancedFiltersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10973
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", 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}:is() .dropdown{width:100%}:is() .dropdown-content{width:50%}\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", "size"] }, { 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\"]", inputs: ["class", "variant", "decoration"] }, { kind: "directive", type: TabsComponent, selector: "tabs, Tabs", inputs: ["class"] }, { kind: "directive", type: TabComponent, selector: "tab, Tab", inputs: ["class", "variant", "size", "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" }] });
|
|
11703
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", 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}:is() .dropdown{width:100%}:is() .dropdown-content{width:50%}\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", "size"] }, { 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", "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" }] });
|
|
10974
11704
|
}
|
|
10975
11705
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: DrawerAdvancedFiltersComponent, decorators: [{
|
|
10976
11706
|
type: Component,
|
|
@@ -11868,7 +12598,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
11868
12598
|
args: [{ selector: 'feedback, Feedback', standalone: true, imports: [ButtonComponent, MenuComponent, MenuContentComponent, MenuItemComponent, TranslocoPipe, FeedbackDialogComponent], providers: [provideTranslocoScope('feedback')], template: "<menu>\n <button class=\"border border-foreground/18\" variant=\"outline\" title=\"{{ 'feedback.button' | transloco }}\">\n <i class=\"fa-fw far fa-commenting\" aria-hidden=\"true\"></i>\n {{ 'feedback.label' | transloco }}\n <i class=\"fa-fw fas fa-xmark ms-2\" (click)=\"close($event)\" aria-hidden=\"true\"></i>\n </button>\n\n <MenuContent>\n @if (!disliked()) {\n <menuitem (click)=\"like()\" [attr.aria-label]=\"'feedback.like' | transloco\">\n @if (liked()) {\n <i class=\"fa-fw fas fa-thumbs-up\"></i> {{ 'feedback.liked' | transloco }}\n } @else {\n <i class=\"fa-fw far fa-thumbs-up\"></i> {{ 'feedback.like' | transloco }}\n }\n </menuitem>\n }\n @if (!liked()) {\n <menuitem (click)=\"dislike()\" [attr.aria-label]=\"'feedback.dislike' | transloco\">\n @if (disliked()) {\n <i class=\"fa-fw fas fa-thumbs-down\"></i> {{ 'feedback.disliked' | transloco }}\n } @else {\n <i class=\"fa-fw far fa-thumbs-down\"></i> {{ 'feedback.dislike' | transloco }}\n }\n </menuitem>\n }\n @for (menu of menus; track $index) {\n <menuitem (click)=\"openFeedbackDialog(menu.type)\" [attr.aria-label]=\"'feedback.' + menu.type + '.title' | transloco\">\n <i class=\"fa-fw {{ menu.icon }}\"></i> {{ 'feedback.' + menu.type + '.title' | transloco }}\n </menuitem>\n }\n </MenuContent>\n</menu>\n\n<feedback-dialog />\n" }]
|
|
11869
12599
|
}], propDecorators: { onClose: [{ type: i0.Output, args: ["onClose"] }], feedbackDialog: [{ type: i0.ViewChild, args: [i0.forwardRef(() => FeedbackDialogComponent), { isSignal: true }] }], pages: [{ type: i0.Input, args: [{ isSignal: true, alias: "pages", required: true }] }] } });
|
|
11870
12600
|
|
|
11871
|
-
class
|
|
12601
|
+
class AggregationTreeItemComponent {
|
|
11872
12602
|
cn = cn;
|
|
11873
12603
|
get disabled() {
|
|
11874
12604
|
return this.node().count === 0 ? "disabled" : null;
|
|
@@ -11881,7 +12611,7 @@ class AggregationItemComponent {
|
|
|
11881
12611
|
field = input(...(ngDevMode ? [undefined, { debugName: "field" }] : []));
|
|
11882
12612
|
appStore = inject(AppStore);
|
|
11883
12613
|
queryParamsStore = inject(QueryParamsStore);
|
|
11884
|
-
searchText = inject(
|
|
12614
|
+
searchText = inject(AggregationTreeComponent).searchText;
|
|
11885
12615
|
// is the count of items displayed, default to false
|
|
11886
12616
|
showCount = computed(() => this.appStore.general()?.features?.showAggregationItemCount ?? false, ...(ngDevMode ? [{ debugName: "showCount" }] : []));
|
|
11887
12617
|
quickFilter = computed(() => this.appStore.general()?.features?.quickFilter, ...(ngDevMode ? [{ debugName: "quickFilter" }] : []));
|
|
@@ -11951,10 +12681,9 @@ class AggregationItemComponent {
|
|
|
11951
12681
|
}
|
|
11952
12682
|
}
|
|
11953
12683
|
onChildSelect(item) {
|
|
11954
|
-
this.onSelect.emit(undefined);
|
|
11955
12684
|
// if some items are selected visually, this means all these items got selected visually and if one
|
|
11956
12685
|
// got changed we need to change their selection status
|
|
11957
|
-
if (item && this.linkChildren() && this.node().items.some((i) => i.$selectedVisually)) {
|
|
12686
|
+
if (item && this.linkChildren() && !item.$selected && this.node().items.some((i) => i.$selectedVisually)) {
|
|
11958
12687
|
this.node().items.forEach((i) => {
|
|
11959
12688
|
if (i !== item) {
|
|
11960
12689
|
i.$selectedVisually = false;
|
|
@@ -11966,20 +12695,31 @@ class AggregationItemComponent {
|
|
|
11966
12695
|
this.node().$selected = false;
|
|
11967
12696
|
this.node().$selectedVisually = false;
|
|
11968
12697
|
}
|
|
12698
|
+
this.onSelect.emit(this.node());
|
|
11969
12699
|
}
|
|
11970
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type:
|
|
11971
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type:
|
|
12700
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationTreeItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12701
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AggregationTreeItemComponent, isStandalone: true, selector: "aggregation-tree-item, AggregationTreeItem, aggregationtreeitem", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, path: { classPropertyName: "path", publicName: "path", isSignal: true, isRequired: true, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", onOpen: "onOpen", onFilter: "onFilter" }, host: { properties: { "attr.disabled": "this.disabled" } }, ngImport: i0, template: "<a\r\n role=\"listitem\"\r\n [attr.aria-selected]=\"node().$selected || node().$selectedVisually\"\r\n [attr.aria-label]=\"name() | syslang\"\r\n [style.--level]=\"level()\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n node().count === 0 && 'disabled pointer-events-none text-neutral-300',\r\n (node().$selected || node().$selectedVisually) &&\r\n 'bg-primary-100 text-primary dark:text-primary-foreground'\r\n )\r\n \"\r\n (click)=\"select(node(), $event, true)\">\r\n <!-- chrevron is visible only if the node has children -->\r\n <button\r\n (click)=\"open($event, node())\"\r\n class=\"transition-transform ease-in hover:scale-125\"\r\n aria-label=\"Open\">\r\n <ChevronRight\r\n [class]=\"\r\n cn(\r\n 'size-4 translate-x-1',\r\n node().$opened && 'rotate-90',\r\n !node().hasChildren && 'hidden'\r\n )\r\n \"\r\n width=\"16\"\r\n height=\"16\" />\r\n </button>\r\n\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ node().value }}\"\r\n [attr.disabled]=\"node().count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"node().count === 0\"\r\n (keydown.enter)=\"select(node(), $event)\"\r\n [checked]=\"node().$selected || node().$selectedVisually\" />\r\n\r\n @if (node().icon) {\r\n <i\r\n class=\"fa-fw {{ node().icon }} self-center justify-self-center\"\r\n aria-hidden=\"true\"></i>\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isFiltered() ? 'filters.removeFilter' : 'filters.addFilter')\r\n | transloco) +\r\n ': ' +\r\n (name() | syslang)\r\n : (name() | syslang)\r\n \"\r\n (click)=\"onTextClick($event)\">\r\n @for (\r\n chunk of (name() | syslang) ?? \"\" | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && node().count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n node().count\r\n }}</span>\r\n }\r\n</a>\r\n\r\n@if (node().hasChildren && node().$opened) {\r\n @for (item of node().items; track $index) {\r\n <AggregationTreeItem\r\n [node]=\"item\"\r\n [path]=\"childrenPath()\"\r\n [field]=\"field()\"\r\n (onOpen)=\"onOpen.emit($event)\"\r\n (onFilter)=\"onFilter.emit()\"\r\n (onSelect)=\"onChildSelect($event)\" />\r\n }\r\n}\r\n", styles: [":host{display:block;-webkit-user-select:none;user-select:none}:host a{padding-left:calc((var(--agg-tree-indent, .5rem) * var(--level)))}a{line-height:var(--agg-item-height, inherit)}\n"], dependencies: [{ kind: "component", type: AggregationTreeItemComponent, selector: "aggregation-tree-item, AggregationTreeItem, aggregationtreeitem", inputs: ["node", "path", "field"], outputs: ["onSelect", "onOpen", "onFilter"] }, { kind: "directive", type: ListItemComponent, selector: "[role=\"listitem\"], [role=\"option\"]", inputs: ["class", "variant", "decoration"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon", inputs: ["class"] }, { kind: "pipe", type: HighlightWordPipe, name: "highlightWord" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
11972
12702
|
}
|
|
11973
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type:
|
|
12703
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationTreeItemComponent, decorators: [{
|
|
11974
12704
|
type: Component,
|
|
11975
|
-
args: [{ selector: "aggregation-item,
|
|
12705
|
+
args: [{ selector: "aggregation-tree-item, AggregationTreeItem, aggregationtreeitem", standalone: true, imports: [HighlightWordPipe, ListItemComponent, SyslangPipe, ChevronRightIcon, TranslocoPipe], template: "<a\r\n role=\"listitem\"\r\n [attr.aria-selected]=\"node().$selected || node().$selectedVisually\"\r\n [attr.aria-label]=\"name() | syslang\"\r\n [style.--level]=\"level()\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n node().count === 0 && 'disabled pointer-events-none text-neutral-300',\r\n (node().$selected || node().$selectedVisually) &&\r\n 'bg-primary-100 text-primary dark:text-primary-foreground'\r\n )\r\n \"\r\n (click)=\"select(node(), $event, true)\">\r\n <!-- chrevron is visible only if the node has children -->\r\n <button\r\n (click)=\"open($event, node())\"\r\n class=\"transition-transform ease-in hover:scale-125\"\r\n aria-label=\"Open\">\r\n <ChevronRight\r\n [class]=\"\r\n cn(\r\n 'size-4 translate-x-1',\r\n node().$opened && 'rotate-90',\r\n !node().hasChildren && 'hidden'\r\n )\r\n \"\r\n width=\"16\"\r\n height=\"16\" />\r\n </button>\r\n\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ node().value }}\"\r\n [attr.disabled]=\"node().count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"node().count === 0\"\r\n (keydown.enter)=\"select(node(), $event)\"\r\n [checked]=\"node().$selected || node().$selectedVisually\" />\r\n\r\n @if (node().icon) {\r\n <i\r\n class=\"fa-fw {{ node().icon }} self-center justify-self-center\"\r\n aria-hidden=\"true\"></i>\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isFiltered() ? 'filters.removeFilter' : 'filters.addFilter')\r\n | transloco) +\r\n ': ' +\r\n (name() | syslang)\r\n : (name() | syslang)\r\n \"\r\n (click)=\"onTextClick($event)\">\r\n @for (\r\n chunk of (name() | syslang) ?? \"\" | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && node().count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n node().count\r\n }}</span>\r\n }\r\n</a>\r\n\r\n@if (node().hasChildren && node().$opened) {\r\n @for (item of node().items; track $index) {\r\n <AggregationTreeItem\r\n [node]=\"item\"\r\n [path]=\"childrenPath()\"\r\n [field]=\"field()\"\r\n (onOpen)=\"onOpen.emit($event)\"\r\n (onFilter)=\"onFilter.emit()\"\r\n (onSelect)=\"onChildSelect($event)\" />\r\n }\r\n}\r\n", styles: [":host{display:block;-webkit-user-select:none;user-select:none}:host a{padding-left:calc((var(--agg-tree-indent, .5rem) * var(--level)))}a{line-height:var(--agg-item-height, inherit)}\n"] }]
|
|
11976
12706
|
}], propDecorators: { disabled: [{
|
|
11977
12707
|
type: HostBinding,
|
|
11978
12708
|
args: ["attr.disabled"]
|
|
11979
12709
|
}], onSelect: [{ type: i0.Output, args: ["onSelect"] }], onOpen: [{ type: i0.Output, args: ["onOpen"] }], onFilter: [{ type: i0.Output, args: ["onFilter"] }], node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }], path: [{ type: i0.Input, args: [{ isSignal: true, alias: "path", required: true }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }] } });
|
|
11980
12710
|
|
|
11981
|
-
class
|
|
12711
|
+
class AggregationTreeComponent {
|
|
11982
12712
|
cn = cn;
|
|
12713
|
+
virtualItems = viewChildren('virtualItem', ...(ngDevMode ? [{ debugName: "virtualItems" }] : []));
|
|
12714
|
+
scrollElement = viewChild("scrollElement", ...(ngDevMode ? [{ debugName: "scrollElement" }] : []));
|
|
12715
|
+
virtualizer = injectVirtualizer(() => ({
|
|
12716
|
+
count: this.items().length,
|
|
12717
|
+
estimateSize: () => 32,
|
|
12718
|
+
scrollElement: this.scrollElement()
|
|
12719
|
+
}));
|
|
12720
|
+
#measureItems = effect(() => this.virtualItems().forEach((el) => {
|
|
12721
|
+
this.virtualizer.measureElement(el.nativeElement);
|
|
12722
|
+
}), ...(ngDevMode ? [{ debugName: "#measureItems" }] : []));
|
|
11983
12723
|
searchInput = viewChild("searchInput", ...(ngDevMode ? [{ debugName: "searchInput" }] : []));
|
|
11984
12724
|
/* stores */
|
|
11985
12725
|
aggregationsStore = inject(AggregationsStore);
|
|
@@ -11997,6 +12737,7 @@ class AggregationComponent {
|
|
|
11997
12737
|
id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
11998
12738
|
name = input.required(...(ngDevMode ? [{ debugName: "name" }] : []));
|
|
11999
12739
|
column = input.required(...(ngDevMode ? [{ debugName: "column" }] : []));
|
|
12740
|
+
expandedLevel = input(undefined, ...(ngDevMode ? [{ debugName: "expandedLevel", transform: numberAttribute }] : [{ transform: numberAttribute }]));
|
|
12000
12741
|
onSelect = output();
|
|
12001
12742
|
/**
|
|
12002
12743
|
* Determines whether the aggregation component can be collapsed or expanded.
|
|
@@ -12005,7 +12746,7 @@ class AggregationComponent {
|
|
|
12005
12746
|
*
|
|
12006
12747
|
* @default false
|
|
12007
12748
|
*/
|
|
12008
|
-
collapsible = input(false, ...(ngDevMode ? [{ debugName: "collapsible"
|
|
12749
|
+
collapsible = input(false, ...(ngDevMode ? [{ debugName: "collapsible" }] : []));
|
|
12009
12750
|
/**
|
|
12010
12751
|
* Controls whether the aggregation component is in a collapsed state.
|
|
12011
12752
|
* When true, the component will be visually collapsed/hidden.
|
|
@@ -12013,7 +12754,7 @@ class AggregationComponent {
|
|
|
12013
12754
|
*
|
|
12014
12755
|
* @default false
|
|
12015
12756
|
*/
|
|
12016
|
-
collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed"
|
|
12757
|
+
collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : []));
|
|
12017
12758
|
/**
|
|
12018
12759
|
* A computed signal that tracks the collapsed state of the component.
|
|
12019
12760
|
* This signal is linked to the `collapsed()` signal and automatically updates
|
|
@@ -12027,13 +12768,13 @@ class AggregationComponent {
|
|
|
12027
12768
|
*
|
|
12028
12769
|
* @returns Empty string if not collapsed, null if collapsed
|
|
12029
12770
|
*/
|
|
12030
|
-
expanded = computed(() => (this.isCollapsed() ? null :
|
|
12771
|
+
expanded = computed(() => (this.isCollapsed() ? null : ''), ...(ngDevMode ? [{ debugName: "expanded" }] : []));
|
|
12031
12772
|
/**
|
|
12032
12773
|
* A boolean flag indicating whether the component is searchable.
|
|
12033
12774
|
* This property is initialized to `undefined` by default.
|
|
12034
12775
|
* "Undefined" and not "false" because this input overrides the custom json settings
|
|
12035
12776
|
*/
|
|
12036
|
-
searchable = input(undefined, ...(ngDevMode ? [{ debugName: "searchable"
|
|
12777
|
+
searchable = input(undefined, ...(ngDevMode ? [{ debugName: "searchable" }] : []));
|
|
12037
12778
|
selection = signal(false, ...(ngDevMode ? [{ debugName: "selection" }] : []));
|
|
12038
12779
|
/**
|
|
12039
12780
|
* A boolean flag indicating whether we want to see the filters count when some is applied
|
|
@@ -12049,6 +12790,12 @@ class AggregationComponent {
|
|
|
12049
12790
|
if (name !== null) {
|
|
12050
12791
|
const agg = this.aggregationsService.processAggregation(name, column);
|
|
12051
12792
|
if (agg) {
|
|
12793
|
+
if (!agg.isTree) {
|
|
12794
|
+
error("The aggregation tree component does not support list aggregations. Please use the <Aggregation /> component instead.");
|
|
12795
|
+
}
|
|
12796
|
+
// overrides "expandedLevel" from custom JSON file
|
|
12797
|
+
const expandedLevel = this.expandedLevel() ?? agg.expandedLevel ?? 0;
|
|
12798
|
+
this.expandItems(agg.items, expandedLevel);
|
|
12052
12799
|
// overrides "searchable" properties with the input if any
|
|
12053
12800
|
agg.searchable = this.searchable() ?? agg.searchable;
|
|
12054
12801
|
return agg;
|
|
@@ -12121,11 +12868,11 @@ class AggregationComponent {
|
|
|
12121
12868
|
}
|
|
12122
12869
|
// if the aggregation is not a tree, we return the suggestions as is
|
|
12123
12870
|
return this.suggests()?.map((suggest) => ({
|
|
12124
|
-
name:
|
|
12871
|
+
name: this.name(),
|
|
12125
12872
|
value: suggest.normalized || suggest.display || "",
|
|
12126
12873
|
display: suggest.display,
|
|
12127
|
-
column: suggest.
|
|
12128
|
-
count:
|
|
12874
|
+
column: suggest.category,
|
|
12875
|
+
count: Number(suggest.frequency),
|
|
12129
12876
|
$selected: false,
|
|
12130
12877
|
items: []
|
|
12131
12878
|
}));
|
|
@@ -12196,10 +12943,16 @@ class AggregationComponent {
|
|
|
12196
12943
|
const agg = this.aggregation();
|
|
12197
12944
|
if (agg) {
|
|
12198
12945
|
this.queryParamsStore.removeFilterByName(agg.name, agg.column);
|
|
12199
|
-
|
|
12200
|
-
item
|
|
12201
|
-
|
|
12202
|
-
|
|
12946
|
+
const unselect = (items) => {
|
|
12947
|
+
items.forEach((item) => {
|
|
12948
|
+
item.$selected = false;
|
|
12949
|
+
item.$selectedVisually = false;
|
|
12950
|
+
if (item.items)
|
|
12951
|
+
unselect(item.items);
|
|
12952
|
+
});
|
|
12953
|
+
};
|
|
12954
|
+
unselect(this.items());
|
|
12955
|
+
sessionStorage.removeItem(`agg-${this.aggregation()?.column}`);
|
|
12203
12956
|
this.selection.set(false);
|
|
12204
12957
|
this.isAllSelected.set(false);
|
|
12205
12958
|
}
|
|
@@ -12298,7 +13051,7 @@ class AggregationComponent {
|
|
|
12298
13051
|
* If the item is deselected, the selection count is decremented by 1.
|
|
12299
13052
|
*/
|
|
12300
13053
|
select() {
|
|
12301
|
-
this.onSelect.emit(
|
|
13054
|
+
this.onSelect.emit(this.getFlattenTreeItems().filter(item => item.$selected));
|
|
12302
13055
|
this.selection.set(true);
|
|
12303
13056
|
this.verifySelected();
|
|
12304
13057
|
sessionStorage.setItem(`agg-${this.aggregation()?.column}`, JSON.stringify([...this.items()]));
|
|
@@ -12399,7 +13152,9 @@ class AggregationComponent {
|
|
|
12399
13152
|
return flat.concat(item, item.items ? flattenItems(item.items) : []);
|
|
12400
13153
|
}, []);
|
|
12401
13154
|
};
|
|
12402
|
-
|
|
13155
|
+
// we need to flatten both the searched items and the current items to get all the items in the tree
|
|
13156
|
+
const searchedItemsFiltered = (this.searchedItems() || []).filter((item) => "items" in item);
|
|
13157
|
+
const searchItems = flattenItems(searchedItemsFiltered);
|
|
12403
13158
|
const items = flattenItems(this.aggregation()?.items || []);
|
|
12404
13159
|
const flattenedTreeItems = [...searchItems, ...items];
|
|
12405
13160
|
return flattenedTreeItems;
|
|
@@ -12491,287 +13246,312 @@ class AggregationComponent {
|
|
|
12491
13246
|
if (savedItem) {
|
|
12492
13247
|
item.$selected = savedItem.$selected;
|
|
12493
13248
|
item.$selectedVisually = savedItem.$selectedVisually;
|
|
12494
|
-
if (item.items?.length && savedItem.items?.length) {
|
|
12495
|
-
item.items = this.setSelected(item.items, savedItem.items);
|
|
12496
|
-
}
|
|
12497
|
-
}
|
|
12498
|
-
return item;
|
|
12499
|
-
});
|
|
12500
|
-
}
|
|
12501
|
-
processAggregations(items) {
|
|
12502
|
-
if (!this.linkChildren())
|
|
12503
|
-
return items;
|
|
12504
|
-
items.forEach((item) => {
|
|
12505
|
-
if (item.items?.length) {
|
|
12506
|
-
this.selectVisually(item.items, item.$selected || false);
|
|
12507
|
-
}
|
|
12508
|
-
});
|
|
12509
|
-
return items;
|
|
12510
|
-
}
|
|
12511
|
-
selectVisually(items, parentSelected) {
|
|
12512
|
-
items.forEach((item) => {
|
|
12513
|
-
item.$selectedVisually = parentSelected;
|
|
12514
|
-
if (item.items?.length)
|
|
12515
|
-
this.selectVisually(item.items, item.$selected || item.$selectedVisually);
|
|
12516
|
-
});
|
|
12517
|
-
}
|
|
12518
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12519
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AggregationComponent, isStandalone: true, selector: "Aggregation, aggregation", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, 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 }, searchText: { classPropertyName: "searchText", publicName: "searchText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", searchText: "searchTextChange" }, host: { properties: { "class": "cn(\"block h-[inherit] max-h-[inherit]\",class())" } }, viewQueries: [{ propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\n <summary [class.cursor-pointer]=\"collapsible() && !isEmpty()\" [class.text-muted-foreground]=\"isEmpty()\"\n class=\"m-0 mt-1 flex gap-1 h-8 w-full select-none items-center pl-1 font-semibold\" (click)=\"onHeaderClick($event)\">\n <ng-content select=\"label\">\n @if (aggregation()?.icon) {\n <i class=\"fa-fw {{ aggregation()?.icon }} mr-1\" aria-hidden=\"true\"></i>\n }\n <span class=\"grow\">{{ aggregation()?.display | syslang | transloco }}</span>\n </ng-content>\n\n @if (showFiltersCount() && filtersCount() > 0) {\n <!-- count -->\n <Badge size=\"xs\" class=\"ml-1 pb-0.5\">\n {{ filtersCount() }}\n </Badge>\n }\n <!-- apply filter block -->\n @if (!isCollapsed()) {\n <ButtonGroup>\n @if (hasFilters()) {\n <button variant=\"outline\" size=\"icon\" class=\"size-6\" [attr.title]=\"'filters.clearFilters' | transloco\"\n [attr.aria-label]=\"'filters.clearFilters' | transloco\" (click)=\"$event.stopPropagation(); clear()\">\n <i class=\"fa-fw far fa-filter-circle-xmark\"></i>\n </button>\n }\n @if (selection()) {\n <button variant=\"primary\" size=\"xs\" [attr.title]=\"'filters.applyFilters' | transloco\"\n [attr.aria-label]=\"'filters.applyFilters' | transloco\" (click)=\"$event.stopPropagation(); apply()\" class=\"px-1 h-4\">\n <FilterIcon class=\"size-4\"/>\n {{ 'filters.apply' | transloco }}\n </button>\n }\n\n <!-- select / unselect all -->\n @if (isAllSelected()) {\n <button variant=\"outline\" size=\"icon\" class=\"size-6\" [attr.title]=\"'filters.unselectAllFilters' | transloco\"\n [attr.aria-label]=\"'filters.unselectAllFilters' | transloco\" (click)=\"$event.stopPropagation(); unselectAll()\">\n <i class=\"fa-fw far fa-check-square\"></i>\n </button>\n } @else {\n <button variant=\"outline\" size=\"icon\" class=\"size-6\" [attr.title]=\"'filters.selectAllFilters' | transloco\"\n [attr.aria-label]=\"'filters.selectAllFilters' | transloco\" (click)=\"$event.stopPropagation(); selectAll()\">\n <i class=\"fa-fw far fa-square\"></i>\n </button>\n }\n </ButtonGroup>\n }\n\n @if (collapsible()) {\n <button variant=\"none\" title=\"Open/Close\"\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevronright />\n </button>\n }\n </summary>\n\n <!-- content wrapper -->\n @if (aggregation()?.searchable && items().length) {\n <InputGroup class=\"group/item mt-1\">\n <input #searchInput input-group id=\"aggregation-input-{{column()}}\" type=\"text\" [attr.placeholder]=\"'search' | transloco\" [(ngModel)]=\"searchText\" class=\"mt-1\"/>\n <InputGroupAddon>\n <SearchIcon class=\"text-foreground size-4 rotate-0 transition-[rotate] duration-500 group-focus-within/item:rotate-90\" />\n </InputGroupAddon>\n </InputGroup>\n }\n\n <ul class=\"scrollbar-thin max-h-[calc(var(--height,100%)-100px)] snap-y snap-start overflow-auto\" role=\"list\"\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\n @for (item of items(); track $index) {\n <AggregationItem [node]=\"item\" [path]=\"[]\" [field]=\"aggregation()?.column\" (onSelect)=\"select()\"\n (onOpen)=\"open($event)\" (onFilter)=\"apply()\" />\n }\n </ul>\n @if (aggregation()?.$hasMore && this.searchedItems().length === 0) {\n <button variant=\"link\" class=\"mt-1 flex w-full justify-center\" [attr.aria-label]=\"'loadMore' | transloco\"\n (click)=\"loadMore()\">\n {{ 'loadMore' | transloco }}\n </button>\n }\n</details>\n", styles: ["AggregationItem:has(+AggregationItem){margin-bottom:var(--agg-item-gap, 0)}\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: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "component", type: AggregationItemComponent, selector: "aggregation-item, AggregationItem, aggregationitem", inputs: ["node", "path", "field"], outputs: ["onSelect", "onOpen", "onFilter"] }, { kind: "directive", type: BadgeComponent, selector: "badge, Badge", inputs: ["class", "variant", "size"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon", inputs: ["class"] }, { kind: "directive", type: ButtonGroup, selector: "button-group, ButtonGroup", inputs: ["class", "orientation"] }, { kind: "directive", type: InputGroupInput, selector: "input[input-group]", inputs: ["class", "type", "placeholder", "disabled"] }, { kind: "directive", type: InputGroupComponent, selector: "input-group, inputgroup, InputGroup", inputs: ["class"] }, { kind: "directive", type: InputGroupAddonComponent, selector: "input-group-addon, inputgroupaddon, InputGroupAddon", inputs: ["class", "align"] }, { kind: "component", type: SearchIcon, selector: "SearchIcon", inputs: ["class"] }, { kind: "component", type: FilterIcon, selector: "filter-icon, FilterIcon", inputs: ["class"] }, { kind: "pipe", type: SyslangPipe, name: "syslang" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
12520
|
-
}
|
|
12521
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationComponent, decorators: [{
|
|
12522
|
-
type: Component,
|
|
12523
|
-
args: [{ selector: "Aggregation, aggregation", imports: [
|
|
12524
|
-
FormsModule,
|
|
12525
|
-
ReactiveFormsModule,
|
|
12526
|
-
ButtonComponent,
|
|
12527
|
-
AggregationItemComponent,
|
|
12528
|
-
SyslangPipe,
|
|
12529
|
-
TranslocoPipe,
|
|
12530
|
-
BadgeComponent,
|
|
12531
|
-
ChevronRightIcon,
|
|
12532
|
-
ButtonGroup,
|
|
12533
|
-
InputGroupInput,
|
|
12534
|
-
InputGroupComponent,
|
|
12535
|
-
InputGroupAddonComponent,
|
|
12536
|
-
SearchIcon,
|
|
12537
|
-
FilterIcon
|
|
12538
|
-
], standalone: true, host: {
|
|
12539
|
-
"[class]": 'cn("block h-[inherit] max-h-[inherit]",class())'
|
|
12540
|
-
}, template: "<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\n <summary [class.cursor-pointer]=\"collapsible() && !isEmpty()\" [class.text-muted-foreground]=\"isEmpty()\"\n class=\"m-0 mt-1 flex gap-1 h-8 w-full select-none items-center pl-1 font-semibold\" (click)=\"onHeaderClick($event)\">\n <ng-content select=\"label\">\n @if (aggregation()?.icon) {\n <i class=\"fa-fw {{ aggregation()?.icon }} mr-1\" aria-hidden=\"true\"></i>\n }\n <span class=\"grow\">{{ aggregation()?.display | syslang | transloco }}</span>\n </ng-content>\n\n @if (showFiltersCount() && filtersCount() > 0) {\n <!-- count -->\n <Badge size=\"xs\" class=\"ml-1 pb-0.5\">\n {{ filtersCount() }}\n </Badge>\n }\n <!-- apply filter block -->\n @if (!isCollapsed()) {\n <ButtonGroup>\n @if (hasFilters()) {\n <button variant=\"outline\" size=\"icon\" class=\"size-6\" [attr.title]=\"'filters.clearFilters' | transloco\"\n [attr.aria-label]=\"'filters.clearFilters' | transloco\" (click)=\"$event.stopPropagation(); clear()\">\n <i class=\"fa-fw far fa-filter-circle-xmark\"></i>\n </button>\n }\n @if (selection()) {\n <button variant=\"primary\" size=\"xs\" [attr.title]=\"'filters.applyFilters' | transloco\"\n [attr.aria-label]=\"'filters.applyFilters' | transloco\" (click)=\"$event.stopPropagation(); apply()\" class=\"px-1 h-4\">\n <FilterIcon class=\"size-4\"/>\n {{ 'filters.apply' | transloco }}\n </button>\n }\n\n <!-- select / unselect all -->\n @if (isAllSelected()) {\n <button variant=\"outline\" size=\"icon\" class=\"size-6\" [attr.title]=\"'filters.unselectAllFilters' | transloco\"\n [attr.aria-label]=\"'filters.unselectAllFilters' | transloco\" (click)=\"$event.stopPropagation(); unselectAll()\">\n <i class=\"fa-fw far fa-check-square\"></i>\n </button>\n } @else {\n <button variant=\"outline\" size=\"icon\" class=\"size-6\" [attr.title]=\"'filters.selectAllFilters' | transloco\"\n [attr.aria-label]=\"'filters.selectAllFilters' | transloco\" (click)=\"$event.stopPropagation(); selectAll()\">\n <i class=\"fa-fw far fa-square\"></i>\n </button>\n }\n </ButtonGroup>\n }\n\n @if (collapsible()) {\n <button variant=\"none\" title=\"Open/Close\"\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevronright />\n </button>\n }\n </summary>\n\n <!-- content wrapper -->\n @if (aggregation()?.searchable && items().length) {\n <InputGroup class=\"group/item mt-1\">\n <input #searchInput input-group id=\"aggregation-input-{{column()}}\" type=\"text\" [attr.placeholder]=\"'search' | transloco\" [(ngModel)]=\"searchText\" class=\"mt-1\"/>\n <InputGroupAddon>\n <SearchIcon class=\"text-foreground size-4 rotate-0 transition-[rotate] duration-500 group-focus-within/item:rotate-90\" />\n </InputGroupAddon>\n </InputGroup>\n }\n\n <ul class=\"scrollbar-thin max-h-[calc(var(--height,100%)-100px)] snap-y snap-start overflow-auto\" role=\"list\"\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\n @for (item of items(); track $index) {\n <AggregationItem [node]=\"item\" [path]=\"[]\" [field]=\"aggregation()?.column\" (onSelect)=\"select()\"\n (onOpen)=\"open($event)\" (onFilter)=\"apply()\" />\n }\n </ul>\n @if (aggregation()?.$hasMore && this.searchedItems().length === 0) {\n <button variant=\"link\" class=\"mt-1 flex w-full justify-center\" [attr.aria-label]=\"'loadMore' | transloco\"\n (click)=\"loadMore()\">\n {{ 'loadMore' | transloco }}\n </button>\n }\n</details>\n", styles: ["AggregationItem:has(+AggregationItem){margin-bottom:var(--agg-item-gap, 0)}\n"] }]
|
|
12541
|
-
}], ctorParameters: () => [], propDecorators: { searchInput: [{ type: i0.ViewChild, args: ["searchInput", { isSignal: true }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], 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 }] }], searchText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchText", required: false }] }, { type: i0.Output, args: ["searchTextChange"] }] } });
|
|
12542
|
-
|
|
12543
|
-
/**
|
|
12544
|
-
* Injection token that indicates whether custom date ranges are allowed.
|
|
12545
|
-
*
|
|
12546
|
-
* @remarks
|
|
12547
|
-
* This token is used to configure the date component to allow users to select custom date ranges.
|
|
12548
|
-
*
|
|
12549
|
-
* @example
|
|
12550
|
-
* ```typescript
|
|
12551
|
-
* providers: [
|
|
12552
|
-
* { provide: ALLOW_CUSTOM_RANGE, useValue: false }
|
|
12553
|
-
* ]
|
|
12554
|
-
* ```
|
|
12555
|
-
*
|
|
12556
|
-
* @public
|
|
12557
|
-
*/
|
|
12558
|
-
const FILTER_DATE_ALLOW_CUSTOM_RANGE = new InjectionToken("date allow custom range", {
|
|
12559
|
-
factory: () => true
|
|
12560
|
-
});
|
|
12561
|
-
class DateComponent extends AggregationComponent {
|
|
12562
|
-
destroyRef;
|
|
12563
|
-
datepicker = viewChild(DateRangePickerDirective, ...(ngDevMode ? [{ debugName: "datepicker" }] : []));
|
|
12564
|
-
title = input({ label: "Date", icon: "far fa-calendar-day" }, ...(ngDevMode ? [{ debugName: "title" }] : []));
|
|
12565
|
-
displayEmptyDistributionIntervals = input(false, ...(ngDevMode ? [{ debugName: "displayEmptyDistributionIntervals" }] : []));
|
|
12566
|
-
allowCustomRange = inject(FILTER_DATE_ALLOW_CUSTOM_RANGE);
|
|
12567
|
-
transloco = inject(TranslocoService);
|
|
12568
|
-
dateOptions = computed(() => translateAggregationToDateOptions(this.aggregation(), this.displayEmptyDistributionIntervals()), ...(ngDevMode ? [{ debugName: "dateOptions" }] : []));
|
|
12569
|
-
form = new FormGroup({
|
|
12570
|
-
option: new FormControl(null),
|
|
12571
|
-
customRange: new FormGroup({
|
|
12572
|
-
from: new FormControl(null),
|
|
12573
|
-
to: new FormControl(null)
|
|
12574
|
-
})
|
|
12575
|
-
});
|
|
12576
|
-
today = new Date();
|
|
12577
|
-
lang = signal(this.transloco.getActiveLang(), ...(ngDevMode ? [{ debugName: "lang" }] : []));
|
|
12578
|
-
validSelection = signal(false, ...(ngDevMode ? [{ debugName: "validSelection" }] : []));
|
|
12579
|
-
constructor(destroyRef) {
|
|
12580
|
-
super();
|
|
12581
|
-
this.destroyRef = destroyRef;
|
|
12582
|
-
this.transloco.langChanges$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((lang) => {
|
|
12583
|
-
this.lang.set(lang);
|
|
12584
|
-
this.datepicker()?.setOptions({ language: lang });
|
|
12585
|
-
});
|
|
12586
|
-
const abortController = new AbortController();
|
|
12587
|
-
addEventListener("changeDate", this.onDateChange.bind(this), {
|
|
12588
|
-
signal: abortController.signal
|
|
12589
|
-
});
|
|
12590
|
-
// apply current date filter from queryParamsStore
|
|
12591
|
-
effect(() => {
|
|
12592
|
-
this.updateForm(this.queryParamsStore.getFilter({
|
|
12593
|
-
field: this.aggregation()?.column,
|
|
12594
|
-
name: this.aggregation()?.name
|
|
12595
|
-
}));
|
|
12596
|
-
});
|
|
12597
|
-
this.form.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((changes) => {
|
|
12598
|
-
this.validSelection.set(!!changes.option &&
|
|
12599
|
-
(changes.option !== "custom-range" || changes.customRange?.from !== null || changes.customRange?.to !== null));
|
|
12600
|
-
});
|
|
12601
|
-
destroyRef.onDestroy(() => {
|
|
12602
|
-
abortController.abort();
|
|
12603
|
-
});
|
|
12604
|
-
}
|
|
12605
|
-
aggregation = computed(() => {
|
|
12606
|
-
const name = this.name();
|
|
12607
|
-
if (name !== null) {
|
|
12608
|
-
const agg = this.aggregationsService.processAggregation(name, this.column());
|
|
12609
|
-
return {
|
|
12610
|
-
...agg,
|
|
12611
|
-
items: agg?.items?.filter((item) => item.display !== "custom-range") ?? []
|
|
12612
|
-
};
|
|
12613
|
-
}
|
|
12614
|
-
return null;
|
|
12615
|
-
}, ...(ngDevMode ? [{ debugName: "aggregation" }] : []));
|
|
12616
|
-
apply() {
|
|
12617
|
-
try {
|
|
12618
|
-
const filter = this.getFormValueFilter();
|
|
12619
|
-
this.queryParamsStore.updateFilter(filter);
|
|
12620
|
-
this.selection.set(false);
|
|
12621
|
-
}
|
|
12622
|
-
catch (err) {
|
|
12623
|
-
warn("Error applying date filter:", err);
|
|
12624
|
-
if (err instanceof Error) {
|
|
12625
|
-
notify.error(this.transloco.translate(err.message));
|
|
12626
|
-
}
|
|
12627
|
-
}
|
|
12628
|
-
}
|
|
12629
|
-
clear(notify = true) {
|
|
12630
|
-
this.form.setValue({
|
|
12631
|
-
option: null,
|
|
12632
|
-
customRange: {
|
|
12633
|
-
from: null,
|
|
12634
|
-
to: null
|
|
12635
|
-
}
|
|
12636
|
-
});
|
|
12637
|
-
const agg = this.aggregation();
|
|
12638
|
-
if (agg && notify) {
|
|
12639
|
-
this.queryParamsStore.removeFilterByName(agg.name);
|
|
12640
|
-
}
|
|
12641
|
-
}
|
|
12642
|
-
updateForm(filter) {
|
|
12643
|
-
if (!filter) {
|
|
12644
|
-
this.clear(false);
|
|
12645
|
-
return;
|
|
12646
|
-
}
|
|
12647
|
-
const { operator, value } = filter;
|
|
12648
|
-
const code = this.dateOptions().find((option) => option.operator === operator && option.value === value)
|
|
12649
|
-
?.display ?? "custom-range";
|
|
12650
|
-
let from = null, to = null;
|
|
12651
|
-
if (code === "custom-range") {
|
|
12652
|
-
switch (operator) {
|
|
12653
|
-
case "lte":
|
|
12654
|
-
to = filter.value;
|
|
12655
|
-
break;
|
|
12656
|
-
case "gte":
|
|
12657
|
-
from = filter.value;
|
|
12658
|
-
break;
|
|
12659
|
-
case "between":
|
|
12660
|
-
from = filter.start;
|
|
12661
|
-
to = filter.end;
|
|
12662
|
-
break;
|
|
12663
|
-
}
|
|
12664
|
-
}
|
|
12665
|
-
const formValue = {
|
|
12666
|
-
option: code,
|
|
12667
|
-
customRange: {
|
|
12668
|
-
from: code === "custom-range" ? (from ?? null) : null,
|
|
12669
|
-
to: code === "custom-range" ? (to ?? null) : null
|
|
12670
|
-
}
|
|
12671
|
-
};
|
|
12672
|
-
this.form.setValue(formValue);
|
|
12673
|
-
// Update the date range picker
|
|
12674
|
-
const datepicker = this.datepicker();
|
|
12675
|
-
if (datepicker) {
|
|
12676
|
-
const start = new Date(formValue.customRange.from || "");
|
|
12677
|
-
const end = new Date(formValue.customRange.to || "");
|
|
12678
|
-
setTimeout(() => {
|
|
12679
|
-
datepicker.rangeDatepicker?.setDates(start, end);
|
|
12680
|
-
}, 1000);
|
|
12681
|
-
}
|
|
12682
|
-
}
|
|
12683
|
-
getFormValueFilter() {
|
|
12684
|
-
const value = this.form.value;
|
|
12685
|
-
// value.option is null
|
|
12686
|
-
if (!value.option) {
|
|
12687
|
-
throw new Error("filters.selectionInvalid");
|
|
12688
|
-
}
|
|
12689
|
-
if (value.option !== "custom-range") {
|
|
12690
|
-
const dateOption = this.dateOptions().find((option) => option.display === value.option);
|
|
12691
|
-
const aggregation = this.aggregation();
|
|
12692
|
-
if (!aggregation) {
|
|
12693
|
-
throw new Error("filters.aggregationNotFound");
|
|
13249
|
+
if (item.items?.length && savedItem.items?.length) {
|
|
13250
|
+
item.items = this.setSelected(item.items, savedItem.items);
|
|
13251
|
+
}
|
|
12694
13252
|
}
|
|
12695
|
-
return
|
|
12696
|
-
|
|
12697
|
-
|
|
12698
|
-
|
|
12699
|
-
|
|
12700
|
-
|
|
12701
|
-
|
|
12702
|
-
|
|
12703
|
-
|
|
12704
|
-
else if (value.customRange) {
|
|
12705
|
-
// if to is null, operator is gte
|
|
12706
|
-
// if from is null, operator is lte
|
|
12707
|
-
// if both are not null, operator is between
|
|
12708
|
-
const aggregation = this.aggregation();
|
|
12709
|
-
if (!aggregation) {
|
|
12710
|
-
throw new Error("filters.aggregationNotFound");
|
|
13253
|
+
return item;
|
|
13254
|
+
});
|
|
13255
|
+
}
|
|
13256
|
+
processAggregations(items) {
|
|
13257
|
+
if (!this.linkChildren())
|
|
13258
|
+
return items;
|
|
13259
|
+
items.forEach((item) => {
|
|
13260
|
+
if (item.items?.length) {
|
|
13261
|
+
this.selectVisually(item.items, item.$selected || false);
|
|
12711
13262
|
}
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
13263
|
+
});
|
|
13264
|
+
return items;
|
|
13265
|
+
}
|
|
13266
|
+
selectVisually(items, parentSelected) {
|
|
13267
|
+
items.forEach((item) => {
|
|
13268
|
+
item.$selectedVisually = parentSelected;
|
|
13269
|
+
if (item.items?.length)
|
|
13270
|
+
this.selectVisually(item.items, item.$selected || item.$selectedVisually);
|
|
13271
|
+
});
|
|
13272
|
+
}
|
|
13273
|
+
onToggle(event) {
|
|
13274
|
+
const e = event;
|
|
13275
|
+
this.isCollapsed.set(e.newState === "closed");
|
|
13276
|
+
}
|
|
13277
|
+
expandItems(items, expandedLevel) {
|
|
13278
|
+
this.traverse(items, (lineage, node, level) => {
|
|
13279
|
+
if (!node.$opened && node.items?.length >= 0 && level < expandedLevel) {
|
|
13280
|
+
node.$opened = true;
|
|
12721
13281
|
}
|
|
12722
|
-
|
|
12723
|
-
|
|
12724
|
-
filter.value = value.customRange.from;
|
|
13282
|
+
if (node.$opened && level >= expandedLevel) {
|
|
13283
|
+
node.$opened = false;
|
|
12725
13284
|
}
|
|
12726
|
-
|
|
12727
|
-
|
|
12728
|
-
|
|
13285
|
+
return false;
|
|
13286
|
+
});
|
|
13287
|
+
}
|
|
13288
|
+
/**
|
|
13289
|
+
* Traverses a tree structure, executing a callback function at every node
|
|
13290
|
+
* @param nodes the nodes to traverse
|
|
13291
|
+
* @param callback the callback function
|
|
13292
|
+
*/
|
|
13293
|
+
traverse(nodes, callback) {
|
|
13294
|
+
if (!nodes || nodes.length === 0) {
|
|
13295
|
+
return false;
|
|
13296
|
+
}
|
|
13297
|
+
if (!callback) {
|
|
13298
|
+
return false;
|
|
13299
|
+
}
|
|
13300
|
+
const lineage = [];
|
|
13301
|
+
const stack = [];
|
|
13302
|
+
let _i = nodes.length;
|
|
13303
|
+
while (_i--) {
|
|
13304
|
+
stack.push(nodes[_i]);
|
|
13305
|
+
}
|
|
13306
|
+
while (stack.length) {
|
|
13307
|
+
const node = stack.pop();
|
|
13308
|
+
if (!node) {
|
|
13309
|
+
lineage.pop();
|
|
12729
13310
|
}
|
|
12730
13311
|
else {
|
|
12731
|
-
|
|
13312
|
+
lineage.push(node);
|
|
13313
|
+
if (callback(lineage, node, lineage.length - 1)) {
|
|
13314
|
+
return true;
|
|
13315
|
+
}
|
|
13316
|
+
stack.push(undefined);
|
|
13317
|
+
if (node.items && node.items.length > 0) {
|
|
13318
|
+
_i = node.items.length;
|
|
13319
|
+
while (_i--) {
|
|
13320
|
+
stack.push(node.items[_i]);
|
|
13321
|
+
}
|
|
13322
|
+
}
|
|
12732
13323
|
}
|
|
12733
|
-
return filter;
|
|
12734
13324
|
}
|
|
12735
|
-
|
|
13325
|
+
return false;
|
|
12736
13326
|
}
|
|
12737
|
-
|
|
12738
|
-
// Get the selected dates
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
|
|
12743
|
-
|
|
12744
|
-
|
|
12745
|
-
|
|
13327
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13328
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AggregationTreeComponent, isStandalone: true, selector: "AggregationTree, aggregation-tree, aggregationtree", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, expandedLevel: { classPropertyName: "expandedLevel", publicName: "expandedLevel", 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 }, searchText: { classPropertyName: "searchText", publicName: "searchText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", searchText: "searchTextChange" }, host: { properties: { "class": "cn(\"block h-[inherit] max-h-[inherit] w-[inherit]\",class())" } }, viewQueries: [{ propertyName: "virtualItems", predicate: ["virtualItem"], descendants: true, isSignal: true }, { propertyName: "scrollElement", first: true, predicate: ["scrollElement"], descendants: true, isSignal: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (!aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <i class=\"fa-fw fas fa-triangle-exclamation mr-1\"></i>\r\n The aggregationTree component does not support list aggregations. Please use\r\n the <Aggregation /> component instead.\r\n </div>\r\n}\r\n<details\r\n [attr.open]=\"expanded()\"\r\n [attr.name]=\"id()\"\r\n class=\"group space-y-2\"\r\n (toggle)=\"onToggle($event)\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible() && !isEmpty()\"\r\n [class.text-muted-foreground]=\"isEmpty()\"\r\n class=\"m-0 mt-1 flex h-8 w-full items-center gap-1 pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @if (aggregation()?.icon) {\r\n <i class=\"fa-fw {{ aggregation()?.icon }} mr-1\" aria-hidden=\"true\"></i>\r\n }\r\n <span class=\"grow truncate\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (showFiltersCount() && filtersCount() > 0) {\r\n <!-- count -->\r\n <Badge size=\"xs\" class=\"ml-1 pb-0.5\">\r\n {{ filtersCount() }}\r\n </Badge>\r\n }\r\n <!-- apply filter block -->\r\n @if (!isCollapsed()) {\r\n <ButtonGroup>\r\n @if (hasFilters()) {\r\n <button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n class=\"size-6\"\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); clear()\">\r\n <i class=\"fa-fw far fa-filter-circle-xmark\"></i>\r\n <span class=\"sr-only\">{{\r\n \"filters.clearFilters\" | transloco\r\n }}</span>\r\n </button>\r\n }\r\n @if (selection()) {\r\n <button\r\n variant=\"primary\"\r\n size=\"xs\"\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); apply()\"\r\n class=\"h-4 px-1\">\r\n <FilterIcon class=\"size-4\" />\r\n <span class=\"sr-only\">{{ \"filters.apply\" | transloco }}</span>\r\n </button>\r\n }\r\n\r\n <!-- select / unselect all -->\r\n @if (isAllSelected()) {\r\n <button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n class=\"size-6\"\r\n [attr.title]=\"'filters.unselectAllFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.unselectAllFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); unselectAll()\">\r\n <i class=\"fa-fw far fa-check-square\"></i>\r\n <span class=\"sr-only\">{{\r\n \"filters.unselectAllFilters\" | transloco\r\n }}</span>\r\n </button>\r\n } @else {\r\n <button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n class=\"size-6\"\r\n [attr.title]=\"'filters.selectAllFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.selectAllFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); selectAll()\">\r\n <i class=\"fa-fw far fa-square\"></i>\r\n <span class=\"sr-only\">{{\r\n \"filters.selectAllFilters\" | transloco\r\n }}</span>\r\n </button>\r\n }\r\n </ButtonGroup>\r\n }\r\n\r\n @if (collapsible()) {\r\n <button\r\n variant=\"none\"\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n <span class=\"sr-only\">{{ \"filters.toggle\" | transloco }}</span>\r\n </button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n @if (aggregation()?.searchable && items().length) {\r\n <InputGroup class=\"group/item mt-1\">\r\n <input\r\n #searchInput\r\n input-group\r\n id=\"aggregation-input-{{ column() }}\"\r\n type=\"text\"\r\n [attr.placeholder]=\"'search' | transloco\"\r\n [(ngModel)]=\"searchText\"\r\n class=\"mt-1\" />\r\n <InputGroupAddon>\r\n <SearchIcon\r\n class=\"text-foreground size-4 rotate-0 transition-[rotate] duration-500 group-focus-within/item:rotate-90\" />\r\n </InputGroupAddon>\r\n </InputGroup>\r\n }\r\n\r\n <div\r\n #scrollElement\r\n class=\"scrollbar-thin max-h-[calc(var(--height,100%)-100px)] w-full overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"list\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n <div\r\n class=\"absolute top-0 left-0 w-full\"\r\n [style.transform]=\"\r\n 'translateY(' +\r\n (virtualizer.getVirtualItems()[0]\r\n ? virtualizer.getVirtualItems()[0].start\r\n : 0) +\r\n 'px)'\r\n \"\r\n role=\"listitem\">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div #virtualItem [attr.data-index]=\"vItem.index\">\r\n <AggregationTreeItem\r\n [node]=\"item\"\r\n [path]=\"[]\"\r\n [field]=\"aggregation()?.column\"\r\n (onSelect)=\"select()\"\r\n (onOpen)=\"open($event)\"\r\n (onFilter)=\"apply()\" />\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n @if (aggregation()?.$hasMore && this.searchedItems().length === 0) {\r\n <button\r\n variant=\"link\"\r\n class=\"mt-1 flex w-full justify-center\"\r\n [attr.aria-label]=\"'loadMore' | transloco\"\r\n (click)=\"loadMore()\">\r\n {{ \"loadMore\" | transloco }}\r\n </button>\r\n }\r\n</details>\r\n", styles: ["AggregationTreeItem:has(+AggregationTreeItem){margin-bottom:var(--agg-item-gap, 0)}\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: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "component", type: AggregationTreeItemComponent, selector: "aggregation-tree-item, AggregationTreeItem, aggregationtreeitem", inputs: ["node", "path", "field"], outputs: ["onSelect", "onOpen", "onFilter"] }, { kind: "directive", type: BadgeComponent, selector: "badge, Badge", inputs: ["class", "variant", "size"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon", inputs: ["class"] }, { kind: "directive", type: ButtonGroup, selector: "button-group, ButtonGroup", inputs: ["class", "orientation"] }, { kind: "directive", type: InputGroupInput, selector: "input[input-group]", inputs: ["class", "type", "placeholder", "disabled"] }, { kind: "directive", type: InputGroupComponent, selector: "input-group, inputgroup, InputGroup", inputs: ["class"] }, { kind: "directive", type: InputGroupAddonComponent, selector: "input-group-addon, inputgroupaddon, InputGroupAddon", inputs: ["class", "align"] }, { kind: "component", type: SearchIcon, selector: "SearchIcon", inputs: ["class"] }, { kind: "component", type: FilterIcon, selector: "filter-icon, FilterIcon", inputs: ["class"] }, { kind: "pipe", type: SyslangPipe, name: "syslang" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
13329
|
+
}
|
|
13330
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationTreeComponent, decorators: [{
|
|
13331
|
+
type: Component,
|
|
13332
|
+
args: [{ selector: "AggregationTree, aggregation-tree, aggregationtree", imports: [
|
|
13333
|
+
FormsModule,
|
|
13334
|
+
ReactiveFormsModule,
|
|
13335
|
+
ButtonComponent,
|
|
13336
|
+
AggregationTreeItemComponent,
|
|
13337
|
+
SyslangPipe,
|
|
13338
|
+
TranslocoPipe,
|
|
13339
|
+
BadgeComponent,
|
|
13340
|
+
ChevronRightIcon,
|
|
13341
|
+
ButtonGroup,
|
|
13342
|
+
InputGroupInput,
|
|
13343
|
+
InputGroupComponent,
|
|
13344
|
+
InputGroupAddonComponent,
|
|
13345
|
+
SearchIcon,
|
|
13346
|
+
FilterIcon
|
|
13347
|
+
], standalone: true, host: {
|
|
13348
|
+
"[class]": 'cn("block h-[inherit] max-h-[inherit] w-[inherit]",class())'
|
|
13349
|
+
}, template: "@if (!aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <i class=\"fa-fw fas fa-triangle-exclamation mr-1\"></i>\r\n The aggregationTree component does not support list aggregations. Please use\r\n the <Aggregation /> component instead.\r\n </div>\r\n}\r\n<details\r\n [attr.open]=\"expanded()\"\r\n [attr.name]=\"id()\"\r\n class=\"group space-y-2\"\r\n (toggle)=\"onToggle($event)\">\r\n <summary\r\n [class.cursor-pointer]=\"collapsible() && !isEmpty()\"\r\n [class.text-muted-foreground]=\"isEmpty()\"\r\n class=\"m-0 mt-1 flex h-8 w-full items-center gap-1 pl-1 font-semibold select-none\"\r\n (click)=\"onHeaderClick($event)\">\r\n <ng-content select=\"label\">\r\n @if (aggregation()?.icon) {\r\n <i class=\"fa-fw {{ aggregation()?.icon }} mr-1\" aria-hidden=\"true\"></i>\r\n }\r\n <span class=\"grow truncate\">{{\r\n aggregation()?.display | syslang | transloco\r\n }}</span>\r\n </ng-content>\r\n\r\n @if (showFiltersCount() && filtersCount() > 0) {\r\n <!-- count -->\r\n <Badge size=\"xs\" class=\"ml-1 pb-0.5\">\r\n {{ filtersCount() }}\r\n </Badge>\r\n }\r\n <!-- apply filter block -->\r\n @if (!isCollapsed()) {\r\n <ButtonGroup>\r\n @if (hasFilters()) {\r\n <button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n class=\"size-6\"\r\n [attr.title]=\"'filters.clearFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); clear()\">\r\n <i class=\"fa-fw far fa-filter-circle-xmark\"></i>\r\n <span class=\"sr-only\">{{\r\n \"filters.clearFilters\" | transloco\r\n }}</span>\r\n </button>\r\n }\r\n @if (selection()) {\r\n <button\r\n variant=\"primary\"\r\n size=\"xs\"\r\n [attr.title]=\"'filters.applyFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.applyFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); apply()\"\r\n class=\"h-4 px-1\">\r\n <FilterIcon class=\"size-4\" />\r\n <span class=\"sr-only\">{{ \"filters.apply\" | transloco }}</span>\r\n </button>\r\n }\r\n\r\n <!-- select / unselect all -->\r\n @if (isAllSelected()) {\r\n <button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n class=\"size-6\"\r\n [attr.title]=\"'filters.unselectAllFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.unselectAllFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); unselectAll()\">\r\n <i class=\"fa-fw far fa-check-square\"></i>\r\n <span class=\"sr-only\">{{\r\n \"filters.unselectAllFilters\" | transloco\r\n }}</span>\r\n </button>\r\n } @else {\r\n <button\r\n variant=\"outline\"\r\n size=\"icon\"\r\n class=\"size-6\"\r\n [attr.title]=\"'filters.selectAllFilters' | transloco\"\r\n [attr.aria-label]=\"'filters.selectAllFilters' | transloco\"\r\n (click)=\"$event.stopPropagation(); selectAll()\">\r\n <i class=\"fa-fw far fa-square\"></i>\r\n <span class=\"sr-only\">{{\r\n \"filters.selectAllFilters\" | transloco\r\n }}</span>\r\n </button>\r\n }\r\n </ButtonGroup>\r\n }\r\n\r\n @if (collapsible()) {\r\n <button\r\n variant=\"none\"\r\n title=\"Open/Close\"\r\n class=\"cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\r\n <chevronright />\r\n <span class=\"sr-only\">{{ \"filters.toggle\" | transloco }}</span>\r\n </button>\r\n }\r\n </summary>\r\n\r\n <!-- content wrapper -->\r\n @if (aggregation()?.searchable && items().length) {\r\n <InputGroup class=\"group/item mt-1\">\r\n <input\r\n #searchInput\r\n input-group\r\n id=\"aggregation-input-{{ column() }}\"\r\n type=\"text\"\r\n [attr.placeholder]=\"'search' | transloco\"\r\n [(ngModel)]=\"searchText\"\r\n class=\"mt-1\" />\r\n <InputGroupAddon>\r\n <SearchIcon\r\n class=\"text-foreground size-4 rotate-0 transition-[rotate] duration-500 group-focus-within/item:rotate-90\" />\r\n </InputGroupAddon>\r\n </InputGroup>\r\n }\r\n\r\n <div\r\n #scrollElement\r\n class=\"scrollbar-thin max-h-[calc(var(--height,100%)-100px)] w-full overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"list\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n <div\r\n class=\"absolute top-0 left-0 w-full\"\r\n [style.transform]=\"\r\n 'translateY(' +\r\n (virtualizer.getVirtualItems()[0]\r\n ? virtualizer.getVirtualItems()[0].start\r\n : 0) +\r\n 'px)'\r\n \"\r\n role=\"listitem\">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div #virtualItem [attr.data-index]=\"vItem.index\">\r\n <AggregationTreeItem\r\n [node]=\"item\"\r\n [path]=\"[]\"\r\n [field]=\"aggregation()?.column\"\r\n (onSelect)=\"select()\"\r\n (onOpen)=\"open($event)\"\r\n (onFilter)=\"apply()\" />\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n @if (aggregation()?.$hasMore && this.searchedItems().length === 0) {\r\n <button\r\n variant=\"link\"\r\n class=\"mt-1 flex w-full justify-center\"\r\n [attr.aria-label]=\"'loadMore' | transloco\"\r\n (click)=\"loadMore()\">\r\n {{ \"loadMore\" | transloco }}\r\n </button>\r\n }\r\n</details>\r\n", styles: ["AggregationTreeItem:has(+AggregationTreeItem){margin-bottom:var(--agg-item-gap, 0)}\n"] }]
|
|
13350
|
+
}], ctorParameters: () => [], propDecorators: { virtualItems: [{ type: i0.ViewChildren, args: ['virtualItem', { isSignal: true }] }], scrollElement: [{ type: i0.ViewChild, args: ["scrollElement", { isSignal: true }] }], searchInput: [{ type: i0.ViewChild, args: ["searchInput", { isSignal: true }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], expandedLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedLevel", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], 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 }] }], searchText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchText", required: false }] }, { type: i0.Output, args: ["searchTextChange"] }] } });
|
|
13351
|
+
|
|
13352
|
+
/**
|
|
13353
|
+
* The `AggregationComponent` is responsible for rendering the appropriate aggregation component based on the type of aggregation (date, tree, or list). It uses the `AggregationsService` to retrieve the aggregation data and determines which component to display based on the column type and whether it is a tree structure. The component also provides inputs for configuring its behavior, such as collapsibility, searchability, and showing filter counts.
|
|
13354
|
+
*
|
|
13355
|
+
*
|
|
13356
|
+
* @example
|
|
13357
|
+
* <Aggregation
|
|
13358
|
+
* name="myAggregation"
|
|
13359
|
+
* column="myColumn"
|
|
13360
|
+
* [collapsible]="true"
|
|
13361
|
+
* [collapsed]="false"
|
|
13362
|
+
* [showFiltersCount]="true"
|
|
13363
|
+
* [searchable]="true"
|
|
13364
|
+
* (onSelect)="handleSelect($event)"
|
|
13365
|
+
* />
|
|
13366
|
+
*/
|
|
13367
|
+
class AggregationComponent {
|
|
13368
|
+
cn = cn;
|
|
13369
|
+
appStore = inject(AppStore);
|
|
13370
|
+
aggregationsStore = inject(AggregationsStore);
|
|
13371
|
+
aggregationsService = inject(AggregationsService);
|
|
13372
|
+
aggregationDate = viewChild(AggregationDateComponent, ...(ngDevMode ? [{ debugName: "aggregationDate" }] : []));
|
|
13373
|
+
aggregationTree = viewChild(AggregationTreeComponent, ...(ngDevMode ? [{ debugName: "aggregationTree" }] : []));
|
|
13374
|
+
aggregationList = viewChild(AggregationListComponent, ...(ngDevMode ? [{ debugName: "aggregationList" }] : []));
|
|
13375
|
+
class = input("", ...(ngDevMode ? [{ debugName: "class" }] : []));
|
|
13376
|
+
/**
|
|
13377
|
+
* The name of the <details> element. When you provide the same id, the component work as an accordion
|
|
13378
|
+
* @defaultValue null
|
|
13379
|
+
*/
|
|
13380
|
+
id = input(null, ...(ngDevMode ? [{ debugName: "id" }] : []));
|
|
13381
|
+
/**
|
|
13382
|
+
* The name of the aggregation. This is a required input and must be provided for the component to function correctly.
|
|
13383
|
+
* @defaultValue undefined
|
|
13384
|
+
*/
|
|
13385
|
+
name = input.required(...(ngDevMode ? [{ debugName: "name" }] : []));
|
|
13386
|
+
/**
|
|
13387
|
+
* The column associated with the aggregation. This is a required input and must be provided for the component to function correctly.
|
|
13388
|
+
* @defaultValue undefined
|
|
13389
|
+
*/
|
|
13390
|
+
column = input.required(...(ngDevMode ? [{ debugName: "column" }] : []));
|
|
13391
|
+
/**
|
|
13392
|
+
* A boolean flag indicating whether we want to see the filters count when some is applied
|
|
13393
|
+
* This property is initialized to `false` by default.
|
|
13394
|
+
*/
|
|
13395
|
+
showFiltersCount = input(null, ...(ngDevMode ? [{ debugName: "showFiltersCount", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
13396
|
+
/**
|
|
13397
|
+
* Determines whether the aggregation component can be collapsed or expanded.
|
|
13398
|
+
* When true, the component will display collapse/expand controls allowing users
|
|
13399
|
+
* to show or hide the aggregation content.
|
|
13400
|
+
*
|
|
13401
|
+
* @default false
|
|
13402
|
+
*/
|
|
13403
|
+
collapsible = input(false, ...(ngDevMode ? [{ debugName: "collapsible" }] : []));
|
|
13404
|
+
/**
|
|
13405
|
+
* Controls whether the aggregation component is in a collapsed state.
|
|
13406
|
+
* When true, the component will be visually collapsed/hidden.
|
|
13407
|
+
* When false, the component will be expanded/visible.
|
|
13408
|
+
*
|
|
13409
|
+
* @default false
|
|
13410
|
+
*/
|
|
13411
|
+
collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : []));
|
|
13412
|
+
/**
|
|
13413
|
+
* A boolean flag indicating whether the component is searchable.
|
|
13414
|
+
* This property is initialized to `undefined` by default.
|
|
13415
|
+
* "Undefined" and not "false" because this input overrides the custom json settings
|
|
13416
|
+
*/
|
|
13417
|
+
searchable = input(undefined, ...(ngDevMode ? [{ debugName: "searchable" }] : []));
|
|
13418
|
+
expandedLevel = input(undefined, ...(ngDevMode ? [{ debugName: "expandedLevel", transform: numberAttribute }] : [{ transform: numberAttribute }]));
|
|
13419
|
+
/**
|
|
13420
|
+
* An output event that emits an array of selected `AggregationItem` objects when the user selects items in the aggregation component. This allows parent components to listen for selection changes and react accordingly, such as applying filters or updating the UI based on the selected items.
|
|
13421
|
+
*/
|
|
13422
|
+
onSelect = output();
|
|
13423
|
+
/**
|
|
13424
|
+
* A computed property that retrieves the items to be displayed in the aggregation component. It checks for the presence of date, tree, or list aggregation components and returns their respective items. If none of these components are present, it returns an empty array. This allows the component to dynamically display the appropriate items based on the type of aggregation being rendered.
|
|
13425
|
+
*
|
|
13426
|
+
* @returns An array of items to be displayed in the aggregation component.
|
|
13427
|
+
*/
|
|
13428
|
+
items = computed(() => this.aggregationDate()?.items() ??
|
|
13429
|
+
this.aggregationTree()?.items() ??
|
|
13430
|
+
this.aggregationList()?.items() ??
|
|
13431
|
+
[], ...(ngDevMode ? [{ debugName: "items" }] : []));
|
|
13432
|
+
/**
|
|
13433
|
+
* A computed property that filters the items to include only those that are currently selected. It uses the `items` computed property to retrieve all items and then applies a filter to return only those with the `$selected` property set to true. This allows the component to easily access and manage the selected items for further processing, such as applying filters or updating the UI based on user selections.
|
|
13434
|
+
*
|
|
13435
|
+
* @returns An array of selected items from the aggregation component.
|
|
13436
|
+
*/
|
|
13437
|
+
selectedItems = computed(() => this.items().filter(item => item.$selected), ...(ngDevMode ? [{ debugName: "selectedItems" }] : []));
|
|
13438
|
+
/* aggregation */
|
|
13439
|
+
aggregation = computed(() => {
|
|
13440
|
+
// when the aggegation store updates, we need to check if the aggregation is still valid
|
|
13441
|
+
getState(this.aggregationsStore);
|
|
13442
|
+
const agg = this.aggregationsService.getAggregation(this.name(), this.column());
|
|
13443
|
+
if (!agg) {
|
|
13444
|
+
warn(`Aggregation with name ${this.name()} and column ${this.column()} not found`);
|
|
13445
|
+
return undefined;
|
|
12746
13446
|
}
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
|
|
12754
|
-
|
|
13447
|
+
return agg;
|
|
13448
|
+
}, ...(ngDevMode ? [{ debugName: "aggregation" }] : []));
|
|
13449
|
+
/**
|
|
13450
|
+
* Checks if the specified column is a date filter.
|
|
13451
|
+
*
|
|
13452
|
+
* @param column The column name to check.
|
|
13453
|
+
* @returns True if the column is a date filter, false otherwise.
|
|
13454
|
+
*/
|
|
13455
|
+
isDate(column) {
|
|
13456
|
+
if (!column) {
|
|
13457
|
+
return false;
|
|
12755
13458
|
}
|
|
13459
|
+
return this.appStore.isDateColumn(column);
|
|
12756
13460
|
}
|
|
12757
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type:
|
|
12758
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type:
|
|
13461
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13462
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AggregationComponent, isStandalone: true, selector: "Aggregation, aggregation", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, showFiltersCount: { classPropertyName: "showFiltersCount", publicName: "showFiltersCount", isSignal: true, isRequired: false, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, expandedLevel: { classPropertyName: "expandedLevel", publicName: "expandedLevel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect" }, host: { properties: { "class": "cn(\"block h-[inherit] max-h-[inherit]\",class())" } }, viewQueries: [{ propertyName: "aggregationDate", first: true, predicate: AggregationDateComponent, descendants: true, isSignal: true }, { propertyName: "aggregationTree", first: true, predicate: AggregationTreeComponent, descendants: true, isSignal: true }, { propertyName: "aggregationList", first: true, predicate: AggregationListComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
13463
|
+
@if (isDate(aggregation()?.column)) {
|
|
13464
|
+
<AggregationDate
|
|
13465
|
+
class="max-w-80"
|
|
13466
|
+
[id]="id()"
|
|
13467
|
+
[name]="name()"
|
|
13468
|
+
[column]="column()"
|
|
13469
|
+
[collapsed]="collapsed()"
|
|
13470
|
+
[collapsible]="collapsible()"
|
|
13471
|
+
[showFiltersCount]="showFiltersCount()"
|
|
13472
|
+
[searchable]="searchable()"
|
|
13473
|
+
[title]="{ label: 'Date', icon: 'far fa-calendar-day' }"
|
|
13474
|
+
/>
|
|
13475
|
+
} @else if (aggregation()?.isTree) {
|
|
13476
|
+
<AggregationTree
|
|
13477
|
+
class="max-w-80"
|
|
13478
|
+
[id]="id()"
|
|
13479
|
+
[name]="name()"
|
|
13480
|
+
[column]="column()"
|
|
13481
|
+
[collapsed]="collapsed()"
|
|
13482
|
+
[collapsible]="collapsible()"
|
|
13483
|
+
[showFiltersCount]="showFiltersCount()"
|
|
13484
|
+
[searchable]="searchable()"
|
|
13485
|
+
[expandedLevel]="expandedLevel()"
|
|
13486
|
+
(onSelect)="onSelect.emit($event)"
|
|
13487
|
+
/>
|
|
13488
|
+
}
|
|
13489
|
+
@else {
|
|
13490
|
+
<AggregationList
|
|
13491
|
+
class="w-60"
|
|
13492
|
+
[id]="id()"
|
|
13493
|
+
[name]="name()"
|
|
13494
|
+
[column]="column()"
|
|
13495
|
+
[collapsed]="collapsed()"
|
|
13496
|
+
[collapsible]="collapsible()"
|
|
13497
|
+
[showFiltersCount]="showFiltersCount()"
|
|
13498
|
+
[searchable]="searchable()"
|
|
13499
|
+
(onSelect)="onSelect.emit($event)"
|
|
13500
|
+
/>
|
|
13501
|
+
}
|
|
13502
|
+
`, isInline: true, dependencies: [{ kind: "component", type: AggregationListComponent, selector: "AggregationList, aggregation-list, aggregationlist", inputs: ["class", "id", "name", "column", "collapsible", "collapsed", "searchable", "showFiltersCount", "searchText"], outputs: ["onSelect", "searchTextChange"] }, { kind: "component", type: AggregationTreeComponent, selector: "AggregationTree, aggregation-tree, aggregationtree", inputs: ["class", "id", "name", "column", "expandedLevel", "collapsible", "collapsed", "searchable", "showFiltersCount", "searchText"], outputs: ["onSelect", "searchTextChange"] }, { kind: "component", type: AggregationDateComponent, selector: "aggregation-date, AggregationDate, aggregationdate", inputs: ["title", "displayEmptyDistributionIntervals"] }] });
|
|
12759
13503
|
}
|
|
12760
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type:
|
|
13504
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AggregationComponent, decorators: [{
|
|
12761
13505
|
type: Component,
|
|
12762
|
-
args: [{
|
|
12763
|
-
|
|
12764
|
-
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
12768
|
-
|
|
12769
|
-
|
|
12770
|
-
|
|
12771
|
-
|
|
12772
|
-
|
|
12773
|
-
|
|
12774
|
-
|
|
13506
|
+
args: [{
|
|
13507
|
+
selector: "Aggregation, aggregation",
|
|
13508
|
+
imports: [AggregationListComponent, AggregationTreeComponent, AggregationDateComponent],
|
|
13509
|
+
template: `
|
|
13510
|
+
@if (isDate(aggregation()?.column)) {
|
|
13511
|
+
<AggregationDate
|
|
13512
|
+
class="max-w-80"
|
|
13513
|
+
[id]="id()"
|
|
13514
|
+
[name]="name()"
|
|
13515
|
+
[column]="column()"
|
|
13516
|
+
[collapsed]="collapsed()"
|
|
13517
|
+
[collapsible]="collapsible()"
|
|
13518
|
+
[showFiltersCount]="showFiltersCount()"
|
|
13519
|
+
[searchable]="searchable()"
|
|
13520
|
+
[title]="{ label: 'Date', icon: 'far fa-calendar-day' }"
|
|
13521
|
+
/>
|
|
13522
|
+
} @else if (aggregation()?.isTree) {
|
|
13523
|
+
<AggregationTree
|
|
13524
|
+
class="max-w-80"
|
|
13525
|
+
[id]="id()"
|
|
13526
|
+
[name]="name()"
|
|
13527
|
+
[column]="column()"
|
|
13528
|
+
[collapsed]="collapsed()"
|
|
13529
|
+
[collapsible]="collapsible()"
|
|
13530
|
+
[showFiltersCount]="showFiltersCount()"
|
|
13531
|
+
[searchable]="searchable()"
|
|
13532
|
+
[expandedLevel]="expandedLevel()"
|
|
13533
|
+
(onSelect)="onSelect.emit($event)"
|
|
13534
|
+
/>
|
|
13535
|
+
}
|
|
13536
|
+
@else {
|
|
13537
|
+
<AggregationList
|
|
13538
|
+
class="w-60"
|
|
13539
|
+
[id]="id()"
|
|
13540
|
+
[name]="name()"
|
|
13541
|
+
[column]="column()"
|
|
13542
|
+
[collapsed]="collapsed()"
|
|
13543
|
+
[collapsible]="collapsible()"
|
|
13544
|
+
[showFiltersCount]="showFiltersCount()"
|
|
13545
|
+
[searchable]="searchable()"
|
|
13546
|
+
(onSelect)="onSelect.emit($event)"
|
|
13547
|
+
/>
|
|
13548
|
+
}
|
|
13549
|
+
`,
|
|
13550
|
+
host: {
|
|
13551
|
+
"[class]": 'cn("block h-[inherit] max-h-[inherit]",class())'
|
|
13552
|
+
}
|
|
13553
|
+
}]
|
|
13554
|
+
}], propDecorators: { aggregationDate: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AggregationDateComponent), { isSignal: true }] }], aggregationTree: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AggregationTreeComponent), { isSignal: true }] }], aggregationList: [{ type: i0.ViewChild, args: [i0.forwardRef(() => AggregationListComponent), { isSignal: true }] }], class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], showFiltersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFiltersCount", required: false }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], expandedLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedLevel", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }] } });
|
|
12775
13555
|
|
|
12776
13556
|
class AsideFiltersComponent {
|
|
12777
13557
|
cn = cn;
|
|
@@ -12782,14 +13562,23 @@ class AsideFiltersComponent {
|
|
|
12782
13562
|
const asideFilters = this.appStore.filters().filter((f) => f.position === "left");
|
|
12783
13563
|
return this.appStore.getAuthorized(asideFilters);
|
|
12784
13564
|
}, ...(ngDevMode ? [{ debugName: "asideFilters" }] : []));
|
|
13565
|
+
/**
|
|
13566
|
+
* Checks if the specified column is a date filter.
|
|
13567
|
+
*
|
|
13568
|
+
* @param column The column name to check.
|
|
13569
|
+
* @returns True if the column is a date filter, false otherwise.
|
|
13570
|
+
*/
|
|
13571
|
+
isDate(column) {
|
|
13572
|
+
return this.appStore.isDateColumn(column);
|
|
13573
|
+
}
|
|
12785
13574
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AsideFiltersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12786
13575
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: AsideFiltersComponent, isStandalone: true, selector: "aside-filters, AsideFilters, asidefilters", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "cn('flex flex-col gap-2 overflow-auto', class())" } }, ngImport: i0, template: `
|
|
12787
13576
|
<div class="flex flex-col gap-2">
|
|
12788
13577
|
@for (agg of asideFilters(); track agg.name) {
|
|
12789
|
-
<Aggregation [name]="agg.name" [column]="agg.column" showFiltersCount />
|
|
13578
|
+
<Aggregation id="aside-filters" [name]="agg.name" [column]="agg.column" showFiltersCount />
|
|
12790
13579
|
}
|
|
12791
13580
|
</div>
|
|
12792
|
-
`, isInline: true, dependencies: [{ kind: "component", type: AggregationComponent, selector: "Aggregation, aggregation", inputs: ["class", "id", "name", "column", "
|
|
13581
|
+
`, isInline: true, dependencies: [{ kind: "component", type: AggregationComponent, selector: "Aggregation, aggregation", inputs: ["class", "id", "name", "column", "showFiltersCount", "collapsible", "collapsed", "searchable", "expandedLevel"], outputs: ["onSelect"] }] });
|
|
12793
13582
|
}
|
|
12794
13583
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: AsideFiltersComponent, decorators: [{
|
|
12795
13584
|
type: Component,
|
|
@@ -12799,7 +13588,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
12799
13588
|
template: `
|
|
12800
13589
|
<div class="flex flex-col gap-2">
|
|
12801
13590
|
@for (agg of asideFilters(); track agg.name) {
|
|
12802
|
-
<Aggregation [name]="agg.name" [column]="agg.column" showFiltersCount />
|
|
13591
|
+
<Aggregation id="aside-filters" [name]="agg.name" [column]="agg.column" showFiltersCount />
|
|
12803
13592
|
}
|
|
12804
13593
|
</div>
|
|
12805
13594
|
`,
|
|
@@ -12814,6 +13603,7 @@ class FilterButtonComponent {
|
|
|
12814
13603
|
column = input.required(...(ngDevMode ? [{ debugName: "column" }] : []));
|
|
12815
13604
|
position = input("bottom-start", ...(ngDevMode ? [{ debugName: "position" }] : []));
|
|
12816
13605
|
offset = input(8, ...(ngDevMode ? [{ debugName: "offset" }] : []));
|
|
13606
|
+
expandedLevel = input(undefined, ...(ngDevMode ? [{ debugName: "expandedLevel", transform: numberAttribute }] : [{ transform: numberAttribute }]));
|
|
12817
13607
|
variant = signal("ghost", ...(ngDevMode ? [{ debugName: "variant" }] : []));
|
|
12818
13608
|
filter = signal({
|
|
12819
13609
|
name: "",
|
|
@@ -12869,7 +13659,7 @@ class FilterButtonComponent {
|
|
|
12869
13659
|
return this.appStore.isDateColumn(column);
|
|
12870
13660
|
}
|
|
12871
13661
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: FilterButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12872
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: FilterButtonComponent, isStandalone: true, selector: "filter-button, FilterButton", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "listitem" }, properties: { "class.hidden": "filter().hidden" } }, viewQueries: [{ propertyName: "popoverRef", first: true, predicate: PopoverComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
13662
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: FilterButtonComponent, isStandalone: true, selector: "filter-button, FilterButton", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, expandedLevel: { classPropertyName: "expandedLevel", publicName: "expandedLevel", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "listitem" }, properties: { "class.hidden": "filter().hidden" } }, viewQueries: [{ propertyName: "popoverRef", first: true, predicate: PopoverComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
12873
13663
|
<Popover [disabled]="filter().disabled" class="group">
|
|
12874
13664
|
<button
|
|
12875
13665
|
[variant]="variant()"
|
|
@@ -12906,25 +13696,17 @@ class FilterButtonComponent {
|
|
|
12906
13696
|
[offset]="offset()"
|
|
12907
13697
|
>
|
|
12908
13698
|
@if(contentRef.isVisible) {
|
|
12909
|
-
@if (isDate(filter().column)) {
|
|
12910
|
-
<DateFilter
|
|
12911
|
-
class="*:details-content:[--height:19lh] w-max max-w-80"
|
|
12912
|
-
[name]="filter().name"
|
|
12913
|
-
[column]="filter().column"
|
|
12914
|
-
id="filter-{{ filter().column }}"
|
|
12915
|
-
[title]="{ label: 'Date', icon: 'far fa-calendar-day' }"
|
|
12916
|
-
/>
|
|
12917
|
-
} @else {
|
|
12918
13699
|
<Aggregation
|
|
12919
|
-
class="
|
|
13700
|
+
class="w-60 [--height:19lh]"
|
|
13701
|
+
id="filter-{{ filter().column }}"
|
|
12920
13702
|
[name]="filter().name"
|
|
12921
13703
|
[column]="filter().column"
|
|
12922
|
-
|
|
12923
|
-
|
|
13704
|
+
[expandedLevel]="expandedLevel()"
|
|
13705
|
+
/>
|
|
12924
13706
|
}
|
|
12925
13707
|
</PopoverContent>
|
|
12926
13708
|
</Popover>
|
|
12927
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "component", type: PopoverComponent, selector: "popover, Popover", inputs: ["disabled", "closeOnScroll"], outputs: ["closed"] }, { kind: "directive", type: PopoverContentComponent, selector: "popover-content, PopoverContent, popovercontent", inputs: ["class", "position", "keepOpen", "offset", "strategy"], exportAs: ["popoverContent"] }, { kind: "component", type: AggregationComponent, selector: "Aggregation, aggregation", inputs: ["class", "id", "name", "column", "
|
|
13709
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "component", type: PopoverComponent, selector: "popover, Popover", inputs: ["disabled", "closeOnScroll"], outputs: ["closed"] }, { kind: "directive", type: PopoverContentComponent, selector: "popover-content, PopoverContent, popovercontent", inputs: ["class", "position", "keepOpen", "offset", "strategy"], exportAs: ["popoverContent"] }, { kind: "component", type: AggregationComponent, selector: "Aggregation, aggregation", inputs: ["class", "id", "name", "column", "showFiltersCount", "collapsible", "collapsed", "searchable", "expandedLevel"], outputs: ["onSelect"] }, { kind: "directive", type: BadgeComponent, selector: "badge, Badge", inputs: ["class", "variant", "size"] }, { kind: "pipe", type: OperatorPipe, name: "operator" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: SyslangPipe, name: "syslang" }] });
|
|
12928
13710
|
}
|
|
12929
13711
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: FilterButtonComponent, decorators: [{
|
|
12930
13712
|
type: Component,
|
|
@@ -12939,7 +13721,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
12939
13721
|
TranslocoPipe,
|
|
12940
13722
|
AggregationComponent,
|
|
12941
13723
|
SyslangPipe,
|
|
12942
|
-
DateComponent,
|
|
12943
13724
|
BadgeComponent
|
|
12944
13725
|
],
|
|
12945
13726
|
template: `
|
|
@@ -12979,21 +13760,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
12979
13760
|
[offset]="offset()"
|
|
12980
13761
|
>
|
|
12981
13762
|
@if(contentRef.isVisible) {
|
|
12982
|
-
@if (isDate(filter().column)) {
|
|
12983
|
-
<DateFilter
|
|
12984
|
-
class="*:details-content:[--height:19lh] w-max max-w-80"
|
|
12985
|
-
[name]="filter().name"
|
|
12986
|
-
[column]="filter().column"
|
|
12987
|
-
id="filter-{{ filter().column }}"
|
|
12988
|
-
[title]="{ label: 'Date', icon: 'far fa-calendar-day' }"
|
|
12989
|
-
/>
|
|
12990
|
-
} @else {
|
|
12991
13763
|
<Aggregation
|
|
12992
|
-
class="
|
|
13764
|
+
class="w-60 [--height:19lh]"
|
|
13765
|
+
id="filter-{{ filter().column }}"
|
|
12993
13766
|
[name]="filter().name"
|
|
12994
13767
|
[column]="filter().column"
|
|
12995
|
-
|
|
12996
|
-
|
|
13768
|
+
[expandedLevel]="expandedLevel()"
|
|
13769
|
+
/>
|
|
12997
13770
|
}
|
|
12998
13771
|
</PopoverContent>
|
|
12999
13772
|
</Popover>
|
|
@@ -13003,7 +13776,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
13003
13776
|
role: "listitem"
|
|
13004
13777
|
}
|
|
13005
13778
|
}]
|
|
13006
|
-
}], ctorParameters: () => [], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], popoverRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => PopoverComponent), { isSignal: true }] }] } });
|
|
13779
|
+
}], ctorParameters: () => [], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], expandedLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedLevel", required: false }] }], popoverRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => PopoverComponent), { isSignal: true }] }] } });
|
|
13007
13780
|
|
|
13008
13781
|
const FILTERS_BREAKPOINT = new InjectionToken('FILTERS_BREAKPOINT', { factory: () => 5 });
|
|
13009
13782
|
|
|
@@ -13079,17 +13852,6 @@ class MoreComponent {
|
|
|
13079
13852
|
});
|
|
13080
13853
|
});
|
|
13081
13854
|
}
|
|
13082
|
-
/**
|
|
13083
|
-
* Checks if the specified column is a date filter.
|
|
13084
|
-
*
|
|
13085
|
-
* @param column The column name to check.
|
|
13086
|
-
* @returns True if the column is a date filter, false otherwise.
|
|
13087
|
-
*/
|
|
13088
|
-
isDate(column) {
|
|
13089
|
-
if (!column)
|
|
13090
|
-
return false;
|
|
13091
|
-
return this.appStore.isDateColumn(column);
|
|
13092
|
-
}
|
|
13093
13855
|
/**
|
|
13094
13856
|
* Checks whether there are active filters for the current aggregation column.
|
|
13095
13857
|
*
|
|
@@ -13104,39 +13866,31 @@ class MoreComponent {
|
|
|
13104
13866
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MoreComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13105
13867
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: MoreComponent, isStandalone: true, selector: "more, More", inputs: { count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, includedFilters: { classPropertyName: "includedFilters", publicName: "includedFilters", isSignal: true, isRequired: false, transformFunction: null }, excludedFilters: { classPropertyName: "excludedFilters", publicName: "excludedFilters", isSignal: true, isRequired: false, transformFunction: null }, aggregations: { classPropertyName: "aggregations", publicName: "aggregations", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "divide-y divide-muted-foreground/18" }, ngImport: i0, template: `
|
|
13106
13868
|
@for (filter of visibleFilters(); track $index) {
|
|
13107
|
-
|
|
13108
|
-
|
|
13109
|
-
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
|
|
13113
|
-
|
|
13114
|
-
|
|
13115
|
-
|
|
13116
|
-
|
|
13117
|
-
|
|
13118
|
-
[column]="filter.column" />
|
|
13119
|
-
}
|
|
13120
|
-
}
|
|
13121
|
-
`, isInline: true, styles: [":host{scrollbar-width:none}\n"], dependencies: [{ kind: "component", type: AggregationComponent, selector: "Aggregation, aggregation", inputs: ["class", "id", "name", "column", "collapsible", "collapsed", "searchable", "showFiltersCount", "searchText"], outputs: ["onSelect", "searchTextChange"] }, { kind: "component", type: DateComponent, selector: "date-filter,DateFilter", inputs: ["title", "displayEmptyDistributionIntervals"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
13869
|
+
<Aggregation
|
|
13870
|
+
class="w-60 max-w-80 [--height:15lh]"
|
|
13871
|
+
id="more-filters"
|
|
13872
|
+
[attr.title]="'filters.openFilter' | transloco: { filter: filter.display || filter.name }"
|
|
13873
|
+
showFiltersCount
|
|
13874
|
+
[collapsible]="true"
|
|
13875
|
+
[collapsed]="true"
|
|
13876
|
+
[name]="filter.name"
|
|
13877
|
+
[column]="filter.column" />
|
|
13878
|
+
}
|
|
13879
|
+
`, isInline: true, styles: [":host{scrollbar-width:none}\n"], dependencies: [{ kind: "component", type: AggregationComponent, selector: "Aggregation, aggregation", inputs: ["class", "id", "name", "column", "showFiltersCount", "collapsible", "collapsed", "searchable", "expandedLevel"], outputs: ["onSelect"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
13122
13880
|
}
|
|
13123
13881
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MoreComponent, decorators: [{
|
|
13124
13882
|
type: Component,
|
|
13125
|
-
args: [{ selector: "more, More", standalone: true, imports: [AggregationComponent,
|
|
13883
|
+
args: [{ selector: "more, More", standalone: true, imports: [AggregationComponent, TranslocoPipe], template: `
|
|
13126
13884
|
@for (filter of visibleFilters(); track $index) {
|
|
13127
|
-
|
|
13128
|
-
|
|
13129
|
-
|
|
13130
|
-
|
|
13131
|
-
|
|
13132
|
-
|
|
13133
|
-
|
|
13134
|
-
|
|
13135
|
-
|
|
13136
|
-
collapsed
|
|
13137
|
-
[name]="filter.name"
|
|
13138
|
-
[column]="filter.column" />
|
|
13139
|
-
}
|
|
13885
|
+
<Aggregation
|
|
13886
|
+
class="w-60 max-w-80 [--height:15lh]"
|
|
13887
|
+
id="more-filters"
|
|
13888
|
+
[attr.title]="'filters.openFilter' | transloco: { filter: filter.display || filter.name }"
|
|
13889
|
+
showFiltersCount
|
|
13890
|
+
[collapsible]="true"
|
|
13891
|
+
[collapsed]="true"
|
|
13892
|
+
[name]="filter.name"
|
|
13893
|
+
[column]="filter.column" />
|
|
13140
13894
|
}
|
|
13141
13895
|
`, host: {
|
|
13142
13896
|
class: "divide-y divide-muted-foreground/18"
|
|
@@ -13265,6 +14019,7 @@ class FiltersBarComponent {
|
|
|
13265
14019
|
* @defaultValue 8px
|
|
13266
14020
|
*/
|
|
13267
14021
|
offset = input(8, ...(ngDevMode ? [{ debugName: "offset" }] : []));
|
|
14022
|
+
expandedLevel = input(undefined, ...(ngDevMode ? [{ debugName: "expandedLevel", transform: numberAttribute }] : [{ transform: numberAttribute }]));
|
|
13268
14023
|
/**
|
|
13269
14024
|
* Event emitted when the clear filters button is clicked.
|
|
13270
14025
|
* This event can be used to perform additional actions when filters are cleared.
|
|
@@ -13357,6 +14112,9 @@ class FiltersBarComponent {
|
|
|
13357
14112
|
*/
|
|
13358
14113
|
clearFilters() {
|
|
13359
14114
|
this.queryParamsStore.clearFilters();
|
|
14115
|
+
this.aggregationsStore.aggregations().forEach(agg => {
|
|
14116
|
+
sessionStorage.removeItem(`agg-${agg.column}`);
|
|
14117
|
+
});
|
|
13360
14118
|
this.onClearFilters.emit();
|
|
13361
14119
|
}
|
|
13362
14120
|
/**
|
|
@@ -13395,7 +14153,7 @@ class FiltersBarComponent {
|
|
|
13395
14153
|
});
|
|
13396
14154
|
}
|
|
13397
14155
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: FiltersBarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13398
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: FiltersBarComponent, isStandalone: true, selector: "filters-bar, FiltersBar, filtersbar", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, morePosition: { classPropertyName: "morePosition", publicName: "morePosition", isSignal: true, isRequired: false, transformFunction: null }, aggregations: { classPropertyName: "aggregations", publicName: "aggregations", isSignal: true, isRequired: false, transformFunction: null }, includeFilters: { classPropertyName: "includeFilters", publicName: "includeFilters", isSignal: true, isRequired: false, transformFunction: null }, excludeFilters: { classPropertyName: "excludeFilters", publicName: "excludeFilters", isSignal: true, isRequired: false, transformFunction: null }, filtersCount: { classPropertyName: "filtersCount", publicName: "filtersCount", isSignal: true, isRequired: false, transformFunction: null }, showMoreFiltersButton: { classPropertyName: "showMoreFiltersButton", publicName: "showMoreFiltersButton", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onClearFilters: "onClearFilters", onClearBasket: "onClearBasket" }, host: { listeners: { "click": "handleClick($event)" }, properties: { "class": "cn('block relative', class())" } }, providers: [provideTranslocoScope("filters")], viewQueries: [{ propertyName: "moreButtonRef", first: true, predicate: MoreButtonComponent, descendants: true, isSignal: true }, { propertyName: "filterButtonRefs", predicate: FilterButtonComponent, descendants: true, isSignal: true }, { propertyName: "overflowManagerRef", first: true, predicate: OverflowManagerDirective, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
14156
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: FiltersBarComponent, isStandalone: true, selector: "filters-bar, FiltersBar, filtersbar", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, position: { classPropertyName: "position", publicName: "position", isSignal: true, isRequired: false, transformFunction: null }, morePosition: { classPropertyName: "morePosition", publicName: "morePosition", isSignal: true, isRequired: false, transformFunction: null }, aggregations: { classPropertyName: "aggregations", publicName: "aggregations", isSignal: true, isRequired: false, transformFunction: null }, includeFilters: { classPropertyName: "includeFilters", publicName: "includeFilters", isSignal: true, isRequired: false, transformFunction: null }, excludeFilters: { classPropertyName: "excludeFilters", publicName: "excludeFilters", isSignal: true, isRequired: false, transformFunction: null }, filtersCount: { classPropertyName: "filtersCount", publicName: "filtersCount", isSignal: true, isRequired: false, transformFunction: null }, showMoreFiltersButton: { classPropertyName: "showMoreFiltersButton", publicName: "showMoreFiltersButton", isSignal: true, isRequired: false, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, offset: { classPropertyName: "offset", publicName: "offset", isSignal: true, isRequired: false, transformFunction: null }, expandedLevel: { classPropertyName: "expandedLevel", publicName: "expandedLevel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onClearFilters: "onClearFilters", onClearBasket: "onClearBasket" }, host: { listeners: { "click": "handleClick($event)" }, properties: { "class": "cn('block relative', class())" } }, providers: [provideTranslocoScope("filters")], viewQueries: [{ propertyName: "moreButtonRef", first: true, predicate: MoreButtonComponent, descendants: true, isSignal: true }, { propertyName: "filterButtonRefs", predicate: FilterButtonComponent, descendants: true, isSignal: true }, { propertyName: "overflowManagerRef", first: true, predicate: OverflowManagerDirective, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
13399
14157
|
<ng-container overflowManager [direction]="direction()" [target]="el.nativeElement" (count)="adjustFiltersCount($event)">
|
|
13400
14158
|
@if (hasFilters()) {
|
|
13401
14159
|
<button
|
|
@@ -13429,7 +14187,7 @@ class FiltersBarComponent {
|
|
|
13429
14187
|
role="list"
|
|
13430
14188
|
aria-label="Filters list">
|
|
13431
14189
|
@for (filter of authorizedFilters(); track filter.name) {
|
|
13432
|
-
<FilterButton class="max-w-60" overflowItem [name]="filter.name" [column]="filter.column" [position]="position()" [offset]="offset()" />
|
|
14190
|
+
<FilterButton class="max-w-60" overflowItem [name]="filter.name" [column]="filter.column" [position]="position()" [offset]="offset()" [expandedLevel]="expandedLevel()" />
|
|
13433
14191
|
}
|
|
13434
14192
|
|
|
13435
14193
|
<!-- More button to show more filters if there are any, it's hidden by default -->
|
|
@@ -13447,7 +14205,7 @@ class FiltersBarComponent {
|
|
|
13447
14205
|
</div>
|
|
13448
14206
|
}
|
|
13449
14207
|
</ng-container>
|
|
13450
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "component", type: MoreButtonComponent, selector: "more-button, MoreButton", inputs: ["count", "position", "includedFilters", "excludedFilters", "aggregations"] }, { kind: "component", type: FilterButtonComponent, selector: "filter-button, FilterButton", inputs: ["name", "column", "position", "offset"] }, { kind: "directive", type: OverflowManagerDirective, selector: "[overflowManager]", inputs: ["target", "margin", "direction"], outputs: ["count"] }, { kind: "directive", type: OverflowItemDirective, selector: "[overflowItem]" }, { kind: "directive", type: OverflowStopDirective, selector: "[overflowStop]" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
14208
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "component", type: MoreButtonComponent, selector: "more-button, MoreButton", inputs: ["count", "position", "includedFilters", "excludedFilters", "aggregations"] }, { kind: "component", type: FilterButtonComponent, selector: "filter-button, FilterButton", inputs: ["name", "column", "position", "offset", "expandedLevel"] }, { kind: "directive", type: OverflowManagerDirective, selector: "[overflowManager]", inputs: ["target", "margin", "direction"], outputs: ["count"] }, { kind: "directive", type: OverflowItemDirective, selector: "[overflowItem]" }, { kind: "directive", type: OverflowStopDirective, selector: "[overflowStop]" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
13451
14209
|
}
|
|
13452
14210
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: FiltersBarComponent, decorators: [{
|
|
13453
14211
|
type: Component,
|
|
@@ -13498,7 +14256,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
13498
14256
|
role="list"
|
|
13499
14257
|
aria-label="Filters list">
|
|
13500
14258
|
@for (filter of authorizedFilters(); track filter.name) {
|
|
13501
|
-
<FilterButton class="max-w-60" overflowItem [name]="filter.name" [column]="filter.column" [position]="position()" [offset]="offset()" />
|
|
14259
|
+
<FilterButton class="max-w-60" overflowItem [name]="filter.name" [column]="filter.column" [position]="position()" [offset]="offset()" [expandedLevel]="expandedLevel()" />
|
|
13502
14260
|
}
|
|
13503
14261
|
|
|
13504
14262
|
<!-- More button to show more filters if there are any, it's hidden by default -->
|
|
@@ -13522,7 +14280,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
13522
14280
|
"(click)": "handleClick($event)"
|
|
13523
14281
|
}
|
|
13524
14282
|
}]
|
|
13525
|
-
}], ctorParameters: () => [], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], morePosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "morePosition", required: false }] }], aggregations: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregations", required: false }] }], includeFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "includeFilters", required: false }] }], excludeFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "excludeFilters", required: false }] }], filtersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtersCount", required: false }] }], showMoreFiltersButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showMoreFiltersButton", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], onClearFilters: [{ type: i0.Output, args: ["onClearFilters"] }], onClearBasket: [{ type: i0.Output, args: ["onClearBasket"] }], moreButtonRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MoreButtonComponent), { isSignal: true }] }], filterButtonRefs: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => FilterButtonComponent), { isSignal: true }] }], overflowManagerRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => OverflowManagerDirective), { isSignal: true }] }] } });
|
|
14283
|
+
}], ctorParameters: () => [], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], morePosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "morePosition", required: false }] }], aggregations: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregations", required: false }] }], includeFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "includeFilters", required: false }] }], excludeFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "excludeFilters", required: false }] }], filtersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtersCount", required: false }] }], showMoreFiltersButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showMoreFiltersButton", required: false }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], offset: [{ type: i0.Input, args: [{ isSignal: true, alias: "offset", required: false }] }], expandedLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedLevel", required: false }] }], onClearFilters: [{ type: i0.Output, args: ["onClearFilters"] }], onClearBasket: [{ type: i0.Output, args: ["onClearBasket"] }], moreButtonRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => MoreButtonComponent), { isSignal: true }] }], filterButtonRefs: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => FilterButtonComponent), { isSignal: true }] }], overflowManagerRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => OverflowManagerDirective), { isSignal: true }] }] } });
|
|
13526
14284
|
|
|
13527
14285
|
class LabelService {
|
|
13528
14286
|
appStore = inject(AppStore);
|
|
@@ -13846,7 +14604,7 @@ class MultiSelectLabelsComponent {
|
|
|
13846
14604
|
{{ 'labels.error' | transloco }}
|
|
13847
14605
|
</div>
|
|
13848
14606
|
}
|
|
13849
|
-
`, isInline: true, styles: [".anchor:has(.popover:popover-open){z-index:var(--z-menu, 1000);border-radius:var(--radius) var(--radius) 0 0}.anchor .popover::backdrop{background-color:transparent;-webkit-backdrop-filter:none;backdrop-filter:none}.popover{width:anchor-size(width);top:anchor(bottom);left:anchor(left)}@supports (-moz-appearance: none){.popover{margin:calc(33.3333333333vh + 30px) 25vw;width:50vw}}@supports (background: -webkit-named-image(i)){.popover{margin:calc(33.3333333333vh + 30px) 25vw;width:50vw}}\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: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { 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\"]", inputs: ["class", "variant", "decoration"] }, { kind: "directive", type: BadgeComponent, selector: "badge, Badge", inputs: ["class", "variant", "size"] }, { kind: "directive", type: ListItemComponent, selector: "[role=\"listitem\"], [role=\"option\"]", inputs: ["class", "variant", "decoration"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
14607
|
+
`, isInline: true, styles: [".anchor:has(.popover:popover-open){z-index:var(--z-menu, 1000);border-radius:var(--radius) var(--radius) 0 0}.anchor .popover::backdrop{background-color:transparent;-webkit-backdrop-filter:none;backdrop-filter:none}.popover{width:anchor-size(width);top:anchor(bottom);left:anchor(left)}@supports (-moz-appearance: none){.popover{margin:calc(33.3333333333vh + 30px) 25vw;width:50vw}}@supports (background: -webkit-named-image(i)){.popover{margin:calc(33.3333333333vh + 30px) 25vw;width:50vw}}\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: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { 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: BadgeComponent, selector: "badge, Badge", inputs: ["class", "variant", "size"] }, { kind: "directive", type: ListItemComponent, selector: "[role=\"listitem\"], [role=\"option\"]", inputs: ["class", "variant", "decoration"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
13850
14608
|
}
|
|
13851
14609
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: MultiSelectLabelsComponent, decorators: [{
|
|
13852
14610
|
type: Component,
|
|
@@ -14320,7 +15078,7 @@ class SavedSearchDialog {
|
|
|
14320
15078
|
</DialogFooter>
|
|
14321
15079
|
</DialogContent>
|
|
14322
15080
|
</dialog>
|
|
14323
|
-
`, isInline: true, 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", "size"] }, { kind: "component", type: DialogComponent, selector: "dialog, [dialog]", outputs: ["closed"], exportAs: ["dialog"] }, { kind: "component", type: DialogHeaderComponent, selector: "DialogHeader" }, { kind: "directive", type: DialogTitleComponent, selector: "DialogTitle", inputs: ["class"] }, { kind: "directive", type: DialogContentComponent, selector: "DialogContent", inputs: ["class"] }, { kind: "directive", type: DialogFooterComponent, selector: "DialogFooter", inputs: ["class"] }, { 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\"]", inputs: ["class", "variant", "decoration"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
15081
|
+
`, isInline: true, 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", "size"] }, { kind: "component", type: DialogComponent, selector: "dialog, [dialog]", outputs: ["closed"], exportAs: ["dialog"] }, { kind: "component", type: DialogHeaderComponent, selector: "DialogHeader" }, { kind: "directive", type: DialogTitleComponent, selector: "DialogTitle", inputs: ["class"] }, { kind: "directive", type: DialogContentComponent, selector: "DialogContent", inputs: ["class"] }, { kind: "directive", type: DialogFooterComponent, selector: "DialogFooter", inputs: ["class"] }, { 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: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
14324
15082
|
}
|
|
14325
15083
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: SavedSearchDialog, decorators: [{
|
|
14326
15084
|
type: Component,
|
|
@@ -14443,7 +15201,13 @@ class UserProfileFormComponent {
|
|
|
14443
15201
|
appStore = inject(AppStore);
|
|
14444
15202
|
transloco = inject(TranslocoService);
|
|
14445
15203
|
createInputElement = viewChild("avatarInput", ...(ngDevMode ? [{ debugName: "createInputElement" }] : []));
|
|
14446
|
-
dataKeys = ["fullName", "mail", "jobTitle", "group", "organisation", "location"];
|
|
15204
|
+
dataKeys = ["fullName", "mail", "jobTitle", "group", "organisation", "location", "skills", "profilePhoto"];
|
|
15205
|
+
keys = computed(() => {
|
|
15206
|
+
const data = this.appStore.general()?.features?.userProfile?.data;
|
|
15207
|
+
return data?.length ? this.dataKeys.filter(key => data.some(k => k === key)) : this.dataKeys;
|
|
15208
|
+
}, ...(ngDevMode ? [{ debugName: "keys" }] : []));
|
|
15209
|
+
formKeys = computed(() => this.keys().filter(k => k !== "profilePhoto"), ...(ngDevMode ? [{ debugName: "formKeys" }] : []));
|
|
15210
|
+
allowProfilePhoto = computed(() => this.keys().some(k => k === "profilePhoto"), ...(ngDevMode ? [{ debugName: "allowProfilePhoto" }] : []));
|
|
14447
15211
|
initials = computed(() => {
|
|
14448
15212
|
const fullName = this.userProfile()?.data?.fullName;
|
|
14449
15213
|
if (!fullName)
|
|
@@ -14453,6 +15217,7 @@ class UserProfileFormComponent {
|
|
|
14453
15217
|
.map((word) => word[0].toUpperCase())
|
|
14454
15218
|
.join("");
|
|
14455
15219
|
}, ...(ngDevMode ? [{ debugName: "initials" }] : []));
|
|
15220
|
+
username = computed(() => this.principalStore.name() || this.principalStore.email() || null, ...(ngDevMode ? [{ debugName: "username" }] : []));
|
|
14456
15221
|
propertyToEdit = signal(undefined, ...(ngDevMode ? [{ debugName: "propertyToEdit" }] : []));
|
|
14457
15222
|
value = model(undefined, ...(ngDevMode ? [{ debugName: "value" }] : []));
|
|
14458
15223
|
currentLanguage = model(...(ngDevMode ? [undefined, { debugName: "currentLanguage" }] : []));
|
|
@@ -14464,11 +15229,15 @@ class UserProfileFormComponent {
|
|
|
14464
15229
|
principal = computed(() => this.principalStore.principal?.(), ...(ngDevMode ? [{ debugName: "principal" }] : []));
|
|
14465
15230
|
customData = computed(() => {
|
|
14466
15231
|
const customData = this.appStore.general()?.features?.userProfile?.customData;
|
|
14467
|
-
|
|
14468
|
-
|
|
14469
|
-
|
|
14470
|
-
return data ? Object.keys(data) : [];
|
|
15232
|
+
const profileKeys = this.userProfile()?.customData;
|
|
15233
|
+
return (customData?.length ? customData : (profileKeys ? Object.keys(profileKeys) : []))
|
|
15234
|
+
.filter(key => !this.keys().some(k => k.toLowerCase() === key.toLowerCase())); // hide any duplicates in customData keys from data's
|
|
14471
15235
|
}, ...(ngDevMode ? [{ debugName: "customData" }] : []));
|
|
15236
|
+
allowChangePassword = computed(() => {
|
|
15237
|
+
const { useCredentials } = globalConfig;
|
|
15238
|
+
const { allowChangePassword = false } = this.appStore.general()?.features || {};
|
|
15239
|
+
return allowChangePassword && useCredentials && this.principal().editablePartition;
|
|
15240
|
+
}, ...(ngDevMode ? [{ debugName: "allowChangePassword" }] : []));
|
|
14472
15241
|
constructor() {
|
|
14473
15242
|
this.currentLanguage.set(this.transloco.getActiveLang());
|
|
14474
15243
|
effect(() => {
|
|
@@ -14518,14 +15287,17 @@ class UserProfileFormComponent {
|
|
|
14518
15287
|
* @param category data or customData
|
|
14519
15288
|
* @param propertyName property name
|
|
14520
15289
|
*/
|
|
14521
|
-
onDeleteData(category, propertyName) {
|
|
14522
|
-
this.userProfile
|
|
14523
|
-
|
|
14524
|
-
|
|
14525
|
-
|
|
14526
|
-
|
|
14527
|
-
|
|
14528
|
-
|
|
15290
|
+
async onDeleteData(category, propertyName) {
|
|
15291
|
+
const response = await deleteUserProfileProperty(this.userProfile(), category, propertyName);
|
|
15292
|
+
if (response) {
|
|
15293
|
+
info("Deleted user profile property", response);
|
|
15294
|
+
const userProfile = JSON.parse(JSON.stringify(this.userProfile()));
|
|
15295
|
+
userProfile[category][propertyName] = undefined;
|
|
15296
|
+
this.userProfileResource.set(userProfile);
|
|
15297
|
+
}
|
|
15298
|
+
else {
|
|
15299
|
+
info("Could not delete user profile property", response);
|
|
15300
|
+
}
|
|
14529
15301
|
}
|
|
14530
15302
|
/**
|
|
14531
15303
|
* Saving a data property
|
|
@@ -14545,8 +15317,8 @@ class UserProfileFormComponent {
|
|
|
14545
15317
|
this.userProfileResource.set({
|
|
14546
15318
|
...newUserProfile,
|
|
14547
15319
|
...response,
|
|
14548
|
-
data: { ...(
|
|
14549
|
-
customData: { ...(
|
|
15320
|
+
data: { ...(newUserProfile?.data || {}), ...(response.data || {}) },
|
|
15321
|
+
customData: { ...(newUserProfile?.customData || {}), ...(response.customData || {}) }
|
|
14550
15322
|
});
|
|
14551
15323
|
}
|
|
14552
15324
|
this.value.set(undefined);
|
|
@@ -14594,7 +15366,7 @@ class UserProfileFormComponent {
|
|
|
14594
15366
|
}
|
|
14595
15367
|
}
|
|
14596
15368
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: UserProfileFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14597
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: UserProfileFormComponent, isStandalone: true, selector: "user-profile-form, UserProfileForm, userprofileform", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, currentLanguage: { classPropertyName: "currentLanguage", publicName: "currentLanguage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", currentLanguage: "currentLanguageChange" }, providers: [provideTranslocoScope("user-profile", "login")], viewQueries: [{ propertyName: "createInputElement", first: true, predicate: ["avatarInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (userProfileResource.hasValue()) {\n @let profile = userProfile();\n <div class=\"flex flex-col gap-2 p-4\">\n <div class=\"flex mb-4\">\n <!-- AVATAR -->\n <Avatar\n class=\"bg-accent text-accent-foreground hover:bg-accent/80 hover:text-accent-foreground/80 cursor-pointer size-14\"\n (click)=\"avatarInput.click()\">\n @if (profile?.data?.profilePhoto) {\n <AvatarImage [src]=\"profile?.data?.profilePhoto!\" width=\"44\" height=\"44\" alt=\"avatar\" />\n }\n <AvatarFallback class=\"text-lg\">\n @if (initials()) {\n <span>{{ initials() }}</span>\n }\n @else {\n <UserIcon class=\"size-7 p-1\" />\n }\n </AvatarFallback>\n </Avatar>\n <div class=\"grow\"></div>\n <div class=\"flex flex-col gap-4\">\n <!-- LANGUAGE -->\n <select\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 rounded-md border px-2 hover:outline focus:outline\"\n [(ngModel)]=\"currentLanguage\" (change)=\"changeLanguage()\">\n @for (lang of AllLanguages; track lang.code) {\n <option [value]=\"lang.code\">\n {{ lang.label }}\n </option>\n }\n </select>\n <!-- CHANGE PASSWORD -->\n @if (principal().editablePartition) {\n <button [disabled]=\"changingPassword()\" (click)=\"changingPassword.set(true)\">\n {{ \"login.changePassword\" | transloco }}\n </button>\n }\n </div>\n </div>\n @if (changingPassword()) {\n <ChangePassword\n [redirectAfterSuccess]=\"false\"\n (cancel)=\"changingPassword.set(false)\"\n (success)=\"changingPassword.set(false)\" />\n }\n @else {\n @for (key of dataKeys; track key) {\n <div>\n <p>{{ `userProfile.data.${key}` | transloco}}</p>\n <div class=\"flex flex-row group\">\n <div class=\"grow\" [class.text-muted-foreground]=\"!getDataValue('data', key)\">\n @if (propertyToEdit() === 'data.'+key) {\n <input id=\"user-profile-{{key}}\" type=\"text\" [(ngModel)]=\"value\" (keydown.enter)=\"onSaveData('data', key)\" />\n } @else {\n {{ getDataValue('data', key) || ('userProfile.notDefined' | transloco) }}\n }\n </div>\n @if (propertyToEdit() === 'data.'+key) {\n <button variant=\"ghost\" size=\"icon\" class=\"mx-2 cursor-pointer\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\" [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onSaveData('data', key)\">\n <i class=\"fa-fw fas fa-save\"></i>\n </button>\n <button variant=\"ghost\" size=\"icon\" class=\"cursor-pointer\" title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\" (click)=\"propertyToEdit.set(undefined)\">\n <UndoIcon />\n </button>\n } @else {\n <button variant=\"ghost\" size=\"icon\" class=\"invisible group-hover:visible mx-2 cursor-pointer\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\" [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onEdit('data', key)\">\n <EditIcon />\n </button>\n <button variant=\"ghost\" size=\"icon\" class=\"text-destructive invisible group-hover:visible cursor-pointer\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\" (click)=\"onDeleteData('data', key)\">\n <TrashIcon />\n </button>\n }\n </div>\n </div>\n }\n @for (key of customData(); track key) {\n <div>\n <p>{{ `userProfile.customData.${key}` | transloco }}</p>\n <div class=\"flex flex-row group\">\n <div class=\"grow\" [class.text-muted-foreground]=\"!getDataValue('customData', key)\">\n @if (propertyToEdit() === 'customData.'+key) {\n <input id=\"user-profile-{{key}}\" type=\"text\" [(ngModel)]=\"value\" (keydown.enter)=\"onSaveData('customData', key)\" />\n } @else {\n {{ getDataValue('customData', key) || ('userProfile.notDefined' | transloco) }}\n }\n </div>\n @if (propertyToEdit() === 'customData.'+key) {\n <button variant=\"ghost\" size=\"icon\" class=\"mx-2 cursor-pointer\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\" [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onSaveData('customData', key)\">\n <i class=\"fa-fw fas fa-save\"></i>\n </button>\n <button variant=\"ghost\" size=\"icon\" class=\"cursor-pointer\" title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\" (click)=\"propertyToEdit.set(undefined)\">\n <UndoIcon />\n </button>\n } @else {\n <button variant=\"ghost\" size=\"icon\" class=\"invisible group-hover:visible mx-2 cursor-pointer\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\" [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onEdit('customData', key)\">\n <EditIcon />\n </button>\n <button variant=\"ghost\" size=\"icon\" class=\"text-destructive invisible group-hover:visible cursor-pointer\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\" (click)=\"onDeleteData('customData', key)\">\n <TrashIcon />\n </button>\n }\n </div>\n </div>\n }\n }\n </div>\n}\n@else if (userProfileResource.isLoading()) {\n <span>Loading...</span>\n}\n@else if (userProfileResource.error()) {\n <p>Please contact an administrator to create your user profile.</p>\n}\n\n<!-- AVATAR UPLOAD -->\n<input #avatarInput class=\"hidden\" type=\"file\" accept=\"image/*\" (change)=\"uploadAvatar($event)\" />", dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: RouterModule }, { 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\"]", inputs: ["class", "variant", "decoration"] }, { kind: "component", type: TrashIcon, selector: "trash-icon, TrashIcon", inputs: ["class"] }, { kind: "component", type: EditIcon, selector: "edit-icon, EditIcon, editicon", inputs: ["class"] }, { kind: "component", type: UndoIcon, selector: "UndoIcon, undoicon, undo-icon", inputs: ["class"] }, { kind: "component", type: AvatarComponent, selector: "avatar, Avatar", inputs: ["class"] }, { kind: "component", type: AvatarFallbackComponent, selector: "avatar-fallback, avatarfallback, AvatarFallback", inputs: ["class"] }, { kind: "component", type: UserIcon, selector: "user-icon, UserIcon", inputs: ["class"] }, { kind: "component", type: AvatarImageComponent, selector: "avatar-image, AvatarImage, avatarimage", inputs: ["class", "src", "alt", "width", "height", "referrerPolicy", "crossOrigin"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "component", type: ChangePasswordComponent, selector: "change-password, ChangePassword, changepassword", inputs: ["username", "alert", "redirectAfterSuccess", "currentPassword", "newPassword", "confirmPassword"], outputs: ["success", "cancel", "currentPasswordChange", "newPasswordChange", "confirmPasswordChange"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
15369
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: UserProfileFormComponent, isStandalone: true, selector: "user-profile-form, UserProfileForm, userprofileform", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, currentLanguage: { classPropertyName: "currentLanguage", publicName: "currentLanguage", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", currentLanguage: "currentLanguageChange" }, providers: [provideTranslocoScope("user-profile", "login")], viewQueries: [{ propertyName: "createInputElement", first: true, predicate: ["avatarInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (userProfileResource.hasValue()) {\n @let profile = userProfile();\n <div class=\"flex flex-col gap-2 p-4\">\n <div class=\"mb-4 flex\">\n @if (allowProfilePhoto()) {\n <!-- AVATAR -->\n <Avatar\n class=\"bg-accent text-accent-foreground hover:bg-accent/80 hover:text-accent-foreground/80 size-14 cursor-pointer\"\n (click)=\"avatarInput.click()\">\n @if (profile?.data?.profilePhoto) {\n <AvatarImage\n [src]=\"profile?.data?.profilePhoto!\"\n width=\"44\"\n height=\"44\"\n alt=\"avatar\" />\n }\n <AvatarFallback class=\"text-lg\">\n @if (initials()) {\n <span>{{ initials() }}</span>\n } @else {\n <UserIcon class=\"size-7 p-1\" />\n }\n </AvatarFallback>\n </Avatar>\n @if (profile?.data?.profilePhoto) {\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"text-destructive invisible cursor-pointer group-hover:visible\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\n (click)=\"onDeleteData('data', 'profilePhoto')\">\n <TrashIcon />\n </button>\n }\n }\n <div class=\"grow\"></div>\n <div class=\"flex flex-col gap-4\">\n <!-- LANGUAGE -->\n <select\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 rounded-md border px-2 hover:outline focus:outline\"\n [(ngModel)]=\"currentLanguage\"\n (change)=\"changeLanguage()\">\n @for (lang of AllLanguages; track lang.code) {\n <option [value]=\"lang.code\">\n {{ lang.label }}\n </option>\n }\n </select>\n <!-- CHANGE PASSWORD -->\n @if (allowChangePassword()) {\n <button\n [disabled]=\"changingPassword()\"\n (click)=\"changingPassword.set(true)\">\n {{ \"login.changePassword\" | transloco }}\n </button>\n }\n </div>\n </div>\n @if (changingPassword()) {\n <ChangePassword\n [username]=\"username()\"\n [redirectAfterSuccess]=\"false\"\n [redirectAfterCancel]=\"false\"\n (cancel)=\"changingPassword.set(false)\"\n (success)=\"changingPassword.set(false)\" />\n } @else {\n @for (key of formKeys(); track key) {\n <div>\n <p>{{ `userProfile.data.${key}` | transloco }}</p>\n <div class=\"group flex flex-row\">\n <div\n class=\"grow whitespace-pre-line\"\n [class.text-muted-foreground]=\"!getDataValue('data', key)\">\n @if (propertyToEdit() === \"data.\" + key) {\n <textarea\n class=\"hover:outline-primary focus:outline-primary border-foreground/20 hover:bg-muted focus:bg-muted mt-2 w-full rounded-md border px-2 pt-1 hover:outline focus:outline\"\n id=\"user-profile-{{ key }}\"\n [(ngModel)]=\"value\"></textarea>\n } @else {\n {{\n getDataValue(\"data\", key) ||\n (\"userProfile.notDefined\" | transloco)\n }}\n }\n </div>\n @if (propertyToEdit() === \"data.\" + key) {\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"mx-2 cursor-pointer\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onSaveData('data', key)\">\n <i class=\"fa-fw fas fa-save ml-2\"></i>\n </button>\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"cursor-pointer\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"propertyToEdit.set(undefined)\">\n <UndoIcon />\n </button>\n } @else {\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"invisible mx-2 cursor-pointer group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onEdit('data', key)\">\n <EditIcon />\n </button>\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"text-destructive invisible cursor-pointer group-hover:visible\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\n (click)=\"onDeleteData('data', key)\">\n <TrashIcon />\n </button>\n }\n </div>\n </div>\n }\n @for (key of customData(); track key) {\n <div>\n <p>{{ key }}</p>\n <div class=\"group flex flex-row\">\n <div\n class=\"grow whitespace-pre-line\"\n [class.text-muted-foreground]=\"!getDataValue('customData', key)\">\n @if (propertyToEdit() === \"customData.\" + key) {\n <textarea\n class=\"hover:outline-primary focus:outline-primary border-foreground/20 hover:bg-muted focus:bg-muted mt-2 w-full rounded-md border px-2 pt-1 hover:outline focus:outline\"\n id=\"user-profile-{{ key }}\"\n [(ngModel)]=\"value\"></textarea>\n } @else {\n {{\n getDataValue(\"customData\", key) ||\n (\"userProfile.notDefined\" | transloco)\n }}\n }\n </div>\n @if (propertyToEdit() === \"customData.\" + key) {\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"mx-2 cursor-pointer\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onSaveData('customData', key)\">\n <i class=\"fa-fw fas fa-save ml-2\"></i>\n </button>\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"cursor-pointer\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"propertyToEdit.set(undefined)\">\n <UndoIcon />\n </button>\n } @else {\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"invisible mx-2 cursor-pointer group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onEdit('customData', key)\">\n <EditIcon />\n </button>\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"text-destructive invisible cursor-pointer group-hover:visible\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\n (click)=\"onDeleteData('customData', key)\">\n <TrashIcon />\n </button>\n }\n </div>\n </div>\n }\n }\n </div>\n} @else if (userProfileResource.isLoading()) {\n <span>Loading...</span>\n} @else if (userProfileResource.error()) {\n <p>Please contact an administrator to create your user profile.</p>\n}\n\n<!-- AVATAR UPLOAD -->\n<input\n #avatarInput\n class=\"hidden\"\n type=\"file\"\n accept=\"image/*\"\n (change)=\"uploadAvatar($event)\" />\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { 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.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: RouterModule }, { 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: "component", type: TrashIcon, selector: "trash-icon, TrashIcon", inputs: ["class"] }, { kind: "component", type: EditIcon, selector: "edit-icon, EditIcon, editicon", inputs: ["class"] }, { kind: "component", type: UndoIcon, selector: "UndoIcon, undoicon, undo-icon", inputs: ["class"] }, { kind: "component", type: AvatarComponent, selector: "avatar, Avatar", inputs: ["class"] }, { kind: "component", type: AvatarFallbackComponent, selector: "avatar-fallback, avatarfallback, AvatarFallback", inputs: ["class"] }, { kind: "component", type: UserIcon, selector: "user-icon, UserIcon", inputs: ["class"] }, { kind: "component", type: AvatarImageComponent, selector: "avatar-image, AvatarImage, avatarimage", inputs: ["class", "src", "alt", "width", "height", "referrerPolicy", "crossOrigin"] }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "size"] }, { kind: "component", type: ChangePasswordComponent, selector: "change-password, ChangePassword, changepassword", inputs: ["username", "alert", "redirectAfterSuccess", "redirectAfterCancel", "currentPassword", "newPassword", "confirmPassword"], outputs: ["success", "cancel", "currentPasswordChange", "newPasswordChange", "confirmPasswordChange"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
14598
15370
|
}
|
|
14599
15371
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: UserProfileFormComponent, decorators: [{
|
|
14600
15372
|
type: Component,
|
|
@@ -14612,7 +15384,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
14612
15384
|
AvatarImageComponent,
|
|
14613
15385
|
ButtonComponent,
|
|
14614
15386
|
ChangePasswordComponent
|
|
14615
|
-
], providers: [provideTranslocoScope("user-profile", "login")], template: "@if (userProfileResource.hasValue()) {\n @let profile = userProfile();\n <div class=\"flex flex-col gap-2 p-4\">\n <div class=\"
|
|
15387
|
+
], providers: [provideTranslocoScope("user-profile", "login")], template: "@if (userProfileResource.hasValue()) {\n @let profile = userProfile();\n <div class=\"flex flex-col gap-2 p-4\">\n <div class=\"mb-4 flex\">\n @if (allowProfilePhoto()) {\n <!-- AVATAR -->\n <Avatar\n class=\"bg-accent text-accent-foreground hover:bg-accent/80 hover:text-accent-foreground/80 size-14 cursor-pointer\"\n (click)=\"avatarInput.click()\">\n @if (profile?.data?.profilePhoto) {\n <AvatarImage\n [src]=\"profile?.data?.profilePhoto!\"\n width=\"44\"\n height=\"44\"\n alt=\"avatar\" />\n }\n <AvatarFallback class=\"text-lg\">\n @if (initials()) {\n <span>{{ initials() }}</span>\n } @else {\n <UserIcon class=\"size-7 p-1\" />\n }\n </AvatarFallback>\n </Avatar>\n @if (profile?.data?.profilePhoto) {\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"text-destructive invisible cursor-pointer group-hover:visible\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\n (click)=\"onDeleteData('data', 'profilePhoto')\">\n <TrashIcon />\n </button>\n }\n }\n <div class=\"grow\"></div>\n <div class=\"flex flex-col gap-4\">\n <!-- LANGUAGE -->\n <select\n class=\"hover:outline-primary focus:outline-primary border-foreground/10 bg-background hover:bg-muted focus:bg-muted h-8 rounded-md border px-2 hover:outline focus:outline\"\n [(ngModel)]=\"currentLanguage\"\n (change)=\"changeLanguage()\">\n @for (lang of AllLanguages; track lang.code) {\n <option [value]=\"lang.code\">\n {{ lang.label }}\n </option>\n }\n </select>\n <!-- CHANGE PASSWORD -->\n @if (allowChangePassword()) {\n <button\n [disabled]=\"changingPassword()\"\n (click)=\"changingPassword.set(true)\">\n {{ \"login.changePassword\" | transloco }}\n </button>\n }\n </div>\n </div>\n @if (changingPassword()) {\n <ChangePassword\n [username]=\"username()\"\n [redirectAfterSuccess]=\"false\"\n [redirectAfterCancel]=\"false\"\n (cancel)=\"changingPassword.set(false)\"\n (success)=\"changingPassword.set(false)\" />\n } @else {\n @for (key of formKeys(); track key) {\n <div>\n <p>{{ `userProfile.data.${key}` | transloco }}</p>\n <div class=\"group flex flex-row\">\n <div\n class=\"grow whitespace-pre-line\"\n [class.text-muted-foreground]=\"!getDataValue('data', key)\">\n @if (propertyToEdit() === \"data.\" + key) {\n <textarea\n class=\"hover:outline-primary focus:outline-primary border-foreground/20 hover:bg-muted focus:bg-muted mt-2 w-full rounded-md border px-2 pt-1 hover:outline focus:outline\"\n id=\"user-profile-{{ key }}\"\n [(ngModel)]=\"value\"></textarea>\n } @else {\n {{\n getDataValue(\"data\", key) ||\n (\"userProfile.notDefined\" | transloco)\n }}\n }\n </div>\n @if (propertyToEdit() === \"data.\" + key) {\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"mx-2 cursor-pointer\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onSaveData('data', key)\">\n <i class=\"fa-fw fas fa-save ml-2\"></i>\n </button>\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"cursor-pointer\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"propertyToEdit.set(undefined)\">\n <UndoIcon />\n </button>\n } @else {\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"invisible mx-2 cursor-pointer group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onEdit('data', key)\">\n <EditIcon />\n </button>\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"text-destructive invisible cursor-pointer group-hover:visible\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\n (click)=\"onDeleteData('data', key)\">\n <TrashIcon />\n </button>\n }\n </div>\n </div>\n }\n @for (key of customData(); track key) {\n <div>\n <p>{{ key }}</p>\n <div class=\"group flex flex-row\">\n <div\n class=\"grow whitespace-pre-line\"\n [class.text-muted-foreground]=\"!getDataValue('customData', key)\">\n @if (propertyToEdit() === \"customData.\" + key) {\n <textarea\n class=\"hover:outline-primary focus:outline-primary border-foreground/20 hover:bg-muted focus:bg-muted mt-2 w-full rounded-md border px-2 pt-1 hover:outline focus:outline\"\n id=\"user-profile-{{ key }}\"\n [(ngModel)]=\"value\"></textarea>\n } @else {\n {{\n getDataValue(\"customData\", key) ||\n (\"userProfile.notDefined\" | transloco)\n }}\n }\n </div>\n @if (propertyToEdit() === \"customData.\" + key) {\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"mx-2 cursor-pointer\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onSaveData('customData', key)\">\n <i class=\"fa-fw fas fa-save ml-2\"></i>\n </button>\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"cursor-pointer\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"propertyToEdit.set(undefined)\">\n <UndoIcon />\n </button>\n } @else {\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"invisible mx-2 cursor-pointer group-hover:visible\"\n title=\"{{ 'userProfile.editProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.editProperty' | transloco\"\n (click)=\"onEdit('customData', key)\">\n <EditIcon />\n </button>\n <button\n variant=\"ghost\"\n size=\"icon\"\n class=\"text-destructive invisible cursor-pointer group-hover:visible\"\n title=\"{{ 'userProfile.deleteProperty' | transloco }}\"\n [attr.aria-label]=\"'userProfile.deleteProperty' | transloco\"\n (click)=\"onDeleteData('customData', key)\">\n <TrashIcon />\n </button>\n }\n </div>\n </div>\n }\n }\n </div>\n} @else if (userProfileResource.isLoading()) {\n <span>Loading...</span>\n} @else if (userProfileResource.error()) {\n <p>Please contact an administrator to create your user profile.</p>\n}\n\n<!-- AVATAR UPLOAD -->\n<input\n #avatarInput\n class=\"hidden\"\n type=\"file\"\n accept=\"image/*\"\n (change)=\"uploadAvatar($event)\" />\n" }]
|
|
14616
15388
|
}], ctorParameters: () => [], propDecorators: { createInputElement: [{ type: i0.ViewChild, args: ["avatarInput", { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], currentLanguage: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentLanguage", required: false }] }, { type: i0.Output, args: ["currentLanguageChange"] }] } });
|
|
14617
15389
|
|
|
14618
15390
|
class UserProfileDialog {
|
|
@@ -14623,11 +15395,11 @@ class UserProfileDialog {
|
|
|
14623
15395
|
this.opened.set(true);
|
|
14624
15396
|
}
|
|
14625
15397
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: UserProfileDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14626
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: UserProfileDialog, isStandalone: true, selector: "user-profile-dialog, userprofiledialog, UserProfileDialog", viewQueries: [{ propertyName: "dialog", first: true, predicate: DialogComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
15398
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.16", type: UserProfileDialog, isStandalone: true, selector: "user-profile-dialog, userprofiledialog, UserProfileDialog", providers: [provideTranslocoScope("user-profile")], viewQueries: [{ propertyName: "dialog", first: true, predicate: DialogComponent, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
14627
15399
|
<dialog #dialog>
|
|
14628
15400
|
<DialogContent class="max-h-11/12 overflow-auto">
|
|
14629
15401
|
<DialogHeader>
|
|
14630
|
-
<DialogTitle>
|
|
15402
|
+
<DialogTitle>{{ 'userProfile.title' | transloco }}</DialogTitle>
|
|
14631
15403
|
</DialogHeader>
|
|
14632
15404
|
|
|
14633
15405
|
@if (opened()) {
|
|
@@ -14636,7 +15408,7 @@ class UserProfileDialog {
|
|
|
14636
15408
|
|
|
14637
15409
|
</DialogContent>
|
|
14638
15410
|
</dialog>
|
|
14639
|
-
`, isInline: true, dependencies: [{ kind: "component", type: DialogComponent, selector: "dialog, [dialog]", outputs: ["closed"], exportAs: ["dialog"] }, { kind: "component", type: DialogHeaderComponent, selector: "DialogHeader" }, { kind: "directive", type: DialogContentComponent, selector: "DialogContent", inputs: ["class"] }, { kind: "component", type: UserProfileFormComponent, selector: "user-profile-form, UserProfileForm, userprofileform", inputs: ["value", "currentLanguage"], outputs: ["valueChange", "currentLanguageChange"] }, { kind: "directive", type: DialogTitleComponent, selector: "DialogTitle", inputs: ["class"] }] });
|
|
15411
|
+
`, isInline: true, dependencies: [{ kind: "component", type: DialogComponent, selector: "dialog, [dialog]", outputs: ["closed"], exportAs: ["dialog"] }, { kind: "component", type: DialogHeaderComponent, selector: "DialogHeader" }, { kind: "directive", type: DialogContentComponent, selector: "DialogContent", inputs: ["class"] }, { kind: "component", type: UserProfileFormComponent, selector: "user-profile-form, UserProfileForm, userprofileform", inputs: ["value", "currentLanguage"], outputs: ["valueChange", "currentLanguageChange"] }, { kind: "directive", type: DialogTitleComponent, selector: "DialogTitle", inputs: ["class"] }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }] });
|
|
14640
15412
|
}
|
|
14641
15413
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImport: i0, type: UserProfileDialog, decorators: [{
|
|
14642
15414
|
type: Component,
|
|
@@ -14648,13 +15420,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
14648
15420
|
DialogHeaderComponent,
|
|
14649
15421
|
DialogContentComponent,
|
|
14650
15422
|
UserProfileFormComponent,
|
|
14651
|
-
DialogTitleComponent
|
|
15423
|
+
DialogTitleComponent,
|
|
15424
|
+
TranslocoPipe
|
|
14652
15425
|
],
|
|
15426
|
+
providers: [provideTranslocoScope("user-profile")],
|
|
14653
15427
|
template: `
|
|
14654
15428
|
<dialog #dialog>
|
|
14655
15429
|
<DialogContent class="max-h-11/12 overflow-auto">
|
|
14656
15430
|
<DialogHeader>
|
|
14657
|
-
<DialogTitle>
|
|
15431
|
+
<DialogTitle>{{ 'userProfile.title' | transloco }}</DialogTitle>
|
|
14658
15432
|
</DialogHeader>
|
|
14659
15433
|
|
|
14660
15434
|
@if (opened()) {
|
|
@@ -14797,6 +15571,7 @@ const errorInterceptorFn = (request, next) => {
|
|
|
14797
15571
|
}));
|
|
14798
15572
|
};
|
|
14799
15573
|
|
|
15574
|
+
const SHOW_ERROR_TOKEN = new HttpContextToken(() => true);
|
|
14800
15575
|
/**
|
|
14801
15576
|
* Intercepts HTTP requests and handles errors by displaying toast notifications.
|
|
14802
15577
|
*
|
|
@@ -14814,6 +15589,18 @@ const toastInterceptorFn = (request, next) => {
|
|
|
14814
15589
|
}
|
|
14815
15590
|
return next(request).pipe(catchError$1((err) => {
|
|
14816
15591
|
const { status, statusText, error, url } = err;
|
|
15592
|
+
if (status === 401 && globalConfig.userOverrideActive && error?.errorCode == 6) {
|
|
15593
|
+
if (request.context.get(SHOW_ERROR_TOKEN)) {
|
|
15594
|
+
const errorMsg = error?.errorMessage || 'The account may be deactivated or inaccessible';
|
|
15595
|
+
notify.error(`Cannot override user: Unauthorized access`, {
|
|
15596
|
+
description: errorMsg,
|
|
15597
|
+
closeButton: true,
|
|
15598
|
+
duration: 5000
|
|
15599
|
+
});
|
|
15600
|
+
request.context.set(SHOW_ERROR_TOKEN, false);
|
|
15601
|
+
return throwError(() => err);
|
|
15602
|
+
}
|
|
15603
|
+
}
|
|
14817
15604
|
if ([400, 403, 500, 503].includes(status)) {
|
|
14818
15605
|
// Avoid showing error toasts for preview requests
|
|
14819
15606
|
if (url.includes("api/v1/preview") === false) {
|
|
@@ -14849,5 +15636,5 @@ const queryNameResolver = () => {
|
|
|
14849
15636
|
* Generated bundle index. Do not edit.
|
|
14850
15637
|
*/
|
|
14851
15638
|
|
|
14852
|
-
export { AGGREGATIONS_NAMES, AGGREGATIONS_NAMES_PRESET_DEFAULT, APP_FEATURES, AdvancedFiltersComponent, AdvancedSearch, AdvancedSearchComponent, AggregationComponent, AggregationsService, AggregationsStore, Alert, AlertDialog, AlertsComponent, AppService, AppStore, ApplicationService, ApplicationStore, ArticleEntities, ArticleExtracts, ArticleLabels, ArticleSimilarDocuments, AsideFiltersComponent, AuditFeedbackType, AuditService, AuthGuard, AuthPageComponent, AutocompleteService, BOOKMARKS_CONFIG, BOOKMARKS_OPTIONS, BackdropComponent, BackdropService, BookmarkButtonComponent, BookmarksComponent, COLLECTIONS_CONFIG, COLLECTIONS_OPTIONS, COMPONENTS_FOR_DOCUMENT_TYPE, ChangePasswordComponent, ChildMarkerDirective, CollectionsComponent, CollectionsDialog, DRAWER_COMPONENT, DRAWER_STACK_MAX_COUNT, DateComponent, DeleteCollectionDialog, DidYouMeanComponent, DocumentLocatorComponent, DrawerAdvancedFiltersComponent, DrawerComponent, DrawerNavbarComponent, DrawerPreviewComponent, DrawerService, DrawerStackComponent, DrawerStackService, DropdownInputComponent, DropdownListComponent, ErrorComponent, ExportDialog, ExportService, FILTERS_BREAKPOINT, FILTER_DATE_ALLOW_CUSTOM_RANGE, FeedbackDialogComponent, FileSizePipe, FilterButtonComponent, FiltersBarComponent, HIGHLIGHTS, HighlightWordPipe, InfinityScrollDirective, InlineWorker, JsonMethodPluginService, KeyboardNavigatorDirective, LabelService, LabelsEditDialog, LoadingComponent, MetadataComponent, MissingTermsComponent, MoreButtonComponent, MoreComponent, MultiSelectLabelsComponent, MultiSelectionToolbarComponent, NON_SEARCHABLE_COLUMNS, NON_SEARCHABLE_DEFAULTS, NavbarTabsComponent, NavigationService, NoResultComponent, OpenArticleOnCtrlEnterDirective, OperatorPipe, OverflowItemDirective, OverflowManagerDirective, OverflowStopDirective, OverrideUserDialogComponent, PREVIEW_CONFIG, PagerComponent,
|
|
15639
|
+
export { AGGREGATIONS_NAMES, AGGREGATIONS_NAMES_PRESET_DEFAULT, APP_FEATURES, AdvancedFiltersComponent, AdvancedSearch, AdvancedSearchComponent, AggregationComponent, AggregationDateComponent, AggregationListComponent, AggregationTreeComponent, AggregationsService, AggregationsStore, Alert, AlertDialog, AlertsComponent, AppService, AppStore, ApplicationService, ApplicationStore, ArticleEntities, ArticleExtracts, ArticleLabels, ArticleSimilarDocuments, AsideFiltersComponent, AuditFeedbackType, AuditService, AuthGuard, AuthPageComponent, AutocompleteService, BOOKMARKS_CONFIG, BOOKMARKS_OPTIONS, BackdropComponent, BackdropService, BookmarkButtonComponent, BookmarksComponent, COLLECTIONS_CONFIG, COLLECTIONS_OPTIONS, COMPONENTS_FOR_DOCUMENT_TYPE, ChangePasswordComponent, ChildMarkerDirective, CollectionsComponent, CollectionsDialog, DRAWER_COMPONENT, DRAWER_STACK_MAX_COUNT, DateComponent, DeleteCollectionDialog, DidYouMeanComponent, DocumentLocatorComponent, DrawerAdvancedFiltersComponent, DrawerComponent, DrawerNavbarComponent, DrawerPreviewComponent, DrawerService, DrawerStackComponent, DrawerStackService, DropdownInputComponent, DropdownListComponent, ErrorComponent, ExportDialog, ExportService, FILTERS_BREAKPOINT, FILTER_DATE_ALLOW_CUSTOM_RANGE, FeedbackDialogComponent, FileSizePipe, FilterButtonComponent, FiltersBarComponent, HIGHLIGHTS, HighlightWordPipe, InfinityScrollDirective, InlineWorker, JsonMethodPluginService, KeyboardNavigatorDirective, LabelService, LabelsEditDialog, LoadingComponent, MetadataComponent, MissingTermsComponent, MoreButtonComponent, MoreComponent, MultiSelectLabelsComponent, MultiSelectionToolbarComponent, NON_SEARCHABLE_COLUMNS, NON_SEARCHABLE_DEFAULTS, NavbarTabsComponent, NavigationService, NoResultComponent, OpenArticleOnCtrlEnterDirective, OperatorPipe, OverflowItemDirective, OverflowManagerDirective, OverflowStopDirective, OverrideUserDialogComponent, PREVIEW_CONFIG, PagerComponent, PreviewNavigator, PreviewService, PrincipalService, PrincipalStore, QueryParamsStore, QueryService, RECENT_SEARCHES_CONFIG, RECENT_SEARCHES_OPTIONS, ROUTE_COMPONENTS, RecentSearchesComponent, ResetUserSettingsDialogComponent, SAVED_SEARCHES_CONFIG, SAVED_SEARCHES_OPTIONS, SavedSearchDialog, SavedSearchesComponent, SavedSearchesService, SearchFeedbackComponent, SearchInputFooter, SearchService, SelectArticleDirective, SelectArticleOnClickDirective, SelectionHistoryService, SelectionService, SelectionStore, ShowBookmarkDirective, SignInComponent, SortSelectorComponent, SourceComponent, SourceIconPipe, SponsoredResultsComponent, SyslangPipe, THEMES, TextChunkService, ThemeProviderDirective, ThemeSelectorComponent, ThemeStore, ThemeToggleComponent, TranslocoDateImpurePipe, UserProfileDialog, UserProfileFormComponent, UserProfileService, UserSettingsStore, applyThemeToNativeElement, auditInterceptorFn, authInterceptorFn, bodyInterceptorFn, buildQuery, debouncedSignal, errorInterceptorFn, getCurrentPath, getCurrentQueryName, getQueryNameFromRoute, processCssVars, queryNameResolver, signIn, themeColorNameToCssVariable, themeColorsToCssVariables, toastInterceptorFn, withAggregationsFeatures, withAlertsFeatures, withAppFeatures, withApplicationFeatures, withAssistantFeatures, withBasketsFeatures, withBookmarkFeatures, withBootstrapApp, withExtractsFeatures, withFetch, withMultiSelectionFeatures, withPrincipalFeatures, withQueryParamsFeatures, withRecentSearchesFeatures, withSavedSearchesFeatures, withSelectionFeatures, withThemeBodyHook, withThemes, withThemesFeatures, withUserSettingsFeatures };
|
|
14853
15640
|
//# sourceMappingURL=sinequa-atomic-angular.mjs.map
|