@sinequa/atomic-angular 2.10.0 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/features/filters/i18n/de.json +22 -1
- package/features/filters/i18n/en.json +22 -1
- package/features/filters/i18n/fr.json +22 -1
- package/features/quick-filters/i18n/de.json +5 -1
- package/features/quick-filters/i18n/en.json +5 -1
- package/features/quick-filters/i18n/fr.json +5 -1
- package/fesm2022/sinequa-atomic-angular.mjs +2096 -712
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/package.json +1 -1
- package/types/sinequa-atomic-angular-document-workspaces.d.ts +5 -5
- package/types/sinequa-atomic-angular.d.ts +431 -56
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
|
-
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DOCUMENT, DestroyRef, Directive, ElementRef, EnvironmentInjector, HostBinding, HostListener, Inject, Injectable, InjectionToken, Injector, LOCALE_ID, Optional, Pipe, Service, TemplateRef, ViewEncapsulation, afterNextRender, afterRenderEffect, assertInInjectionContext, booleanAttribute, computed, contentChild, contentChildren, effect, inject, input, isDevMode, linkedSignal, makeEnvironmentProviders, model, numberAttribute, output, provideEnvironmentInitializer, resource, runInInjectionContext, signal, untracked, viewChild, viewChildren } from "@angular/core";
|
|
2
|
+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DOCUMENT, DestroyRef, Directive, ElementRef, EnvironmentInjector, HostBinding, HostListener, Inject, Injectable, InjectionToken, Injector, LOCALE_ID, Optional, Pipe, Service, TemplateRef, ViewContainerRef, ViewEncapsulation, afterNextRender, afterRenderEffect, assertInInjectionContext, booleanAttribute, computed, contentChild, contentChildren, effect, inject, input, isDevMode, linkedSignal, makeEnvironmentProviders, model, numberAttribute, output, provideEnvironmentInitializer, resource, runInInjectionContext, signal, untracked, viewChild, viewChildren } from "@angular/core";
|
|
3
3
|
import { BehaviorSubject, EMPTY, Subject, Subscription, catchError, debounceTime, filter, firstValueFrom, from, fromEvent, map, of, scan, shareReplay, switchMap, tap, throwError } from "rxjs";
|
|
4
4
|
import { TranslocoPipe, TranslocoService, provideTranslocoScope } from "@jsverse/transloco";
|
|
5
|
-
import { ArrowDownIcon, ArrowDownZaIcon, ArrowLeftIcon, ArrowRightFromBracketIcon, ArrowRightIcon, ArrowUpAzIcon, ArrowUpIcon, ArrowUpRightFromSquareIcon, ArrowsMaximizeIcon, AutocompleteComponent, AvatarComponent, AvatarFallbackComponent, AvatarImageComponent, BadgeComponent, BarsIcon, BellIcon, BellSlashIcon, BookmarkIcon, BreakpointObserverService, BuildingIcon, ButtonComponent, CalendarDayIcon, CheckIcon, CheckboxComponent, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronsLeftIcon, ChevronsRightIcon, CircleCheckIcon, ClockIcon, CommentIcon, CommentsIcon, DIALOG_REF, DesktopIcon, DialogBodyComponent, DialogComponent, DialogContentComponent, DialogDescriptionComponent, DialogFooterComponent, DialogHeaderComponent, DialogTitleComponent, DropdownDirective, EditIcon, EllipsisIcon, EllipsisVerticalIcon, EyeIcon, EyeSlashIcon, FileHTMLIcon, FileIcon, FileOutputIcon, FileSpreadsheetIcon, FileTextIcon, FlagEnglishIcon, FlagFrenchIcon, FlagGermanIcon, FlashlightIcon, FolderIcon, FolderOpenIcon, FrownIcon, GearIcon, GitForkIcon, GlobeIcon, GripVerticalIcon, HighlighterIcon, HistoryIcon, HomeIcon, ImageIcon, InboxIcon, InfoCircleIcon, InputComponent, InputControlDirective, InputIconRightDirective, KbdComponent, KbdGroupComponent, KeyIcon, KeyboardIcon, LanguageIcon, LightbulbIcon, LightbulbSlashIcon, LinkComponent, ListComponent, ListItemActionComponent, ListItemAvatarDirective, ListItemComponent, ListItemIconDirective, ListItemLeftDirective, ListItemTextComponent, LoadingCircleIcon, LockIcon, MagnifyingGlassIcon, MagnifyingGlassMinusIcon, MagnifyingGlassPlusIcon, MapPinIcon, MenuComponent, MenuContentComponent, MenuItemComponent, MenuTriggerDirective, MoonIcon, PaletteIcon, PaperclipIcon, PenToSquareIcon, PlusIcon, PopoverDirective, PopoverResizeHandleComponent, PresentationIcon, QuestionCircleIcon, QuickFilterIcon, RadioComponent, RobotIcon, SaveIcon, SearchBarComponent, SearchBarSuggestionsComponent, SearchIcon, SearchInputComponent, SelectComponent, SelectTriggerDirective, SendIcon, SheetBodyComponent, SheetComponent, SheetHeaderComponent, SidebarInsetDirective, SlashIcon, SlidersIcon, SortableDirective, SortableItemDirective, SparklesIcon, SpinnerIcon, SquareCheckIcon, SquareIcon, SquareMinusIcon, StarIcon, SunBrightIcon, SwitchComponent, SyncIcon, TabComponent, TabListComponent, TabPanelComponent, TabsComponent, TagComponent, TagIcon, TagInputComponent, TagsIcon, TextareaComponent, TextareaControlDirective, ThumbsDownIcon, ThumbsUpIcon, ToggleComponent, ToggleGroupComponent, ToggleItemComponent, ToggleLeftIcon, ToggleRightIcon, TooltipDirective, TrashCanIcon, TrashIcon, TreeComponent, TreeItemComponent, TreeItemExpanderComponent, TreeItemGroupComponent, TriangleAlertIcon, TruncatedDirective, UndoIcon, UserIcon, UserSecretIcon, VideoIcon, XMarkIcon, createCallable, injectCallRef, injectFloatingPanel, isApplePlatform } from "@sinequa/galactik";
|
|
5
|
+
import { ArrowDownIcon, ArrowDownZaIcon, ArrowLeftIcon, ArrowRightFromBracketIcon, ArrowRightIcon, ArrowUpAzIcon, ArrowUpIcon, ArrowUpRightFromSquareIcon, ArrowsMaximizeIcon, AutocompleteComponent, AvatarComponent, AvatarFallbackComponent, AvatarImageComponent, BadgeComponent, BarsIcon, BellIcon, BellSlashIcon, BookmarkIcon, BreakpointObserverService, BuildingIcon, ButtonComponent, CalendarDayIcon, CheckIcon, CheckboxComponent, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronsLeftIcon, ChevronsRightIcon, CircleCheckIcon, ClockIcon, CommentIcon, CommentsIcon, DIALOG_REF, DesktopIcon, DialogBodyComponent, DialogComponent, DialogContentComponent, DialogDescriptionComponent, DialogFooterComponent, DialogHeaderComponent, DialogTitleComponent, DropdownDirective, EditIcon, EllipsisIcon, EllipsisVerticalIcon, EyeIcon, EyeSlashIcon, FileHTMLIcon, FileIcon, FileOutputIcon, FileSpreadsheetIcon, FileTextIcon, FilterIcon, FlagEnglishIcon, FlagFrenchIcon, FlagGermanIcon, FlashlightIcon, FolderIcon, FolderOpenIcon, FrownIcon, GearIcon, GitForkIcon, GlobeIcon, GripVerticalIcon, HighlighterIcon, HistoryIcon, HomeIcon, ImageIcon, InboxIcon, InfoCircleIcon, InputComponent, InputControlDirective, InputIconRightDirective, KbdComponent, KbdGroupComponent, KeyIcon, KeyboardIcon, LanguageIcon, LightbulbIcon, LightbulbSlashIcon, LinkComponent, ListComponent, ListItemActionComponent, ListItemAvatarDirective, ListItemComponent, ListItemIconDirective, ListItemLeftDirective, ListItemTextComponent, LoadingCircleIcon, LockIcon, MagnifyingGlassIcon, MagnifyingGlassMinusIcon, MagnifyingGlassPlusIcon, MapPinIcon, MenuComponent, MenuContentComponent, MenuItemComponent, MenuTriggerDirective, MoonIcon, PaletteIcon, PaperclipIcon, PenToSquareIcon, PlusIcon, PopoverDirective, PopoverResizeHandleComponent, PresentationIcon, QuestionCircleIcon, QuickFilterIcon, RadioComponent, RobotIcon, SaveIcon, SearchBarComponent, SearchBarSuggestionsComponent, SearchIcon, SearchInputComponent, SelectComponent, SelectTriggerDirective, SendIcon, SheetBodyComponent, SheetComponent, SheetHeaderComponent, SidebarInsetDirective, SlashIcon, SlidersIcon, SortableDirective, SortableItemDirective, SparklesIcon, SpinnerIcon, SquareCheckIcon, SquareIcon, SquareMinusIcon, StarIcon, SunBrightIcon, SwitchComponent, SyncIcon, TabComponent, TabListComponent, TabPanelComponent, TabsComponent, TagComponent, TagIcon, TagInputComponent, TagsIcon, TextareaComponent, TextareaControlDirective, ThumbsDownIcon, ThumbsUpIcon, ToggleComponent, ToggleGroupComponent, ToggleItemComponent, ToggleLeftIcon, ToggleRightIcon, TooltipDirective, TrashCanIcon, TrashIcon, TreeComponent, TreeItemComponent, TreeItemExpanderComponent, TreeItemGroupComponent, TriangleAlertIcon, TruncatedDirective, UndoIcon, UserIcon, UserSecretIcon, VideoIcon, XMarkIcon, createCallable, injectCallRef, injectFloatingPanel, isApplePlatform } from "@sinequa/galactik";
|
|
6
6
|
import { withDevtools } from "@ngrx-toolkit/core";
|
|
7
7
|
import { getState, patchState, signalStore, signalStoreFeature, type, withComputed, withMethods, withState } from "@ngrx/signals";
|
|
8
8
|
import { ActivatedRoute, NavigationEnd, Router, RouterLink, RouterModule } from "@angular/router";
|
|
@@ -5521,6 +5521,39 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
5521
5521
|
args: [{ name: "fileSize" }]
|
|
5522
5522
|
}]
|
|
5523
5523
|
});
|
|
5524
|
+
var LocalizedNumberPipe = class LocalizedNumberPipe {
|
|
5525
|
+
transloco = inject(TranslocoService);
|
|
5526
|
+
lang = toSignal(this.transloco.langChanges$, { initialValue: this.transloco.getActiveLang() });
|
|
5527
|
+
transform(value) {
|
|
5528
|
+
return new Intl.NumberFormat(this.lang()).format(value ?? 0);
|
|
5529
|
+
}
|
|
5530
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
5531
|
+
minVersion: "12.0.0",
|
|
5532
|
+
version: "22.2.1",
|
|
5533
|
+
ngImport: i0,
|
|
5534
|
+
type: LocalizedNumberPipe,
|
|
5535
|
+
deps: [],
|
|
5536
|
+
target: i0.ɵɵFactoryTarget.Pipe
|
|
5537
|
+
});
|
|
5538
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({
|
|
5539
|
+
minVersion: "14.0.0",
|
|
5540
|
+
version: "22.2.1",
|
|
5541
|
+
ngImport: i0,
|
|
5542
|
+
type: LocalizedNumberPipe,
|
|
5543
|
+
isStandalone: true,
|
|
5544
|
+
name: "localizedNumber"
|
|
5545
|
+
});
|
|
5546
|
+
};
|
|
5547
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
5548
|
+
minVersion: "12.0.0",
|
|
5549
|
+
version: "22.2.1",
|
|
5550
|
+
ngImport: i0,
|
|
5551
|
+
type: LocalizedNumberPipe,
|
|
5552
|
+
decorators: [{
|
|
5553
|
+
type: Pipe,
|
|
5554
|
+
args: [{ name: "localizedNumber" }]
|
|
5555
|
+
}]
|
|
5556
|
+
});
|
|
5524
5557
|
var MetadataComponent = class MetadataComponent {
|
|
5525
5558
|
click = output();
|
|
5526
5559
|
class = input(...ngDevMode ? [void 0, { debugName: "class" }] : /* istanbul ignore next */ []);
|
|
@@ -8218,6 +8251,8 @@ var NavbarTabsComponent = class NavbarTabsComponent {
|
|
|
8218
8251
|
appStore = inject(AppStore);
|
|
8219
8252
|
class = input(...ngDevMode ? [void 0, { debugName: "class" }] : /* istanbul ignore next */ []);
|
|
8220
8253
|
ariaLabel = input(...ngDevMode ? [void 0, { debugName: "ariaLabel" }] : /* istanbul ignore next */ []);
|
|
8254
|
+
variant = input("primary", ...ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []);
|
|
8255
|
+
isPill = computed(() => this.variant() === "primary", ...ngDevMode ? [{ debugName: "isPill" }] : /* istanbul ignore next */ []);
|
|
8221
8256
|
showCount = input(true, {
|
|
8222
8257
|
...ngDevMode ? { debugName: "showCount" } : /* istanbul ignore next */ {},
|
|
8223
8258
|
transform: booleanAttribute
|
|
@@ -8325,6 +8360,13 @@ var NavbarTabsComponent = class NavbarTabsComponent {
|
|
|
8325
8360
|
isRequired: false,
|
|
8326
8361
|
transformFunction: null
|
|
8327
8362
|
},
|
|
8363
|
+
variant: {
|
|
8364
|
+
classPropertyName: "variant",
|
|
8365
|
+
publicName: "variant",
|
|
8366
|
+
isSignal: true,
|
|
8367
|
+
isRequired: false,
|
|
8368
|
+
transformFunction: null
|
|
8369
|
+
},
|
|
8328
8370
|
showCount: {
|
|
8329
8371
|
classPropertyName: "showCount",
|
|
8330
8372
|
publicName: "showCount",
|
|
@@ -8382,10 +8424,11 @@ var NavbarTabsComponent = class NavbarTabsComponent {
|
|
|
8382
8424
|
WHICH list it landed in — and a page may hold more than one. The default is translated
|
|
8383
8425
|
so every consumer gets a named tab list without passing anything.
|
|
8384
8426
|
(No backticks in this comment: the template is a template literal.) -->
|
|
8385
|
-
<TabList class="w-full" [attr.aria-label]="ariaLabel() ?? ('navbarTabs.tabs' | transloco)" [(selectedTab)]="selectedTab" (selectedTabChange)="onSelectTab($event)" selectionMode="explicit" [softDisabled]="false">
|
|
8427
|
+
<TabList class="w-full" [variant]="variant()" [attr.aria-label]="ariaLabel() ?? ('navbarTabs.tabs' | transloco)" [(selectedTab)]="selectedTab" (selectedTabChange)="onSelectTab($event)" selectionMode="explicit" [softDisabled]="false">
|
|
8386
8428
|
@for (tab of nav.tabs(); track $index) {
|
|
8387
8429
|
<Tab
|
|
8388
|
-
[
|
|
8430
|
+
[variant]="variant()"
|
|
8431
|
+
[class]="cn('group', !noTruncate() && 'basis-50', !isPill() && 'h-11 px-(--spacing-xs) text-(length:--size-base) font-semibold')"
|
|
8389
8432
|
[value]="tab.path"
|
|
8390
8433
|
overflowItem
|
|
8391
8434
|
[noTruncate]="noTruncate()"
|
|
@@ -8416,315 +8459,11 @@ var NavbarTabsComponent = class NavbarTabsComponent {
|
|
|
8416
8459
|
</div>
|
|
8417
8460
|
</Tab>
|
|
8418
8461
|
}
|
|
8419
|
-
<!--
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
— so the list keeps ONE tab stop and the arrows reach it like any other tab.
|
|
8423
|
-
|
|
8424
|
-
It stays a real <button> because that is what menuTrigger needs: it wires the menu
|
|
8425
|
-
through the native popovertarget attribute, which only works on a button.
|
|
8426
|
-
|
|
8427
|
-
ms-auto pins it to the right end of the flex tab list. overflowStop is found by content
|
|
8428
|
-
query, so the measurement is unaffected by where it sits. Always mounted so the stop
|
|
8429
|
-
position is stable; hidden when every tab fits.
|
|
8430
|
-
(No backticks in this comment: the template is a template literal.) -->
|
|
8431
|
-
<button
|
|
8432
|
-
ngTab
|
|
8433
|
-
[value]="MORE_TABS_VALUE"
|
|
8434
|
-
[menuTrigger]="moreTabsMenu"
|
|
8435
|
-
variant="tertiary" [iconOnly]="true"
|
|
8436
|
-
[attr.aria-label]="'navbarTabs.moreTabs' | transloco" [tooltip]="'navbarTabs.moreTabs' | transloco" overflowStop
|
|
8437
|
-
[disabled]="moreTabs().length === 0"
|
|
8438
|
-
class="ms-auto shrink-0"
|
|
8439
|
-
[class.invisible]="moreTabs().length === 0">
|
|
8440
|
-
<EllipsisIcon orientation="vertical"></EllipsisIcon>
|
|
8441
|
-
<span class="sr-only">{{ "navbarTabs.moreTabs" | transloco }}</span>
|
|
8442
|
-
</button>
|
|
8443
|
-
</TabList>
|
|
8444
|
-
</Tabs>
|
|
8462
|
+
<!-- Pill strip: ms-auto pins the trigger to the right end of the strip. Underline variants: no pin, the trigger
|
|
8463
|
+
follows the last visible tab, which is where the eye expects the rest of the list to be. The overflow
|
|
8464
|
+
manager reads only its size, never its position, so both placements measure the same.
|
|
8445
8465
|
|
|
8446
|
-
|
|
8447
|
-
<MenuContent class="z-500 w-fit">
|
|
8448
|
-
@for (tab of moreTabs(); track $index) {
|
|
8449
|
-
<MenuItem [value]="tab" [disabled]="isTabDisabled(tab)">
|
|
8450
|
-
<a class="inline-flex items-center gap-1 whitespace-nowrap first-letter:capitalize"
|
|
8451
|
-
[routerLink]="isTabDisabled(tab) ? null : [tab.routerLink]"
|
|
8452
|
-
[queryParams]="getQueryParams(tab)"
|
|
8453
|
-
[queryParamsHandling]="getQueryParamsHandling()"
|
|
8454
|
-
[attr.aria-selected]="nav.currentPath() === tab.path"
|
|
8455
|
-
[attr.aria-label]="tab.display | syslang | transloco"
|
|
8456
|
-
[attr.aria-disabled]="isTabDisabled(tab) ? true : null"
|
|
8457
|
-
[attr.tabindex]="isTabDisabled(tab) ? -1 : null"
|
|
8458
|
-
(click)="onOverflowItemClick($event, tab)"
|
|
8459
|
-
>
|
|
8460
|
-
@if (tab.icon) {
|
|
8461
|
-
<FaIcon [faClass]="tab.icon"/>
|
|
8462
|
-
}
|
|
8463
|
-
{{ tab.display | syslang | transloco }}
|
|
8464
|
-
</a>
|
|
8465
|
-
</MenuItem>
|
|
8466
|
-
}
|
|
8467
|
-
</MenuContent>
|
|
8468
|
-
</Menu>
|
|
8469
|
-
</div>
|
|
8470
|
-
}
|
|
8471
|
-
`,
|
|
8472
|
-
isInline: true,
|
|
8473
|
-
dependencies: [
|
|
8474
|
-
{
|
|
8475
|
-
kind: "directive",
|
|
8476
|
-
type: RouterLink,
|
|
8477
|
-
selector: "[routerLink]",
|
|
8478
|
-
inputs: [
|
|
8479
|
-
"target",
|
|
8480
|
-
"queryParams",
|
|
8481
|
-
"fragment",
|
|
8482
|
-
"queryParamsHandling",
|
|
8483
|
-
"state",
|
|
8484
|
-
"info",
|
|
8485
|
-
"relativeTo",
|
|
8486
|
-
"preserveFragment",
|
|
8487
|
-
"skipLocationChange",
|
|
8488
|
-
"replaceUrl",
|
|
8489
|
-
"browserUrl",
|
|
8490
|
-
"routerLink"
|
|
8491
|
-
]
|
|
8492
|
-
},
|
|
8493
|
-
{
|
|
8494
|
-
kind: "directive",
|
|
8495
|
-
type: ButtonComponent,
|
|
8496
|
-
selector: "button",
|
|
8497
|
-
inputs: [
|
|
8498
|
-
"class",
|
|
8499
|
-
"variant",
|
|
8500
|
-
"scheme",
|
|
8501
|
-
"iconOnly",
|
|
8502
|
-
"size"
|
|
8503
|
-
]
|
|
8504
|
-
},
|
|
8505
|
-
{
|
|
8506
|
-
kind: "directive",
|
|
8507
|
-
type: TabsComponent,
|
|
8508
|
-
selector: "Tabs",
|
|
8509
|
-
inputs: ["class"]
|
|
8510
|
-
},
|
|
8511
|
-
{
|
|
8512
|
-
kind: "directive",
|
|
8513
|
-
type: TabListComponent,
|
|
8514
|
-
selector: "TabList",
|
|
8515
|
-
inputs: ["class", "variant"]
|
|
8516
|
-
},
|
|
8517
|
-
{
|
|
8518
|
-
kind: "directive",
|
|
8519
|
-
type: TabComponent,
|
|
8520
|
-
selector: "Tab",
|
|
8521
|
-
inputs: [
|
|
8522
|
-
"class",
|
|
8523
|
-
"variant",
|
|
8524
|
-
"size",
|
|
8525
|
-
"noTruncate"
|
|
8526
|
-
]
|
|
8527
|
-
},
|
|
8528
|
-
{
|
|
8529
|
-
kind: "directive",
|
|
8530
|
-
type: Tab,
|
|
8531
|
-
selector: "[ngTab]",
|
|
8532
|
-
inputs: [
|
|
8533
|
-
"id",
|
|
8534
|
-
"disabled",
|
|
8535
|
-
"value"
|
|
8536
|
-
],
|
|
8537
|
-
exportAs: ["ngTab"]
|
|
8538
|
-
},
|
|
8539
|
-
{
|
|
8540
|
-
kind: "directive",
|
|
8541
|
-
type: OverflowManagerDirective,
|
|
8542
|
-
selector: "[overflowManager]",
|
|
8543
|
-
inputs: [
|
|
8544
|
-
"target",
|
|
8545
|
-
"margin",
|
|
8546
|
-
"direction",
|
|
8547
|
-
"reserveStop"
|
|
8548
|
-
],
|
|
8549
|
-
outputs: ["count"]
|
|
8550
|
-
},
|
|
8551
|
-
{
|
|
8552
|
-
kind: "directive",
|
|
8553
|
-
type: OverflowItemDirective,
|
|
8554
|
-
selector: "[overflowItem]"
|
|
8555
|
-
},
|
|
8556
|
-
{
|
|
8557
|
-
kind: "directive",
|
|
8558
|
-
type: OverflowStopDirective,
|
|
8559
|
-
selector: "[overflowStop]"
|
|
8560
|
-
},
|
|
8561
|
-
{
|
|
8562
|
-
kind: "directive",
|
|
8563
|
-
type: BadgeComponent,
|
|
8564
|
-
selector: "badge, Badge",
|
|
8565
|
-
inputs: [
|
|
8566
|
-
"class",
|
|
8567
|
-
"variant",
|
|
8568
|
-
"fill",
|
|
8569
|
-
"scheme",
|
|
8570
|
-
"size",
|
|
8571
|
-
"aria-label"
|
|
8572
|
-
]
|
|
8573
|
-
},
|
|
8574
|
-
{
|
|
8575
|
-
kind: "component",
|
|
8576
|
-
type: EllipsisIcon,
|
|
8577
|
-
selector: "ellipsis-icon, EllipsisIcon, ellipsisicon",
|
|
8578
|
-
inputs: ["class", "orientation"]
|
|
8579
|
-
},
|
|
8580
|
-
{
|
|
8581
|
-
kind: "directive",
|
|
8582
|
-
type: TooltipDirective,
|
|
8583
|
-
selector: "[tooltip]",
|
|
8584
|
-
inputs: [
|
|
8585
|
-
"tooltip",
|
|
8586
|
-
"tooltipPosition",
|
|
8587
|
-
"tooltipEvent",
|
|
8588
|
-
"showDelay",
|
|
8589
|
-
"hideDelay",
|
|
8590
|
-
"life",
|
|
8591
|
-
"tooltipOffset",
|
|
8592
|
-
"autoHide",
|
|
8593
|
-
"hideOnEscape",
|
|
8594
|
-
"escape",
|
|
8595
|
-
"tooltipDisabled",
|
|
8596
|
-
"tooltipArrow",
|
|
8597
|
-
"tooltipStyleClass",
|
|
8598
|
-
"tooltipOptions",
|
|
8599
|
-
"tooltip-position",
|
|
8600
|
-
"tooltip-delay",
|
|
8601
|
-
"tooltip-duration",
|
|
8602
|
-
"tooltip-offset"
|
|
8603
|
-
],
|
|
8604
|
-
exportAs: ["tooltip"]
|
|
8605
|
-
},
|
|
8606
|
-
{
|
|
8607
|
-
kind: "component",
|
|
8608
|
-
type: FaIconComponent,
|
|
8609
|
-
selector: "fa-icon, FaIcon",
|
|
8610
|
-
inputs: ["faClass", "class"]
|
|
8611
|
-
},
|
|
8612
|
-
{
|
|
8613
|
-
kind: "directive",
|
|
8614
|
-
type: MenuTriggerDirective,
|
|
8615
|
-
selector: "[menuTrigger]",
|
|
8616
|
-
inputs: ["menuTrigger"],
|
|
8617
|
-
exportAs: ["menuTrigger"]
|
|
8618
|
-
},
|
|
8619
|
-
{
|
|
8620
|
-
kind: "directive",
|
|
8621
|
-
type: MenuComponent,
|
|
8622
|
-
selector: "Menu,menu",
|
|
8623
|
-
inputs: ["class"],
|
|
8624
|
-
exportAs: ["menu"]
|
|
8625
|
-
},
|
|
8626
|
-
{
|
|
8627
|
-
kind: "component",
|
|
8628
|
-
type: MenuContentComponent,
|
|
8629
|
-
selector: "menu-content,MenuContent,menucontent",
|
|
8630
|
-
inputs: ["class"]
|
|
8631
|
-
},
|
|
8632
|
-
{
|
|
8633
|
-
kind: "directive",
|
|
8634
|
-
type: MenuItemComponent,
|
|
8635
|
-
selector: "menu-item,MenuItem,menuitem",
|
|
8636
|
-
inputs: ["class", "checked"],
|
|
8637
|
-
exportAs: ["menuitem"]
|
|
8638
|
-
},
|
|
8639
|
-
{
|
|
8640
|
-
kind: "pipe",
|
|
8641
|
-
type: TranslocoPipe,
|
|
8642
|
-
name: "transloco"
|
|
8643
|
-
},
|
|
8644
|
-
{
|
|
8645
|
-
kind: "pipe",
|
|
8646
|
-
type: SyslangPipe,
|
|
8647
|
-
name: "syslang"
|
|
8648
|
-
}
|
|
8649
|
-
]
|
|
8650
|
-
});
|
|
8651
|
-
};
|
|
8652
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
8653
|
-
minVersion: "12.0.0",
|
|
8654
|
-
version: "22.2.1",
|
|
8655
|
-
ngImport: i0,
|
|
8656
|
-
type: NavbarTabsComponent,
|
|
8657
|
-
decorators: [{
|
|
8658
|
-
type: Component,
|
|
8659
|
-
args: [{
|
|
8660
|
-
selector: "navbar-tabs",
|
|
8661
|
-
imports: [
|
|
8662
|
-
RouterLink,
|
|
8663
|
-
TranslocoPipe,
|
|
8664
|
-
SyslangPipe,
|
|
8665
|
-
ButtonComponent,
|
|
8666
|
-
TabsComponent,
|
|
8667
|
-
TabListComponent,
|
|
8668
|
-
TabComponent,
|
|
8669
|
-
Tab,
|
|
8670
|
-
OverflowManagerDirective,
|
|
8671
|
-
OverflowItemDirective,
|
|
8672
|
-
OverflowStopDirective,
|
|
8673
|
-
BadgeComponent,
|
|
8674
|
-
EllipsisIcon,
|
|
8675
|
-
TooltipDirective,
|
|
8676
|
-
FaIconComponent,
|
|
8677
|
-
MenuTriggerDirective,
|
|
8678
|
-
MenuComponent,
|
|
8679
|
-
MenuContentComponent,
|
|
8680
|
-
MenuItemComponent
|
|
8681
|
-
],
|
|
8682
|
-
providers: [provideTranslocoScope("navbar-tabs")],
|
|
8683
|
-
template: `
|
|
8684
|
-
<!-- do not display the tabs if there are no tabs -->
|
|
8685
|
-
@if (nav.tabs().length > 0) {
|
|
8686
|
-
<div overflowManager (count)="visibleTabCount.set($event)" class="flex items-end rounded-[inherit] bg-inherit">
|
|
8687
|
-
<Tabs class="flex-1 min-w-0 overflow-hidden">
|
|
8688
|
-
<!-- The tab list needs a name of its own: the tablist role announces itself and its item
|
|
8689
|
-
count, but without an accessible name a screen reader has nothing to tell the user
|
|
8690
|
-
WHICH list it landed in — and a page may hold more than one. The default is translated
|
|
8691
|
-
so every consumer gets a named tab list without passing anything.
|
|
8692
|
-
(No backticks in this comment: the template is a template literal.) -->
|
|
8693
|
-
<TabList class="w-full" [attr.aria-label]="ariaLabel() ?? ('navbarTabs.tabs' | transloco)" [(selectedTab)]="selectedTab" (selectedTabChange)="onSelectTab($event)" selectionMode="explicit" [softDisabled]="false">
|
|
8694
|
-
@for (tab of nav.tabs(); track $index) {
|
|
8695
|
-
<Tab
|
|
8696
|
-
[class]="cn('group', !noTruncate() && 'basis-50')"
|
|
8697
|
-
[value]="tab.path"
|
|
8698
|
-
overflowItem
|
|
8699
|
-
[noTruncate]="noTruncate()"
|
|
8700
|
-
[style.--tab-min-width]="tabMinWidth(!!tab.icon)"
|
|
8701
|
-
[disabled]="isTabDisabled(tab) || !isTabVisible($index)"
|
|
8702
|
-
>
|
|
8703
|
-
<div [class]="cn('flex items-center content-start w-full gap-1', !noTruncate() && '@container overflow-hidden min-w-0')">
|
|
8704
|
-
@if (tab.icon) {
|
|
8705
|
-
<!-- the icon never shrinks: the label truncates first -->
|
|
8706
|
-
<FaIcon [faClass]="tab.icon" class="shrink-0" aria-hidden="true" />
|
|
8707
|
-
}
|
|
8708
|
-
<!-- when the tab gets too narrow to show anything meaningful next to
|
|
8709
|
-
the icon, hide the label entirely instead of a clipped sliver.
|
|
8710
|
-
w-0 + invisible (not display: none) keeps the label's line box,
|
|
8711
|
-
so the tab height doesn't collapse to the icon's height -->
|
|
8712
|
-
<span [class]="cn(!noTruncate() && 'truncate', !noTruncate() && tab.icon && '@max-[3rem]:w-0 @max-[3rem]:invisible')">{{ tab.display | syslang | transloco }}</span>
|
|
8713
|
-
<!-- Show count badge only if count is > 0 -->
|
|
8714
|
-
@if((tab.count ?? 0) > 0) {
|
|
8715
|
-
<Badge
|
|
8716
|
-
variant="number"
|
|
8717
|
-
fill="secondary"
|
|
8718
|
-
scheme="sage"
|
|
8719
|
-
size="xs"
|
|
8720
|
-
[aria-label]="tab.count + ' ' + (tab.count === 1 ? ('navbarTabs.result' | transloco) : ('navbarTabs.results' | transloco))">
|
|
8721
|
-
{{ tab.count }}
|
|
8722
|
-
</Badge>
|
|
8723
|
-
}
|
|
8724
|
-
</div>
|
|
8725
|
-
</Tab>
|
|
8726
|
-
}
|
|
8727
|
-
<!-- The overflow trigger belongs INSIDE the tab list, and it is a tab: a tablist may only
|
|
8466
|
+
The overflow trigger belongs INSIDE the tab list, and it is a tab: a tablist may only
|
|
8728
8467
|
contain tab elements, so a plain button here would break the announced tab count and
|
|
8729
8468
|
the arrow-key navigation. ngTab gives it the role and enrols it in the roving tabindex
|
|
8730
8469
|
— so the list keeps ONE tab stop and the arrows reach it like any other tab.
|
|
@@ -8743,9 +8482,322 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
8743
8482
|
variant="tertiary" [iconOnly]="true"
|
|
8744
8483
|
[attr.aria-label]="'navbarTabs.moreTabs' | transloco" [tooltip]="'navbarTabs.moreTabs' | transloco" overflowStop
|
|
8745
8484
|
[disabled]="moreTabs().length === 0"
|
|
8746
|
-
class="ms-auto
|
|
8485
|
+
[class]="cn('shrink-0', isPill() ? 'ms-auto' : 'self-center')"
|
|
8747
8486
|
[class.invisible]="moreTabs().length === 0">
|
|
8748
|
-
<EllipsisIcon orientation="vertical"></EllipsisIcon>
|
|
8487
|
+
<EllipsisIcon [orientation]="isPill() ? 'vertical' : 'horizontal'"></EllipsisIcon>
|
|
8488
|
+
<span class="sr-only">{{ "navbarTabs.moreTabs" | transloco }}</span>
|
|
8489
|
+
</button>
|
|
8490
|
+
</TabList>
|
|
8491
|
+
</Tabs>
|
|
8492
|
+
|
|
8493
|
+
<Menu #moreTabsMenu="menu" placement="bottom-end">
|
|
8494
|
+
<MenuContent class="z-500 w-fit">
|
|
8495
|
+
@for (tab of moreTabs(); track $index) {
|
|
8496
|
+
<MenuItem [value]="tab" [disabled]="isTabDisabled(tab)">
|
|
8497
|
+
<a class="inline-flex items-center gap-1 whitespace-nowrap first-letter:capitalize"
|
|
8498
|
+
[routerLink]="isTabDisabled(tab) ? null : [tab.routerLink]"
|
|
8499
|
+
[queryParams]="getQueryParams(tab)"
|
|
8500
|
+
[queryParamsHandling]="getQueryParamsHandling()"
|
|
8501
|
+
[attr.aria-selected]="nav.currentPath() === tab.path"
|
|
8502
|
+
[attr.aria-label]="tab.display | syslang | transloco"
|
|
8503
|
+
[attr.aria-disabled]="isTabDisabled(tab) ? true : null"
|
|
8504
|
+
[attr.tabindex]="isTabDisabled(tab) ? -1 : null"
|
|
8505
|
+
(click)="onOverflowItemClick($event, tab)"
|
|
8506
|
+
>
|
|
8507
|
+
@if (tab.icon) {
|
|
8508
|
+
<FaIcon [faClass]="tab.icon"/>
|
|
8509
|
+
}
|
|
8510
|
+
{{ tab.display | syslang | transloco }}
|
|
8511
|
+
</a>
|
|
8512
|
+
</MenuItem>
|
|
8513
|
+
}
|
|
8514
|
+
</MenuContent>
|
|
8515
|
+
</Menu>
|
|
8516
|
+
</div>
|
|
8517
|
+
}
|
|
8518
|
+
`,
|
|
8519
|
+
isInline: true,
|
|
8520
|
+
dependencies: [
|
|
8521
|
+
{
|
|
8522
|
+
kind: "directive",
|
|
8523
|
+
type: RouterLink,
|
|
8524
|
+
selector: "[routerLink]",
|
|
8525
|
+
inputs: [
|
|
8526
|
+
"target",
|
|
8527
|
+
"queryParams",
|
|
8528
|
+
"fragment",
|
|
8529
|
+
"queryParamsHandling",
|
|
8530
|
+
"state",
|
|
8531
|
+
"info",
|
|
8532
|
+
"relativeTo",
|
|
8533
|
+
"preserveFragment",
|
|
8534
|
+
"skipLocationChange",
|
|
8535
|
+
"replaceUrl",
|
|
8536
|
+
"browserUrl",
|
|
8537
|
+
"routerLink"
|
|
8538
|
+
]
|
|
8539
|
+
},
|
|
8540
|
+
{
|
|
8541
|
+
kind: "directive",
|
|
8542
|
+
type: ButtonComponent,
|
|
8543
|
+
selector: "button",
|
|
8544
|
+
inputs: [
|
|
8545
|
+
"class",
|
|
8546
|
+
"variant",
|
|
8547
|
+
"scheme",
|
|
8548
|
+
"iconOnly",
|
|
8549
|
+
"size"
|
|
8550
|
+
]
|
|
8551
|
+
},
|
|
8552
|
+
{
|
|
8553
|
+
kind: "directive",
|
|
8554
|
+
type: TabsComponent,
|
|
8555
|
+
selector: "Tabs",
|
|
8556
|
+
inputs: ["class"]
|
|
8557
|
+
},
|
|
8558
|
+
{
|
|
8559
|
+
kind: "directive",
|
|
8560
|
+
type: TabListComponent,
|
|
8561
|
+
selector: "TabList",
|
|
8562
|
+
inputs: ["class", "variant"]
|
|
8563
|
+
},
|
|
8564
|
+
{
|
|
8565
|
+
kind: "directive",
|
|
8566
|
+
type: TabComponent,
|
|
8567
|
+
selector: "Tab",
|
|
8568
|
+
inputs: [
|
|
8569
|
+
"class",
|
|
8570
|
+
"variant",
|
|
8571
|
+
"size",
|
|
8572
|
+
"noTruncate"
|
|
8573
|
+
]
|
|
8574
|
+
},
|
|
8575
|
+
{
|
|
8576
|
+
kind: "directive",
|
|
8577
|
+
type: Tab,
|
|
8578
|
+
selector: "[ngTab]",
|
|
8579
|
+
inputs: [
|
|
8580
|
+
"id",
|
|
8581
|
+
"disabled",
|
|
8582
|
+
"value"
|
|
8583
|
+
],
|
|
8584
|
+
exportAs: ["ngTab"]
|
|
8585
|
+
},
|
|
8586
|
+
{
|
|
8587
|
+
kind: "directive",
|
|
8588
|
+
type: OverflowManagerDirective,
|
|
8589
|
+
selector: "[overflowManager]",
|
|
8590
|
+
inputs: [
|
|
8591
|
+
"target",
|
|
8592
|
+
"margin",
|
|
8593
|
+
"direction",
|
|
8594
|
+
"reserveStop"
|
|
8595
|
+
],
|
|
8596
|
+
outputs: ["count"]
|
|
8597
|
+
},
|
|
8598
|
+
{
|
|
8599
|
+
kind: "directive",
|
|
8600
|
+
type: OverflowItemDirective,
|
|
8601
|
+
selector: "[overflowItem]"
|
|
8602
|
+
},
|
|
8603
|
+
{
|
|
8604
|
+
kind: "directive",
|
|
8605
|
+
type: OverflowStopDirective,
|
|
8606
|
+
selector: "[overflowStop]"
|
|
8607
|
+
},
|
|
8608
|
+
{
|
|
8609
|
+
kind: "directive",
|
|
8610
|
+
type: BadgeComponent,
|
|
8611
|
+
selector: "badge, Badge",
|
|
8612
|
+
inputs: [
|
|
8613
|
+
"class",
|
|
8614
|
+
"variant",
|
|
8615
|
+
"fill",
|
|
8616
|
+
"scheme",
|
|
8617
|
+
"size",
|
|
8618
|
+
"aria-label"
|
|
8619
|
+
]
|
|
8620
|
+
},
|
|
8621
|
+
{
|
|
8622
|
+
kind: "component",
|
|
8623
|
+
type: EllipsisIcon,
|
|
8624
|
+
selector: "ellipsis-icon, EllipsisIcon, ellipsisicon",
|
|
8625
|
+
inputs: ["class", "orientation"]
|
|
8626
|
+
},
|
|
8627
|
+
{
|
|
8628
|
+
kind: "directive",
|
|
8629
|
+
type: TooltipDirective,
|
|
8630
|
+
selector: "[tooltip]",
|
|
8631
|
+
inputs: [
|
|
8632
|
+
"tooltip",
|
|
8633
|
+
"tooltipPosition",
|
|
8634
|
+
"tooltipEvent",
|
|
8635
|
+
"showDelay",
|
|
8636
|
+
"hideDelay",
|
|
8637
|
+
"life",
|
|
8638
|
+
"tooltipOffset",
|
|
8639
|
+
"autoHide",
|
|
8640
|
+
"hideOnEscape",
|
|
8641
|
+
"escape",
|
|
8642
|
+
"tooltipDisabled",
|
|
8643
|
+
"tooltipArrow",
|
|
8644
|
+
"tooltipStyleClass",
|
|
8645
|
+
"tooltipOptions",
|
|
8646
|
+
"tooltip-position",
|
|
8647
|
+
"tooltip-delay",
|
|
8648
|
+
"tooltip-duration",
|
|
8649
|
+
"tooltip-offset"
|
|
8650
|
+
],
|
|
8651
|
+
exportAs: ["tooltip"]
|
|
8652
|
+
},
|
|
8653
|
+
{
|
|
8654
|
+
kind: "component",
|
|
8655
|
+
type: FaIconComponent,
|
|
8656
|
+
selector: "fa-icon, FaIcon",
|
|
8657
|
+
inputs: ["faClass", "class"]
|
|
8658
|
+
},
|
|
8659
|
+
{
|
|
8660
|
+
kind: "directive",
|
|
8661
|
+
type: MenuTriggerDirective,
|
|
8662
|
+
selector: "[menuTrigger]",
|
|
8663
|
+
inputs: ["menuTrigger"],
|
|
8664
|
+
exportAs: ["menuTrigger"]
|
|
8665
|
+
},
|
|
8666
|
+
{
|
|
8667
|
+
kind: "directive",
|
|
8668
|
+
type: MenuComponent,
|
|
8669
|
+
selector: "Menu,menu",
|
|
8670
|
+
inputs: ["class"],
|
|
8671
|
+
exportAs: ["menu"]
|
|
8672
|
+
},
|
|
8673
|
+
{
|
|
8674
|
+
kind: "component",
|
|
8675
|
+
type: MenuContentComponent,
|
|
8676
|
+
selector: "menu-content,MenuContent,menucontent",
|
|
8677
|
+
inputs: ["class"]
|
|
8678
|
+
},
|
|
8679
|
+
{
|
|
8680
|
+
kind: "directive",
|
|
8681
|
+
type: MenuItemComponent,
|
|
8682
|
+
selector: "menu-item,MenuItem,menuitem",
|
|
8683
|
+
inputs: ["class", "checked"],
|
|
8684
|
+
exportAs: ["menuitem"]
|
|
8685
|
+
},
|
|
8686
|
+
{
|
|
8687
|
+
kind: "pipe",
|
|
8688
|
+
type: TranslocoPipe,
|
|
8689
|
+
name: "transloco"
|
|
8690
|
+
},
|
|
8691
|
+
{
|
|
8692
|
+
kind: "pipe",
|
|
8693
|
+
type: SyslangPipe,
|
|
8694
|
+
name: "syslang"
|
|
8695
|
+
}
|
|
8696
|
+
]
|
|
8697
|
+
});
|
|
8698
|
+
};
|
|
8699
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
8700
|
+
minVersion: "12.0.0",
|
|
8701
|
+
version: "22.2.1",
|
|
8702
|
+
ngImport: i0,
|
|
8703
|
+
type: NavbarTabsComponent,
|
|
8704
|
+
decorators: [{
|
|
8705
|
+
type: Component,
|
|
8706
|
+
args: [{
|
|
8707
|
+
selector: "navbar-tabs",
|
|
8708
|
+
imports: [
|
|
8709
|
+
RouterLink,
|
|
8710
|
+
TranslocoPipe,
|
|
8711
|
+
SyslangPipe,
|
|
8712
|
+
ButtonComponent,
|
|
8713
|
+
TabsComponent,
|
|
8714
|
+
TabListComponent,
|
|
8715
|
+
TabComponent,
|
|
8716
|
+
Tab,
|
|
8717
|
+
OverflowManagerDirective,
|
|
8718
|
+
OverflowItemDirective,
|
|
8719
|
+
OverflowStopDirective,
|
|
8720
|
+
BadgeComponent,
|
|
8721
|
+
EllipsisIcon,
|
|
8722
|
+
TooltipDirective,
|
|
8723
|
+
FaIconComponent,
|
|
8724
|
+
MenuTriggerDirective,
|
|
8725
|
+
MenuComponent,
|
|
8726
|
+
MenuContentComponent,
|
|
8727
|
+
MenuItemComponent
|
|
8728
|
+
],
|
|
8729
|
+
providers: [provideTranslocoScope("navbar-tabs")],
|
|
8730
|
+
template: `
|
|
8731
|
+
<!-- do not display the tabs if there are no tabs -->
|
|
8732
|
+
@if (nav.tabs().length > 0) {
|
|
8733
|
+
<div overflowManager (count)="visibleTabCount.set($event)" class="flex items-end rounded-[inherit] bg-inherit">
|
|
8734
|
+
<Tabs class="flex-1 min-w-0 overflow-hidden">
|
|
8735
|
+
<!-- The tab list needs a name of its own: the tablist role announces itself and its item
|
|
8736
|
+
count, but without an accessible name a screen reader has nothing to tell the user
|
|
8737
|
+
WHICH list it landed in — and a page may hold more than one. The default is translated
|
|
8738
|
+
so every consumer gets a named tab list without passing anything.
|
|
8739
|
+
(No backticks in this comment: the template is a template literal.) -->
|
|
8740
|
+
<TabList class="w-full" [variant]="variant()" [attr.aria-label]="ariaLabel() ?? ('navbarTabs.tabs' | transloco)" [(selectedTab)]="selectedTab" (selectedTabChange)="onSelectTab($event)" selectionMode="explicit" [softDisabled]="false">
|
|
8741
|
+
@for (tab of nav.tabs(); track $index) {
|
|
8742
|
+
<Tab
|
|
8743
|
+
[variant]="variant()"
|
|
8744
|
+
[class]="cn('group', !noTruncate() && 'basis-50', !isPill() && 'h-11 px-(--spacing-xs) text-(length:--size-base) font-semibold')"
|
|
8745
|
+
[value]="tab.path"
|
|
8746
|
+
overflowItem
|
|
8747
|
+
[noTruncate]="noTruncate()"
|
|
8748
|
+
[style.--tab-min-width]="tabMinWidth(!!tab.icon)"
|
|
8749
|
+
[disabled]="isTabDisabled(tab) || !isTabVisible($index)"
|
|
8750
|
+
>
|
|
8751
|
+
<div [class]="cn('flex items-center content-start w-full gap-1', !noTruncate() && '@container overflow-hidden min-w-0')">
|
|
8752
|
+
@if (tab.icon) {
|
|
8753
|
+
<!-- the icon never shrinks: the label truncates first -->
|
|
8754
|
+
<FaIcon [faClass]="tab.icon" class="shrink-0" aria-hidden="true" />
|
|
8755
|
+
}
|
|
8756
|
+
<!-- when the tab gets too narrow to show anything meaningful next to
|
|
8757
|
+
the icon, hide the label entirely instead of a clipped sliver.
|
|
8758
|
+
w-0 + invisible (not display: none) keeps the label's line box,
|
|
8759
|
+
so the tab height doesn't collapse to the icon's height -->
|
|
8760
|
+
<span [class]="cn(!noTruncate() && 'truncate', !noTruncate() && tab.icon && '@max-[3rem]:w-0 @max-[3rem]:invisible')">{{ tab.display | syslang | transloco }}</span>
|
|
8761
|
+
<!-- Show count badge only if count is > 0 -->
|
|
8762
|
+
@if((tab.count ?? 0) > 0) {
|
|
8763
|
+
<Badge
|
|
8764
|
+
variant="number"
|
|
8765
|
+
fill="secondary"
|
|
8766
|
+
scheme="sage"
|
|
8767
|
+
size="xs"
|
|
8768
|
+
[aria-label]="tab.count + ' ' + (tab.count === 1 ? ('navbarTabs.result' | transloco) : ('navbarTabs.results' | transloco))">
|
|
8769
|
+
{{ tab.count }}
|
|
8770
|
+
</Badge>
|
|
8771
|
+
}
|
|
8772
|
+
</div>
|
|
8773
|
+
</Tab>
|
|
8774
|
+
}
|
|
8775
|
+
<!-- Pill strip: ms-auto pins the trigger to the right end of the strip. Underline variants: no pin, the trigger
|
|
8776
|
+
follows the last visible tab, which is where the eye expects the rest of the list to be. The overflow
|
|
8777
|
+
manager reads only its size, never its position, so both placements measure the same.
|
|
8778
|
+
|
|
8779
|
+
The overflow trigger belongs INSIDE the tab list, and it is a tab: a tablist may only
|
|
8780
|
+
contain tab elements, so a plain button here would break the announced tab count and
|
|
8781
|
+
the arrow-key navigation. ngTab gives it the role and enrols it in the roving tabindex
|
|
8782
|
+
— so the list keeps ONE tab stop and the arrows reach it like any other tab.
|
|
8783
|
+
|
|
8784
|
+
It stays a real <button> because that is what menuTrigger needs: it wires the menu
|
|
8785
|
+
through the native popovertarget attribute, which only works on a button.
|
|
8786
|
+
|
|
8787
|
+
ms-auto pins it to the right end of the flex tab list. overflowStop is found by content
|
|
8788
|
+
query, so the measurement is unaffected by where it sits. Always mounted so the stop
|
|
8789
|
+
position is stable; hidden when every tab fits.
|
|
8790
|
+
(No backticks in this comment: the template is a template literal.) -->
|
|
8791
|
+
<button
|
|
8792
|
+
ngTab
|
|
8793
|
+
[value]="MORE_TABS_VALUE"
|
|
8794
|
+
[menuTrigger]="moreTabsMenu"
|
|
8795
|
+
variant="tertiary" [iconOnly]="true"
|
|
8796
|
+
[attr.aria-label]="'navbarTabs.moreTabs' | transloco" [tooltip]="'navbarTabs.moreTabs' | transloco" overflowStop
|
|
8797
|
+
[disabled]="moreTabs().length === 0"
|
|
8798
|
+
[class]="cn('shrink-0', isPill() ? 'ms-auto' : 'self-center')"
|
|
8799
|
+
[class.invisible]="moreTabs().length === 0">
|
|
8800
|
+
<EllipsisIcon [orientation]="isPill() ? 'vertical' : 'horizontal'"></EllipsisIcon>
|
|
8749
8801
|
<span class="sr-only">{{ "navbarTabs.moreTabs" | transloco }}</span>
|
|
8750
8802
|
</button>
|
|
8751
8803
|
</TabList>
|
|
@@ -8802,6 +8854,14 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
8802
8854
|
required: false
|
|
8803
8855
|
}]
|
|
8804
8856
|
}],
|
|
8857
|
+
variant: [{
|
|
8858
|
+
type: i0.Input,
|
|
8859
|
+
args: [{
|
|
8860
|
+
isSignal: true,
|
|
8861
|
+
alias: "variant",
|
|
8862
|
+
required: false
|
|
8863
|
+
}]
|
|
8864
|
+
}],
|
|
8805
8865
|
showCount: [{
|
|
8806
8866
|
type: i0.Input,
|
|
8807
8867
|
args: [{
|
|
@@ -23041,6 +23101,8 @@ var SearchFeedbackComponent = class SearchFeedbackComponent {
|
|
|
23041
23101
|
injector = inject(Injector);
|
|
23042
23102
|
pages = input.required(...ngDevMode ? [{ debugName: "pages" }] : /* istanbul ignore next */ []);
|
|
23043
23103
|
variant = input("secondary", ...ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []);
|
|
23104
|
+
size = input("md", ...ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []);
|
|
23105
|
+
dismissible = input(true, ...ngDevMode ? [{ debugName: "dismissible" }] : /* istanbul ignore next */ []);
|
|
23044
23106
|
auditService = inject(AuditService);
|
|
23045
23107
|
queryParamsStore = inject(QueryParamsStore);
|
|
23046
23108
|
transloco = inject(TranslocoService);
|
|
@@ -23160,12 +23222,26 @@ var SearchFeedbackComponent = class SearchFeedbackComponent {
|
|
|
23160
23222
|
isSignal: true,
|
|
23161
23223
|
isRequired: false,
|
|
23162
23224
|
transformFunction: null
|
|
23225
|
+
},
|
|
23226
|
+
size: {
|
|
23227
|
+
classPropertyName: "size",
|
|
23228
|
+
publicName: "size",
|
|
23229
|
+
isSignal: true,
|
|
23230
|
+
isRequired: false,
|
|
23231
|
+
transformFunction: null
|
|
23232
|
+
},
|
|
23233
|
+
dismissible: {
|
|
23234
|
+
classPropertyName: "dismissible",
|
|
23235
|
+
publicName: "dismissible",
|
|
23236
|
+
isSignal: true,
|
|
23237
|
+
isRequired: false,
|
|
23238
|
+
transformFunction: null
|
|
23163
23239
|
}
|
|
23164
23240
|
},
|
|
23165
23241
|
outputs: { onClose: "onClose" },
|
|
23166
23242
|
providers: [provideTranslocoScope("feedback")],
|
|
23167
23243
|
ngImport: i0,
|
|
23168
|
-
template: "@if (showButton()) {\n @let feedback = \"feedback.label\" | transloco;\n <button\n [menuTrigger]=\"feedbackMenu\"\n [variant]=\"variant()\"\n [aria-label]=\"feedback\">\n <comment-icon />\n <span>{{ feedback }}</span>\n <x-mark-icon class=\"ms-2\" (click)=\"close($event)\" />\n </button>\n\n <menu #feedbackMenu=\"menu\" (itemSelected)=\"onMenuItemSelected($event)\">\n <MenuContent>\n @if (showLike() && !disliked()) {\n @let feedbackLike = \"feedback.like\" | transloco;\n @let feedbackLiked = \"feedback.liked\" | transloco;\n <MenuItem value=\"like\" aria-label=\"feedback\">\n @if (liked()) {\n <thumbs-up-icon [solid]=\"true\" />\n {{ feedbackLiked }}\n } @else {\n <thumbs-up-icon />\n {{ feedbackLike }}\n }\n </MenuItem>\n }\n @if (showDislike() && !liked()) {\n <MenuItem value=\"dislike\" [aria-label]=\"'feedback.dislike' | transloco\">\n @if (disliked()) {\n <thumbs-down-icon [solid]=\"true\" />\n {{ \"feedback.disliked\" | transloco }}\n } @else {\n <thumbs-down-icon />\n {{ \"feedback.dislike\" | transloco }}\n }\n </MenuItem>\n }\n @for (menu of visibleMenus(); track menu.type) {\n @let feedbackTitle = \"feedback.\" + menu.type + \".title\" | transloco;\n <MenuItem [value]=\"menu.type\" [aria-label]=\"feedbackTitle\">\n <fa-icon [faClass]=\"menu.icon\" />\n {{ feedbackTitle }}\n </MenuItem>\n }\n </MenuContent>\n </menu>\n}\n",
|
|
23244
|
+
template: "@if (showButton()) {\n @let feedback = \"feedback.label\" | transloco;\n <button\n [menuTrigger]=\"feedbackMenu\"\n [variant]=\"variant()\"\n [size]=\"size()\"\n [aria-label]=\"feedback\">\n <comment-icon />\n <span>{{ feedback }}</span>\n @if (dismissible()) {\n <x-mark-icon class=\"ms-2\" (click)=\"close($event)\" />\n }\n </button>\n\n <menu #feedbackMenu=\"menu\" (itemSelected)=\"onMenuItemSelected($event)\">\n <MenuContent>\n @if (showLike() && !disliked()) {\n @let feedbackLike = \"feedback.like\" | transloco;\n @let feedbackLiked = \"feedback.liked\" | transloco;\n <MenuItem value=\"like\" aria-label=\"feedback\">\n @if (liked()) {\n <thumbs-up-icon [solid]=\"true\" />\n {{ feedbackLiked }}\n } @else {\n <thumbs-up-icon />\n {{ feedbackLike }}\n }\n </MenuItem>\n }\n @if (showDislike() && !liked()) {\n <MenuItem value=\"dislike\" [aria-label]=\"'feedback.dislike' | transloco\">\n @if (disliked()) {\n <thumbs-down-icon [solid]=\"true\" />\n {{ \"feedback.disliked\" | transloco }}\n } @else {\n <thumbs-down-icon />\n {{ \"feedback.dislike\" | transloco }}\n }\n </MenuItem>\n }\n @for (menu of visibleMenus(); track menu.type) {\n @let feedbackTitle = \"feedback.\" + menu.type + \".title\" | transloco;\n <MenuItem [value]=\"menu.type\" [aria-label]=\"feedbackTitle\">\n <fa-icon [faClass]=\"menu.icon\" />\n {{ feedbackTitle }}\n </MenuItem>\n }\n </MenuContent>\n </menu>\n}\n",
|
|
23169
23245
|
dependencies: [
|
|
23170
23246
|
{
|
|
23171
23247
|
kind: "directive",
|
|
@@ -23272,7 +23348,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
23272
23348
|
A11yModule
|
|
23273
23349
|
],
|
|
23274
23350
|
providers: [provideTranslocoScope("feedback")],
|
|
23275
|
-
template: "@if (showButton()) {\n @let feedback = \"feedback.label\" | transloco;\n <button\n [menuTrigger]=\"feedbackMenu\"\n [variant]=\"variant()\"\n [aria-label]=\"feedback\">\n <comment-icon />\n <span>{{ feedback }}</span>\n <x-mark-icon class=\"ms-2\" (click)=\"close($event)\" />\n </button>\n\n <menu #feedbackMenu=\"menu\" (itemSelected)=\"onMenuItemSelected($event)\">\n <MenuContent>\n @if (showLike() && !disliked()) {\n @let feedbackLike = \"feedback.like\" | transloco;\n @let feedbackLiked = \"feedback.liked\" | transloco;\n <MenuItem value=\"like\" aria-label=\"feedback\">\n @if (liked()) {\n <thumbs-up-icon [solid]=\"true\" />\n {{ feedbackLiked }}\n } @else {\n <thumbs-up-icon />\n {{ feedbackLike }}\n }\n </MenuItem>\n }\n @if (showDislike() && !liked()) {\n <MenuItem value=\"dislike\" [aria-label]=\"'feedback.dislike' | transloco\">\n @if (disliked()) {\n <thumbs-down-icon [solid]=\"true\" />\n {{ \"feedback.disliked\" | transloco }}\n } @else {\n <thumbs-down-icon />\n {{ \"feedback.dislike\" | transloco }}\n }\n </MenuItem>\n }\n @for (menu of visibleMenus(); track menu.type) {\n @let feedbackTitle = \"feedback.\" + menu.type + \".title\" | transloco;\n <MenuItem [value]=\"menu.type\" [aria-label]=\"feedbackTitle\">\n <fa-icon [faClass]=\"menu.icon\" />\n {{ feedbackTitle }}\n </MenuItem>\n }\n </MenuContent>\n </menu>\n}\n"
|
|
23351
|
+
template: "@if (showButton()) {\n @let feedback = \"feedback.label\" | transloco;\n <button\n [menuTrigger]=\"feedbackMenu\"\n [variant]=\"variant()\"\n [size]=\"size()\"\n [aria-label]=\"feedback\">\n <comment-icon />\n <span>{{ feedback }}</span>\n @if (dismissible()) {\n <x-mark-icon class=\"ms-2\" (click)=\"close($event)\" />\n }\n </button>\n\n <menu #feedbackMenu=\"menu\" (itemSelected)=\"onMenuItemSelected($event)\">\n <MenuContent>\n @if (showLike() && !disliked()) {\n @let feedbackLike = \"feedback.like\" | transloco;\n @let feedbackLiked = \"feedback.liked\" | transloco;\n <MenuItem value=\"like\" aria-label=\"feedback\">\n @if (liked()) {\n <thumbs-up-icon [solid]=\"true\" />\n {{ feedbackLiked }}\n } @else {\n <thumbs-up-icon />\n {{ feedbackLike }}\n }\n </MenuItem>\n }\n @if (showDislike() && !liked()) {\n <MenuItem value=\"dislike\" [aria-label]=\"'feedback.dislike' | transloco\">\n @if (disliked()) {\n <thumbs-down-icon [solid]=\"true\" />\n {{ \"feedback.disliked\" | transloco }}\n } @else {\n <thumbs-down-icon />\n {{ \"feedback.dislike\" | transloco }}\n }\n </MenuItem>\n }\n @for (menu of visibleMenus(); track menu.type) {\n @let feedbackTitle = \"feedback.\" + menu.type + \".title\" | transloco;\n <MenuItem [value]=\"menu.type\" [aria-label]=\"feedbackTitle\">\n <fa-icon [faClass]=\"menu.icon\" />\n {{ feedbackTitle }}\n </MenuItem>\n }\n </MenuContent>\n </menu>\n}\n"
|
|
23276
23352
|
}]
|
|
23277
23353
|
}],
|
|
23278
23354
|
propDecorators: {
|
|
@@ -23295,6 +23371,22 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
23295
23371
|
alias: "variant",
|
|
23296
23372
|
required: false
|
|
23297
23373
|
}]
|
|
23374
|
+
}],
|
|
23375
|
+
size: [{
|
|
23376
|
+
type: i0.Input,
|
|
23377
|
+
args: [{
|
|
23378
|
+
isSignal: true,
|
|
23379
|
+
alias: "size",
|
|
23380
|
+
required: false
|
|
23381
|
+
}]
|
|
23382
|
+
}],
|
|
23383
|
+
dismissible: [{
|
|
23384
|
+
type: i0.Input,
|
|
23385
|
+
args: [{
|
|
23386
|
+
isSignal: true,
|
|
23387
|
+
alias: "dismissible",
|
|
23388
|
+
required: false
|
|
23389
|
+
}]
|
|
23298
23390
|
}]
|
|
23299
23391
|
}
|
|
23300
23392
|
});
|
|
@@ -24533,8 +24625,8 @@ var AggregationDateComponent = class AggregationDateComponent {
|
|
|
24533
24625
|
host: { properties: { "class": "cn(\"@container flex h-[inherit] max-h-[inherit] flex-col\", class(), isEmpty() && \"hidden\")" } },
|
|
24534
24626
|
providers: [provideTranslocoScope("filters")],
|
|
24535
24627
|
ngImport: i0,
|
|
24536
|
-
template: "<!-- `<details>`/`<summary>` stay native (open/close, the `name`-based exclusive accordion). A facet is a\n disclosure, not a grid row: see `aggregation-panel.html` for the why (same pattern, duplicated here\n since this facet type doesn't go through `AggregationPanelComponent`). The `<summary>` itself is\n `tabindex=\"-1\"`: the header's chevron is the keyboard stop, see the chevron below. Clear and apply are not in the\n header: they are in the body, in a bar at the top of the facet. -->\n<ng-template #content>\n <!-- content wrapper -->\n <!-- RGAA 11.5 / WCAG 1.3.1: the radios below all share one `name`, so they are one set\n and have to be exposed as one. `role=\"group\"` here rather than `role=\"radiogroup\"`\n on the list: a radiogroup owns its radios directly, and the <li> in between would be\n invalid owned children — the list would also stop announcing how many options it\n holds. The name is the aggregation's own title, the words the header already shows;\n with no title the group stays unnamed rather than carrying an empty label. -->\n @let groupName = aggregation()?.display;\n <form\n class=\"flex min-h-0 flex-1 flex-col\"\n (keydown.enter)=\"onOptionEnter($event)\">\n <!-- The same bar as for the list and the tree, in a popover and in the filters drawer alike: clear (a button with a text, there but disabled when there is nothing\n to clear) and, while a choice is pending, apply, without an icon. There is no search field here to put them under:\n they sit at the top of the facet. -->\n <div class=\"flex shrink-0 items-center justify-end gap-1 pt-1 pl-1\">\n <button\n type=\"button\"\n variant=\"tertiary\"\n size=\"sm\"\n class=\"shrink-0\"\n [disabled]=\"!hasFilters() && !selection()\"\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\n (click)=\"clear()\">\n {{ \"filters.clearAll\" | transloco }}\n </button>\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n <button\n type=\"button\"\n variant=\"accent\"\n size=\"sm\"\n class=\"shrink-0 overflow-hidden\"\n (click)=\"apply()\">\n <span class=\"truncate\">{{ \"filters.apply\" | transloco }}</span>\n </button>\n }\n </div>\n <div\n class=\"flex min-h-0 flex-1 flex-col\"\n role=\"group\"\n [attr.aria-label]=\"groupName ? (groupName | syslang | transloco) : null\">\n <ul\n class=\"flex min-h-0 flex-1 snap-y snap-start scrollbar-thin flex-col gap-0.5 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track option.display) {\n <li\n role=\"listitem\"\n [attr.aria-label]=\"\n maskedLabel(option) ?? (option.display | syslang | transloco)\n \"\n [class]=\"\n cn(\n itemStateClass,\n 'flex cursor-pointer items-center p-2 text-sm text-(--font-neutral-base) select-none',\n dateModel().option === option.display &&\n 'bg-(--bg-primary-lighter)',\n option.hidden && 'hidden',\n option.disabled && 'pointer-events-none opacity-40'\n )\n \"\n [attr.aria-hidden]=\"option.disabled\">\n <radio\n name=\"date-filter\"\n class=\"grow\"\n [checked]=\"dateModel().option === option.display\"\n [disabled]=\"option.disabled\"\n (checkedChange)=\"selectOption(option.display)\">\n {{\n maskedLabel(option) ?? (option.display | syslang | transloco)\n }}\n </radio>\n </li>\n }\n\n @if (allowCustomRange) {\n <li\n role=\"listitem\"\n aria-label=\"open date range picker\"\n [class]=\"\n cn(\n itemStateClass,\n 'flex cursor-pointer items-center gap-(--spacing-xs) p-2 text-sm text-(--font-neutral-base) select-none',\n dateModel().option === 'custom-range' &&\n 'bg-(--bg-primary-lighter)'\n )\n \">\n <radio\n name=\"date-filter\"\n class=\"grow items-center\"\n [checked]=\"dateModel().option === 'custom-range'\"\n (checkedChange)=\"selectOption('custom-range')\">\n <div class=\"flex flex-wrap items-center gap-x-1 gap-y-1\">\n <div class=\"flex items-center gap-1\">\n <label\n [attr.for]=\"startId\"\n class=\"radio-label min-w-8 truncate\"\n >{{ \"filters.from\" | transloco }}</label\n >\n <input-group\n class=\"h-8 max-w-[18ch] min-w-[18ch] has-[[readonly]]:border-(--stroke-primary-muted)\">\n <input\n input-control\n [id]=\"startId\"\n name=\"start\"\n type=\"text\"\n readonly\n [value]=\"customRangeFrom()\"\n (click)=\"selectAndOpenDialog()\"\n (keydown.enter)=\"onRangeInputEnter($event)\" />\n </input-group>\n </div>\n <div class=\"flex items-center gap-1\">\n <label\n [attr.for]=\"endId\"\n class=\"radio-label min-w-8 truncate text-right\"\n >{{ \"filters.to\" | transloco }}</label\n >\n <input-group\n class=\"h-8 max-w-[18ch] min-w-[18ch] has-[[readonly]]:border-(--stroke-primary-muted)\">\n <input\n input-control\n [id]=\"endId\"\n name=\"end\"\n type=\"text\"\n readonly\n [value]=\"customRangeTo()\"\n (click)=\"selectAndOpenDialog()\"\n (keydown.enter)=\"onRangeInputEnter($event)\" />\n </input-group>\n </div>\n </div>\n </radio>\n </li>\n }\n </ul>\n </div>\n </form>\n</ng-template>\n\n@if (headless()) {\n <div class=\"flex min-h-0 flex-1 flex-col\">\n <ng-container [ngTemplateOutlet]=\"content\" />\n </div>\n} @else {\n <details\n [attr.open]=\"expanded()\"\n [attr.name]=\"id()\"\n class=\"group flex min-h-0 flex-1 flex-col space-y-2 details-content:flex details-content:min-h-0 details-content:flex-
|
|
24537
|
-
styles: [":host{min-width:200px}ul[role=list]{scrollbar-width:thin}\n"],
|
|
24628
|
+
template: "<!-- `<details>`/`<summary>` stay native (open/close, the `name`-based exclusive accordion). A facet is a\n disclosure, not a grid row: see `aggregation-panel.html` for the why (same pattern, duplicated here\n since this facet type doesn't go through `AggregationPanelComponent`). The `<summary>` itself is\n `tabindex=\"-1\"`: the header's chevron is the keyboard stop, see the chevron below. Clear and apply are not in the\n header: they are in the body, in a bar at the top of the facet. -->\n<ng-template #content>\n <!-- content wrapper -->\n <!-- RGAA 11.5 / WCAG 1.3.1: the radios below all share one `name`, so they are one set\n and have to be exposed as one. `role=\"group\"` here rather than `role=\"radiogroup\"`\n on the list: a radiogroup owns its radios directly, and the <li> in between would be\n invalid owned children — the list would also stop announcing how many options it\n holds. The name is the aggregation's own title, the words the header already shows;\n with no title the group stays unnamed rather than carrying an empty label. -->\n @let groupName = aggregation()?.display;\n <form\n class=\"flex min-h-0 flex-1 flex-col\"\n (keydown.enter)=\"onOptionEnter($event)\">\n <!-- The same bar as for the list and the tree, in a popover and in the filters drawer alike: clear (a button with a text, there but disabled when there is nothing\n to clear) and, while a choice is pending, apply, without an icon. There is no search field here to put them under:\n they sit at the top of the facet. -->\n <div class=\"flex shrink-0 items-center justify-end gap-1 pt-1 pl-1\">\n <button\n type=\"button\"\n variant=\"tertiary\"\n size=\"sm\"\n class=\"shrink-0\"\n [disabled]=\"!hasFilters() && !selection()\"\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\n (click)=\"clear()\">\n {{ \"filters.clearAll\" | transloco }}\n </button>\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n <button\n type=\"button\"\n variant=\"accent\"\n size=\"sm\"\n class=\"shrink-0 overflow-hidden\"\n (click)=\"apply()\">\n <span class=\"truncate\">{{ \"filters.apply\" | transloco }}</span>\n </button>\n }\n </div>\n <div\n class=\"flex min-h-0 flex-1 flex-col\"\n role=\"group\"\n [attr.aria-label]=\"groupName ? (groupName | syslang | transloco) : null\">\n <ul\n class=\"flex min-h-0 flex-1 snap-y snap-start scrollbar-thin flex-col gap-0.5 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track option.display) {\n <li\n role=\"listitem\"\n [attr.aria-label]=\"\n maskedLabel(option) ?? (option.display | syslang | transloco)\n \"\n [class]=\"\n cn(\n itemStateClass,\n 'flex cursor-pointer items-center p-2 text-sm text-(--font-neutral-base) select-none',\n dateModel().option === option.display &&\n 'bg-(--bg-primary-lighter)',\n option.hidden && 'hidden',\n option.disabled && 'pointer-events-none opacity-40'\n )\n \"\n [attr.aria-hidden]=\"option.disabled\">\n <radio\n name=\"date-filter\"\n class=\"grow\"\n [checked]=\"dateModel().option === option.display\"\n [disabled]=\"option.disabled\"\n (checkedChange)=\"selectOption(option.display)\">\n {{\n maskedLabel(option) ?? (option.display | syslang | transloco)\n }}\n </radio>\n </li>\n }\n\n @if (allowCustomRange) {\n <li\n role=\"listitem\"\n aria-label=\"open date range picker\"\n [class]=\"\n cn(\n itemStateClass,\n 'flex cursor-pointer items-center gap-(--spacing-xs) p-2 text-sm text-(--font-neutral-base) select-none',\n dateModel().option === 'custom-range' &&\n 'bg-(--bg-primary-lighter)'\n )\n \">\n <radio\n name=\"date-filter\"\n class=\"grow items-center\"\n [checked]=\"dateModel().option === 'custom-range'\"\n (checkedChange)=\"selectOption('custom-range')\">\n <div class=\"flex flex-wrap items-center gap-x-1 gap-y-1\">\n <div class=\"flex items-center gap-1\">\n <label\n [attr.for]=\"startId\"\n class=\"radio-label min-w-8 truncate\"\n >{{ \"filters.from\" | transloco }}</label\n >\n <input-group\n class=\"h-8 max-w-[18ch] min-w-[18ch] has-[[readonly]]:border-(--stroke-primary-muted)\">\n <input\n input-control\n [id]=\"startId\"\n name=\"start\"\n type=\"text\"\n readonly\n [value]=\"customRangeFrom()\"\n (click)=\"selectAndOpenDialog()\"\n (keydown.enter)=\"onRangeInputEnter($event)\" />\n </input-group>\n </div>\n <div class=\"flex items-center gap-1\">\n <label\n [attr.for]=\"endId\"\n class=\"radio-label min-w-8 truncate text-right\"\n >{{ \"filters.to\" | transloco }}</label\n >\n <input-group\n class=\"h-8 max-w-[18ch] min-w-[18ch] has-[[readonly]]:border-(--stroke-primary-muted)\">\n <input\n input-control\n [id]=\"endId\"\n name=\"end\"\n type=\"text\"\n readonly\n [value]=\"customRangeTo()\"\n (click)=\"selectAndOpenDialog()\"\n (keydown.enter)=\"onRangeInputEnter($event)\" />\n </input-group>\n </div>\n </div>\n </radio>\n </li>\n }\n </ul>\n </div>\n </form>\n</ng-template>\n\n@if (headless()) {\n <div class=\"flex min-h-0 flex-1 flex-col\">\n <ng-container [ngTemplateOutlet]=\"content\" />\n </div>\n} @else {\n <details\n [attr.open]=\"expanded()\"\n [attr.name]=\"id()\"\n class=\"group flex min-h-0 flex-1 flex-col space-y-2 details-content:flex details-content:min-h-0 details-content:flex-col\">\n <!-- scroll-mb-3/scroll-mt-16: see aggregation-panel-row.html for the full rationale\n (bottom margin against the container's edge, much bigger top margin to also clear\n <More>'s sticky facet-name search box when navigating ArrowUp). -->\n <summary\n tabindex=\"-1\"\n [class.cursor-pointer]=\"collapsible()\"\n [class]=\"rowStateClass\"\n class=\"m-0 flex h-8 w-full shrink-0 scroll-mt-16 scroll-mb-3 items-center gap-1 pl-1 font-semibold select-none\"\n (click)=\"onHeaderClick($event)\">\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n\n @if (collapsible()) {\n <!-- The `<summary>` is out of the tab order (`tabindex=\"-1\"`), so this button is the keyboard way to\n collapse the facet. Its click bubbles to the `<summary>`, whose handler does the toggle, and it\n exposes `aria-expanded`. -->\n <button\n type=\"button\"\n variant=\"tertiary\"\n [iconOnly]=\"true\"\n size=\"sm\"\n [aria-label]=\"'filters.toggle' | transloco\"\n [attr.aria-expanded]=\"!isCollapsed()\"\n [tooltip]=\"'filters.toggle' | transloco\"\n class=\"shrink-0 cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevron-right class=\"shrink-0\" />\n </button>\n }\n </summary>\n <ng-container [ngTemplateOutlet]=\"content\" />\n </details>\n}\n",
|
|
24629
|
+
styles: [":host{interpolate-size:allow-keywords}details::details-content{flex:1 1 auto;block-size:0;overflow:clip;overflow-clip-margin:.25rem;transition:block-size .2s cubic-bezier(.32,.72,0,1),content-visibility .2s allow-discrete}details[open]::details-content{block-size:auto}@media(prefers-reduced-motion:reduce){details::details-content{transition:none}}\n", ":host{min-width:200px}ul[role=list]{scrollbar-width:thin}\n"],
|
|
24538
24630
|
dependencies: [
|
|
24539
24631
|
{
|
|
24540
24632
|
kind: "directive",
|
|
@@ -24665,8 +24757,8 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
24665
24757
|
TooltipDirective
|
|
24666
24758
|
],
|
|
24667
24759
|
host: { "[class]": "cn(\"@container flex h-[inherit] max-h-[inherit] flex-col\", class(), isEmpty() && \"hidden\")" },
|
|
24668
|
-
template: "<!-- `<details>`/`<summary>` stay native (open/close, the `name`-based exclusive accordion). A facet is a\n disclosure, not a grid row: see `aggregation-panel.html` for the why (same pattern, duplicated here\n since this facet type doesn't go through `AggregationPanelComponent`). The `<summary>` itself is\n `tabindex=\"-1\"`: the header's chevron is the keyboard stop, see the chevron below. Clear and apply are not in the\n header: they are in the body, in a bar at the top of the facet. -->\n<ng-template #content>\n <!-- content wrapper -->\n <!-- RGAA 11.5 / WCAG 1.3.1: the radios below all share one `name`, so they are one set\n and have to be exposed as one. `role=\"group\"` here rather than `role=\"radiogroup\"`\n on the list: a radiogroup owns its radios directly, and the <li> in between would be\n invalid owned children — the list would also stop announcing how many options it\n holds. The name is the aggregation's own title, the words the header already shows;\n with no title the group stays unnamed rather than carrying an empty label. -->\n @let groupName = aggregation()?.display;\n <form\n class=\"flex min-h-0 flex-1 flex-col\"\n (keydown.enter)=\"onOptionEnter($event)\">\n <!-- The same bar as for the list and the tree, in a popover and in the filters drawer alike: clear (a button with a text, there but disabled when there is nothing\n to clear) and, while a choice is pending, apply, without an icon. There is no search field here to put them under:\n they sit at the top of the facet. -->\n <div class=\"flex shrink-0 items-center justify-end gap-1 pt-1 pl-1\">\n <button\n type=\"button\"\n variant=\"tertiary\"\n size=\"sm\"\n class=\"shrink-0\"\n [disabled]=\"!hasFilters() && !selection()\"\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\n (click)=\"clear()\">\n {{ \"filters.clearAll\" | transloco }}\n </button>\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n <button\n type=\"button\"\n variant=\"accent\"\n size=\"sm\"\n class=\"shrink-0 overflow-hidden\"\n (click)=\"apply()\">\n <span class=\"truncate\">{{ \"filters.apply\" | transloco }}</span>\n </button>\n }\n </div>\n <div\n class=\"flex min-h-0 flex-1 flex-col\"\n role=\"group\"\n [attr.aria-label]=\"groupName ? (groupName | syslang | transloco) : null\">\n <ul\n class=\"flex min-h-0 flex-1 snap-y snap-start scrollbar-thin flex-col gap-0.5 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track option.display) {\n <li\n role=\"listitem\"\n [attr.aria-label]=\"\n maskedLabel(option) ?? (option.display | syslang | transloco)\n \"\n [class]=\"\n cn(\n itemStateClass,\n 'flex cursor-pointer items-center p-2 text-sm text-(--font-neutral-base) select-none',\n dateModel().option === option.display &&\n 'bg-(--bg-primary-lighter)',\n option.hidden && 'hidden',\n option.disabled && 'pointer-events-none opacity-40'\n )\n \"\n [attr.aria-hidden]=\"option.disabled\">\n <radio\n name=\"date-filter\"\n class=\"grow\"\n [checked]=\"dateModel().option === option.display\"\n [disabled]=\"option.disabled\"\n (checkedChange)=\"selectOption(option.display)\">\n {{\n maskedLabel(option) ?? (option.display | syslang | transloco)\n }}\n </radio>\n </li>\n }\n\n @if (allowCustomRange) {\n <li\n role=\"listitem\"\n aria-label=\"open date range picker\"\n [class]=\"\n cn(\n itemStateClass,\n 'flex cursor-pointer items-center gap-(--spacing-xs) p-2 text-sm text-(--font-neutral-base) select-none',\n dateModel().option === 'custom-range' &&\n 'bg-(--bg-primary-lighter)'\n )\n \">\n <radio\n name=\"date-filter\"\n class=\"grow items-center\"\n [checked]=\"dateModel().option === 'custom-range'\"\n (checkedChange)=\"selectOption('custom-range')\">\n <div class=\"flex flex-wrap items-center gap-x-1 gap-y-1\">\n <div class=\"flex items-center gap-1\">\n <label\n [attr.for]=\"startId\"\n class=\"radio-label min-w-8 truncate\"\n >{{ \"filters.from\" | transloco }}</label\n >\n <input-group\n class=\"h-8 max-w-[18ch] min-w-[18ch] has-[[readonly]]:border-(--stroke-primary-muted)\">\n <input\n input-control\n [id]=\"startId\"\n name=\"start\"\n type=\"text\"\n readonly\n [value]=\"customRangeFrom()\"\n (click)=\"selectAndOpenDialog()\"\n (keydown.enter)=\"onRangeInputEnter($event)\" />\n </input-group>\n </div>\n <div class=\"flex items-center gap-1\">\n <label\n [attr.for]=\"endId\"\n class=\"radio-label min-w-8 truncate text-right\"\n >{{ \"filters.to\" | transloco }}</label\n >\n <input-group\n class=\"h-8 max-w-[18ch] min-w-[18ch] has-[[readonly]]:border-(--stroke-primary-muted)\">\n <input\n input-control\n [id]=\"endId\"\n name=\"end\"\n type=\"text\"\n readonly\n [value]=\"customRangeTo()\"\n (click)=\"selectAndOpenDialog()\"\n (keydown.enter)=\"onRangeInputEnter($event)\" />\n </input-group>\n </div>\n </div>\n </radio>\n </li>\n }\n </ul>\n </div>\n </form>\n</ng-template>\n\n@if (headless()) {\n <div class=\"flex min-h-0 flex-1 flex-col\">\n <ng-container [ngTemplateOutlet]=\"content\" />\n </div>\n} @else {\n <details\n [attr.open]=\"expanded()\"\n [attr.name]=\"id()\"\n class=\"group flex min-h-0 flex-1 flex-col space-y-2 details-content:flex details-content:min-h-0 details-content:flex-
|
|
24669
|
-
styles: [":host{min-width:200px}ul[role=list]{scrollbar-width:thin}\n"]
|
|
24760
|
+
template: "<!-- `<details>`/`<summary>` stay native (open/close, the `name`-based exclusive accordion). A facet is a\n disclosure, not a grid row: see `aggregation-panel.html` for the why (same pattern, duplicated here\n since this facet type doesn't go through `AggregationPanelComponent`). The `<summary>` itself is\n `tabindex=\"-1\"`: the header's chevron is the keyboard stop, see the chevron below. Clear and apply are not in the\n header: they are in the body, in a bar at the top of the facet. -->\n<ng-template #content>\n <!-- content wrapper -->\n <!-- RGAA 11.5 / WCAG 1.3.1: the radios below all share one `name`, so they are one set\n and have to be exposed as one. `role=\"group\"` here rather than `role=\"radiogroup\"`\n on the list: a radiogroup owns its radios directly, and the <li> in between would be\n invalid owned children — the list would also stop announcing how many options it\n holds. The name is the aggregation's own title, the words the header already shows;\n with no title the group stays unnamed rather than carrying an empty label. -->\n @let groupName = aggregation()?.display;\n <form\n class=\"flex min-h-0 flex-1 flex-col\"\n (keydown.enter)=\"onOptionEnter($event)\">\n <!-- The same bar as for the list and the tree, in a popover and in the filters drawer alike: clear (a button with a text, there but disabled when there is nothing\n to clear) and, while a choice is pending, apply, without an icon. There is no search field here to put them under:\n they sit at the top of the facet. -->\n <div class=\"flex shrink-0 items-center justify-end gap-1 pt-1 pl-1\">\n <button\n type=\"button\"\n variant=\"tertiary\"\n size=\"sm\"\n class=\"shrink-0\"\n [disabled]=\"!hasFilters() && !selection()\"\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\n (click)=\"clear()\">\n {{ \"filters.clearAll\" | transloco }}\n </button>\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n <button\n type=\"button\"\n variant=\"accent\"\n size=\"sm\"\n class=\"shrink-0 overflow-hidden\"\n (click)=\"apply()\">\n <span class=\"truncate\">{{ \"filters.apply\" | transloco }}</span>\n </button>\n }\n </div>\n <div\n class=\"flex min-h-0 flex-1 flex-col\"\n role=\"group\"\n [attr.aria-label]=\"groupName ? (groupName | syslang | transloco) : null\">\n <ul\n class=\"flex min-h-0 flex-1 snap-y snap-start scrollbar-thin flex-col gap-0.5 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track option.display) {\n <li\n role=\"listitem\"\n [attr.aria-label]=\"\n maskedLabel(option) ?? (option.display | syslang | transloco)\n \"\n [class]=\"\n cn(\n itemStateClass,\n 'flex cursor-pointer items-center p-2 text-sm text-(--font-neutral-base) select-none',\n dateModel().option === option.display &&\n 'bg-(--bg-primary-lighter)',\n option.hidden && 'hidden',\n option.disabled && 'pointer-events-none opacity-40'\n )\n \"\n [attr.aria-hidden]=\"option.disabled\">\n <radio\n name=\"date-filter\"\n class=\"grow\"\n [checked]=\"dateModel().option === option.display\"\n [disabled]=\"option.disabled\"\n (checkedChange)=\"selectOption(option.display)\">\n {{\n maskedLabel(option) ?? (option.display | syslang | transloco)\n }}\n </radio>\n </li>\n }\n\n @if (allowCustomRange) {\n <li\n role=\"listitem\"\n aria-label=\"open date range picker\"\n [class]=\"\n cn(\n itemStateClass,\n 'flex cursor-pointer items-center gap-(--spacing-xs) p-2 text-sm text-(--font-neutral-base) select-none',\n dateModel().option === 'custom-range' &&\n 'bg-(--bg-primary-lighter)'\n )\n \">\n <radio\n name=\"date-filter\"\n class=\"grow items-center\"\n [checked]=\"dateModel().option === 'custom-range'\"\n (checkedChange)=\"selectOption('custom-range')\">\n <div class=\"flex flex-wrap items-center gap-x-1 gap-y-1\">\n <div class=\"flex items-center gap-1\">\n <label\n [attr.for]=\"startId\"\n class=\"radio-label min-w-8 truncate\"\n >{{ \"filters.from\" | transloco }}</label\n >\n <input-group\n class=\"h-8 max-w-[18ch] min-w-[18ch] has-[[readonly]]:border-(--stroke-primary-muted)\">\n <input\n input-control\n [id]=\"startId\"\n name=\"start\"\n type=\"text\"\n readonly\n [value]=\"customRangeFrom()\"\n (click)=\"selectAndOpenDialog()\"\n (keydown.enter)=\"onRangeInputEnter($event)\" />\n </input-group>\n </div>\n <div class=\"flex items-center gap-1\">\n <label\n [attr.for]=\"endId\"\n class=\"radio-label min-w-8 truncate text-right\"\n >{{ \"filters.to\" | transloco }}</label\n >\n <input-group\n class=\"h-8 max-w-[18ch] min-w-[18ch] has-[[readonly]]:border-(--stroke-primary-muted)\">\n <input\n input-control\n [id]=\"endId\"\n name=\"end\"\n type=\"text\"\n readonly\n [value]=\"customRangeTo()\"\n (click)=\"selectAndOpenDialog()\"\n (keydown.enter)=\"onRangeInputEnter($event)\" />\n </input-group>\n </div>\n </div>\n </radio>\n </li>\n }\n </ul>\n </div>\n </form>\n</ng-template>\n\n@if (headless()) {\n <div class=\"flex min-h-0 flex-1 flex-col\">\n <ng-container [ngTemplateOutlet]=\"content\" />\n </div>\n} @else {\n <details\n [attr.open]=\"expanded()\"\n [attr.name]=\"id()\"\n class=\"group flex min-h-0 flex-1 flex-col space-y-2 details-content:flex details-content:min-h-0 details-content:flex-col\">\n <!-- scroll-mb-3/scroll-mt-16: see aggregation-panel-row.html for the full rationale\n (bottom margin against the container's edge, much bigger top margin to also clear\n <More>'s sticky facet-name search box when navigating ArrowUp). -->\n <summary\n tabindex=\"-1\"\n [class.cursor-pointer]=\"collapsible()\"\n [class]=\"rowStateClass\"\n class=\"m-0 flex h-8 w-full shrink-0 scroll-mt-16 scroll-mb-3 items-center gap-1 pl-1 font-semibold select-none\"\n (click)=\"onHeaderClick($event)\">\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n\n @if (collapsible()) {\n <!-- The `<summary>` is out of the tab order (`tabindex=\"-1\"`), so this button is the keyboard way to\n collapse the facet. Its click bubbles to the `<summary>`, whose handler does the toggle, and it\n exposes `aria-expanded`. -->\n <button\n type=\"button\"\n variant=\"tertiary\"\n [iconOnly]=\"true\"\n size=\"sm\"\n [aria-label]=\"'filters.toggle' | transloco\"\n [attr.aria-expanded]=\"!isCollapsed()\"\n [tooltip]=\"'filters.toggle' | transloco\"\n class=\"shrink-0 cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevron-right class=\"shrink-0\" />\n </button>\n }\n </summary>\n <ng-container [ngTemplateOutlet]=\"content\" />\n </details>\n}\n",
|
|
24761
|
+
styles: [":host{interpolate-size:allow-keywords}details::details-content{flex:1 1 auto;block-size:0;overflow:clip;overflow-clip-margin:.25rem;transition:block-size .2s cubic-bezier(.32,.72,0,1),content-visibility .2s allow-discrete}details[open]::details-content{block-size:auto}@media(prefers-reduced-motion:reduce){details::details-content{transition:none}}\n", ":host{min-width:200px}ul[role=list]{scrollbar-width:thin}\n"]
|
|
24670
24762
|
}]
|
|
24671
24763
|
}],
|
|
24672
24764
|
ctorParameters: () => [],
|
|
@@ -24869,12 +24961,30 @@ var AggregationPanelComponent = class AggregationPanelComponent {
|
|
|
24869
24961
|
isCollapsed = linkedSignal(() => this.collapsed(), ...ngDevMode ? [{ debugName: "isCollapsed" }] : /* istanbul ignore next */ []);
|
|
24870
24962
|
expanded = computed(() => this.isCollapsed() ? null : "", ...ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []);
|
|
24871
24963
|
searchBox = viewChild(SearchInputComponent, ...ngDevMode ? [{ debugName: "searchBox" }] : /* istanbul ignore next */ []);
|
|
24964
|
+
searchToggle = viewChild("searchToggle", ...ngDevMode ? [{ debugName: "searchToggle" }] : /* istanbul ignore next */ []);
|
|
24965
|
+
injector = inject(Injector);
|
|
24872
24966
|
isInPopover = !!inject(PopoverDirective, { optional: true });
|
|
24873
|
-
|
|
24874
|
-
|
|
24875
|
-
|
|
24876
|
-
|
|
24877
|
-
|
|
24967
|
+
static nextId = 0;
|
|
24968
|
+
searchId = `aggregation-search-${AggregationPanelComponent.nextId++}`;
|
|
24969
|
+
searchRequested = signal(false, ...ngDevMode ? [{ debugName: "searchRequested" }] : /* istanbul ignore next */ []);
|
|
24970
|
+
canSearch = computed(() => !!this.aggregation()?.searchable && this.itemsLength() > 0, ...ngDevMode ? [{ debugName: "canSearch" }] : /* istanbul ignore next */ []);
|
|
24971
|
+
showSearch = computed(() => this.canSearch() && (this.searchRequested() || this.searchText() !== ""), ...ngDevMode ? [{ debugName: "showSearch" }] : /* istanbul ignore next */ []);
|
|
24972
|
+
toggleSearch(event) {
|
|
24973
|
+
event?.stopPropagation();
|
|
24974
|
+
event?.preventDefault();
|
|
24975
|
+
if (this.showSearch()) {
|
|
24976
|
+
this.searchText.set("");
|
|
24977
|
+
this.searchRequested.set(false);
|
|
24978
|
+
afterNextRender(() => this.searchToggle()?.nativeElement.focus(), { injector: this.injector });
|
|
24979
|
+
return;
|
|
24980
|
+
}
|
|
24981
|
+
this.searchRequested.set(true);
|
|
24982
|
+
afterNextRender(() => this.searchBox()?.searchInput().nativeElement.focus(), { injector: this.injector });
|
|
24983
|
+
}
|
|
24984
|
+
onSearchEscape(event) {
|
|
24985
|
+
if (!this.showSearch()) return;
|
|
24986
|
+
event.stopPropagation();
|
|
24987
|
+
this.toggleSearch();
|
|
24878
24988
|
}
|
|
24879
24989
|
onHeaderClick(event) {
|
|
24880
24990
|
if (!this.isDate() && this.isEmpty()) {
|
|
@@ -25039,9 +25149,16 @@ var AggregationPanelComponent = class AggregationPanelComponent {
|
|
|
25039
25149
|
predicate: SearchInputComponent,
|
|
25040
25150
|
descendants: true,
|
|
25041
25151
|
isSignal: true
|
|
25152
|
+
}, {
|
|
25153
|
+
propertyName: "searchToggle",
|
|
25154
|
+
first: true,
|
|
25155
|
+
predicate: ["searchToggle"],
|
|
25156
|
+
descendants: true,
|
|
25157
|
+
isSignal: true
|
|
25042
25158
|
}],
|
|
25043
25159
|
ngImport: i0,
|
|
25044
|
-
template: "<!-- `<details>`/`<summary>` stay native (open/close, the `name`-based exclusive accordion): a facet is\n a disclosure, not a grid row. They used to sit in an `@angular/aria` grid so ArrowUp/ArrowDown could\n jump from one facet header to the next, but a grid without headers reads as a layout table\n (RGAA 5.3). The `<summary>` itself is `tabindex=\"-1\"`: Tab walks the header's chevron, never the header row, and the\n chevron is the keyboard way to collapse. Select all, clear and apply are not in the header: they are in the body, in a\n bar under the search field. Label, header actions (the count and the chevron) and body are `<ng-template>`s: `<ng-content select=\"…\">` can only be\n declared once, and the body is projected content. -->\n\n<ng-template #labelSlot>\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow truncate\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n</ng-template>\n\n<ng-template #headerActionsSlot>\n @if (showFiltersCount() && filtersCount() > 0) {\n <Badge variant=\"number\" fill=\"primary\" scheme=\"sage\" size=\"xs\" class=\"ml-1\">\n {{ filtersCount() }}\n </Badge>\n }\n @if (collapsible()) {\n <!-- The `<summary>` is out of the tab order (`tabindex=\"-1\"`), so this button is the keyboard way to\n collapse the facet. Its click bubbles to the `<summary>`, whose handler does the toggle, and it\n exposes `aria-expanded`. -->\n <button\n type=\"button\"\n variant=\"tertiary\"\n [iconOnly]=\"true\"\n size=\"sm\"\n [attr.aria-expanded]=\"!isCollapsed()\"\n [tooltip]=\"'filters.toggle' | transloco\"\n class=\"shrink-0 cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevron-right class=\"shrink-0\" />\n <span class=\"sr-only\">{{ \"filters.toggle\" | transloco }}</span>\n </button>\n }\n</ng-template>\n\n<ng-template #bodySlot>\n <div class=\"flex min-h-0 flex-1 flex-col\">\n @if (
|
|
25160
|
+
template: "<!-- `<details>`/`<summary>` stay native (open/close, the `name`-based exclusive accordion): a facet is\n a disclosure, not a grid row. They used to sit in an `@angular/aria` grid so ArrowUp/ArrowDown could\n jump from one facet header to the next, but a grid without headers reads as a layout table\n (RGAA 5.3). The `<summary>` itself is `tabindex=\"-1\"`: Tab walks the header's chevron, never the header row, and the\n chevron is the keyboard way to collapse. Select all, clear and apply are not in the header: they are in the body, in a\n bar under the search field. Label, header actions (the count and the chevron) and body are `<ng-template>`s: `<ng-content select=\"…\">` can only be\n declared once, and the body is projected content. -->\n\n<ng-template #searchToggleSlot>\n <button\n #searchToggle\n type=\"button\"\n variant=\"tertiary\"\n [iconOnly]=\"true\"\n size=\"sm\"\n class=\"shrink-0 cursor-pointer\"\n [attr.aria-expanded]=\"showSearch()\"\n [attr.aria-controls]=\"searchId\"\n [tooltip]=\"\n 'filters.searchInFacet'\n | transloco: { facet: (aggregation()?.display | syslang | transloco) }\n \"\n (click)=\"toggleSearch($event)\">\n <search-icon class=\"shrink-0\" />\n <span class=\"sr-only\">{{\n \"filters.searchInFacet\"\n | transloco: { facet: (aggregation()?.display | syslang | transloco) }\n }}</span>\n </button>\n</ng-template>\n\n<ng-template #labelSlot>\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow truncate\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n</ng-template>\n\n<ng-template #headerActionsSlot>\n @if (showFiltersCount() && filtersCount() > 0) {\n <Badge variant=\"number\" fill=\"primary\" scheme=\"sage\" size=\"xs\" class=\"ml-1\">\n {{ filtersCount() }}\n </Badge>\n }\n @if (canSearch() && !isCollapsed()) {\n <!-- The magnifier opens the search field of the facet (and closes it): the field is not on screen until then. It is\n in the header, next to the chevron, and only while the facet is open — there is nothing to search in a closed\n one. Interactive content in a summary does not toggle the details by itself, and the click is stopped anyway. -->\n <ng-container [ngTemplateOutlet]=\"searchToggleSlot\" />\n }\n @if (collapsible()) {\n <!-- The `<summary>` is out of the tab order (`tabindex=\"-1\"`), so this button is the keyboard way to\n collapse the facet. Its click bubbles to the `<summary>`, whose handler does the toggle, and it\n exposes `aria-expanded`. -->\n <button\n type=\"button\"\n variant=\"tertiary\"\n [iconOnly]=\"true\"\n size=\"sm\"\n [attr.aria-expanded]=\"!isCollapsed()\"\n [tooltip]=\"'filters.toggle' | transloco\"\n class=\"shrink-0 cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevron-right class=\"shrink-0\" />\n <span class=\"sr-only\">{{ \"filters.toggle\" | transloco }}</span>\n </button>\n }\n</ng-template>\n\n<ng-template #bodySlot>\n <div class=\"flex min-h-0 flex-1 flex-col\">\n @if (canSearch()) {\n <!-- The field is always in the DOM and folded to nothing until it is asked for: a grid row that goes from 0fr to 1fr is\n the one height animation that needs no measuring. `inert` takes the folded field out of the tab order and of the\n accessibility tree. The margins live in the inner box: a padding on the folded element itself would keep it\n from reaching zero.\n\n No `hotkey`: this field is one of many facet search boxes on the page, so it must not advertise a global shortcut.\n A placeholder is not a label (RGAA 11.1): the name says WHICH facet this box filters — the one thing that tells a\n dozen identical fields apart. SearchInput hands its class to its inner field, so a margin on it would count twice,\n which is one more reason for the padding on the box around it. -->\n <div\n [id]=\"searchId\"\n class=\"grid shrink-0 transition-[grid-template-rows,opacity] duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] motion-reduce:transition-none\"\n [class]=\"\n showSearch()\n ? 'grid-rows-[1fr] opacity-100'\n : 'grid-rows-[0fr] opacity-0'\n \"\n [inert]=\"!showSearch()\">\n <!-- `-mx-1 px-1`: the box clips what is outside it, and the focus ring of the field is drawn outside the field,\n so it needs a few pixels of room on each side — taken back by the negative margin so nothing moves. -->\n <div class=\"-mx-1 min-h-0 overflow-hidden px-1\">\n <div [class]=\"headless() ? 'pt-0.5 pb-1' : 'pt-2 pb-1'\">\n <SearchInput\n [placeholder]=\"'search' | transloco\"\n [clearLabel]=\"'filters.clearSearch' | transloco\"\n [aria-label]=\"\n 'filters.searchInFacet'\n | transloco\n : { facet: (aggregation()?.display | syslang | transloco) }\n \"\n [(value)]=\"searchText\"\n (keydown.escape)=\"onSearchEscape($event)\">\n <ng-content select=\"[search-addon]\" />\n </SearchInput>\n </div>\n </div>\n </div>\n }\n\n @if (itemsLength() > 0 || hasFilters() || selection()) {\n <!-- The bar under the search field, in a popover and in the filters drawer alike: clear and apply, and nothing else, so\n both keep their full text in the little room there is. Clear is a button with a text, not an icon, and stays in\n place (disabled) when there is nothing to clear. Apply only shows once something is pending, as it always did, and\n sits at the same level. -->\n <div\n data-bar=\"actions\"\n class=\"mb-1 flex shrink-0 items-center justify-end gap-1\">\n @if (headless() && canSearch()) {\n <ng-container [ngTemplateOutlet]=\"searchToggleSlot\" />\n }\n <button\n type=\"button\"\n variant=\"tertiary\"\n size=\"sm\"\n class=\"shrink-0\"\n [disabled]=\"!hasFilters() && selectedCount() === 0\"\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\n (click)=\"cleared.emit()\">\n {{ \"filters.clearAll\" | transloco }}\n </button>\n @if (selection()) {\n <!-- No icon: the room is short, and the word says it. -->\n <button\n type=\"button\"\n variant=\"accent\"\n size=\"sm\"\n class=\"shrink-0 overflow-hidden\"\n (click)=\"applied.emit()\">\n <span class=\"truncate\">{{ \"filters.apply\" | transloco }}</span>\n </button>\n }\n </div>\n }\n\n @if (itemsLength() > 0) {\n <!-- Select all is the first row of the list, written like one: the box lines up with the boxes of the items it drives and\n carries its own text, which a bare box next to two buttons never did. The whole row is the label, so it is as easy\n to hit as an item. The count of checked items is part of that same label (\"Select all (3)\"), so the two can never\n overlap: when the room is short the label is cut with an ellipsis as a whole. It stays out of the scroller of the list, so it does not scroll away with the items. -->\n <div\n data-bar=\"select-all\"\n class=\"mb-1 flex shrink-0 items-center pr-3 pl-1.5 text-sm\">\n <checkbox\n class=\"min-w-0 flex-1 items-center py-1 [&_.checkbox-label]:truncate [&_.checkbox-text-block]:min-w-0\"\n [checked]=\"isAllSelected()\"\n [indeterminate]=\"selectedCount() > 0 && !isAllSelected()\"\n (checkedChange)=\"$event ? allSelected.emit() : allUnselected.emit()\">\n {{\n (isAllSelected() ? \"filters.unselectAll\" : \"filters.selectAll\")\n | transloco\n }}\n @if (selectedCount() > 0) {\n <span class=\"text-(--font-primary-muted)\"\n >({{ selectedCount() }})</span\n >\n }\n </checkbox>\n <span class=\"sr-only\" aria-live=\"polite\">{{\n \"filters.selectedCount\" | transloco: { count: selectedCount() }\n }}</span>\n </div>\n }\n\n <ng-content />\n\n @if (hasMore() && searchedItemsLength() === 0) {\n <button\n type=\"button\"\n class=\"mt-1 flex w-full shrink-0 justify-center\"\n [attr.aria-label]=\"'loadMore' | transloco\"\n (click)=\"loadedMore.emit()\">\n {{ \"loadMore\" | transloco }}\n </button>\n }\n </div>\n</ng-template>\n\n@if (headless()) {\n <div class=\"flex min-h-0 flex-1 flex-col p-0.5\">\n <ng-container [ngTemplateOutlet]=\"bodySlot\" />\n </div>\n} @else {\n <details\n [attr.open]=\"expanded()\"\n [attr.name]=\"id()\"\n class=\"group flex min-h-0 flex-1 flex-col p-0.5 details-content:flex details-content:min-h-0 details-content:flex-col\"\n (toggle)=\"onToggle($event)\">\n <summary\n tabindex=\"-1\"\n [class.cursor-pointer]=\"collapsible() && !isEmpty()\"\n [class]=\"\n (isEmpty() ? 'text-(--font-neutral-muted) ' : '') + rowStateClass\n \"\n class=\"m-0 flex h-8 w-full shrink-0 scroll-mt-16 scroll-mb-3 items-center gap-1 pl-1 font-semibold select-none\"\n (click)=\"onHeaderClick($event)\">\n <ng-container [ngTemplateOutlet]=\"labelSlot\" />\n <ng-container [ngTemplateOutlet]=\"headerActionsSlot\" />\n </summary>\n <ng-container [ngTemplateOutlet]=\"bodySlot\" />\n </details>\n}\n",
|
|
25161
|
+
styles: [":host{interpolate-size:allow-keywords}details::details-content{flex:1 1 auto;block-size:0;overflow:clip;overflow-clip-margin:.25rem;transition:block-size .2s cubic-bezier(.32,.72,0,1),content-visibility .2s allow-discrete}details[open]::details-content{block-size:auto}@media(prefers-reduced-motion:reduce){details::details-content{transition:none}}\n"],
|
|
25045
25162
|
dependencies: [
|
|
25046
25163
|
{
|
|
25047
25164
|
kind: "directive",
|
|
@@ -25084,6 +25201,12 @@ var AggregationPanelComponent = class AggregationPanelComponent {
|
|
|
25084
25201
|
selector: "chevron-right, chevron-right-icon, ChevronRight, ChevronRightIcon",
|
|
25085
25202
|
inputs: ["class"]
|
|
25086
25203
|
},
|
|
25204
|
+
{
|
|
25205
|
+
kind: "component",
|
|
25206
|
+
type: SearchIcon,
|
|
25207
|
+
selector: "search, search-icon, Search, SearchIcon",
|
|
25208
|
+
inputs: ["class"]
|
|
25209
|
+
},
|
|
25087
25210
|
{
|
|
25088
25211
|
kind: "component",
|
|
25089
25212
|
type: SearchInputComponent,
|
|
@@ -25164,6 +25287,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
25164
25287
|
TranslocoPipe,
|
|
25165
25288
|
BadgeComponent,
|
|
25166
25289
|
ChevronRightIcon,
|
|
25290
|
+
SearchIcon,
|
|
25167
25291
|
SearchInputComponent,
|
|
25168
25292
|
FaIconComponent,
|
|
25169
25293
|
CheckboxComponent,
|
|
@@ -25171,10 +25295,10 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
25171
25295
|
],
|
|
25172
25296
|
providers: [provideTranslocoScope("filters")],
|
|
25173
25297
|
host: { class: "flex min-h-0 flex-1 flex-col" },
|
|
25174
|
-
template: "<!-- `<details>`/`<summary>` stay native (open/close, the `name`-based exclusive accordion): a facet is\n a disclosure, not a grid row. They used to sit in an `@angular/aria` grid so ArrowUp/ArrowDown could\n jump from one facet header to the next, but a grid without headers reads as a layout table\n (RGAA 5.3). The `<summary>` itself is `tabindex=\"-1\"`: Tab walks the header's chevron, never the header row, and the\n chevron is the keyboard way to collapse. Select all, clear and apply are not in the header: they are in the body, in a\n bar under the search field. Label, header actions (the count and the chevron) and body are `<ng-template>`s: `<ng-content select=\"…\">` can only be\n declared once, and the body is projected content. -->\n\n<ng-template #labelSlot>\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow truncate\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n</ng-template>\n\n<ng-template #headerActionsSlot>\n @if (showFiltersCount() && filtersCount() > 0) {\n <Badge variant=\"number\" fill=\"primary\" scheme=\"sage\" size=\"xs\" class=\"ml-1\">\n {{ filtersCount() }}\n </Badge>\n }\n @if (collapsible()) {\n <!-- The `<summary>` is out of the tab order (`tabindex=\"-1\"`), so this button is the keyboard way to\n collapse the facet. Its click bubbles to the `<summary>`, whose handler does the toggle, and it\n exposes `aria-expanded`. -->\n <button\n type=\"button\"\n variant=\"tertiary\"\n [iconOnly]=\"true\"\n size=\"sm\"\n [attr.aria-expanded]=\"!isCollapsed()\"\n [tooltip]=\"'filters.toggle' | transloco\"\n class=\"shrink-0 cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevron-right class=\"shrink-0\" />\n <span class=\"sr-only\">{{ \"filters.toggle\" | transloco }}</span>\n </button>\n }\n</ng-template>\n\n<ng-template #bodySlot>\n <div class=\"flex min-h-0 flex-1 flex-col\">\n @if (
|
|
25298
|
+
template: "<!-- `<details>`/`<summary>` stay native (open/close, the `name`-based exclusive accordion): a facet is\n a disclosure, not a grid row. They used to sit in an `@angular/aria` grid so ArrowUp/ArrowDown could\n jump from one facet header to the next, but a grid without headers reads as a layout table\n (RGAA 5.3). The `<summary>` itself is `tabindex=\"-1\"`: Tab walks the header's chevron, never the header row, and the\n chevron is the keyboard way to collapse. Select all, clear and apply are not in the header: they are in the body, in a\n bar under the search field. Label, header actions (the count and the chevron) and body are `<ng-template>`s: `<ng-content select=\"…\">` can only be\n declared once, and the body is projected content. -->\n\n<ng-template #searchToggleSlot>\n <button\n #searchToggle\n type=\"button\"\n variant=\"tertiary\"\n [iconOnly]=\"true\"\n size=\"sm\"\n class=\"shrink-0 cursor-pointer\"\n [attr.aria-expanded]=\"showSearch()\"\n [attr.aria-controls]=\"searchId\"\n [tooltip]=\"\n 'filters.searchInFacet'\n | transloco: { facet: (aggregation()?.display | syslang | transloco) }\n \"\n (click)=\"toggleSearch($event)\">\n <search-icon class=\"shrink-0\" />\n <span class=\"sr-only\">{{\n \"filters.searchInFacet\"\n | transloco: { facet: (aggregation()?.display | syslang | transloco) }\n }}</span>\n </button>\n</ng-template>\n\n<ng-template #labelSlot>\n <ng-content select=\"label\">\n @let icon = aggregation()?.icon;\n @if (icon) {\n <FaIcon [faClass]=\"icon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow truncate\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n</ng-template>\n\n<ng-template #headerActionsSlot>\n @if (showFiltersCount() && filtersCount() > 0) {\n <Badge variant=\"number\" fill=\"primary\" scheme=\"sage\" size=\"xs\" class=\"ml-1\">\n {{ filtersCount() }}\n </Badge>\n }\n @if (canSearch() && !isCollapsed()) {\n <!-- The magnifier opens the search field of the facet (and closes it): the field is not on screen until then. It is\n in the header, next to the chevron, and only while the facet is open — there is nothing to search in a closed\n one. Interactive content in a summary does not toggle the details by itself, and the click is stopped anyway. -->\n <ng-container [ngTemplateOutlet]=\"searchToggleSlot\" />\n }\n @if (collapsible()) {\n <!-- The `<summary>` is out of the tab order (`tabindex=\"-1\"`), so this button is the keyboard way to\n collapse the facet. Its click bubbles to the `<summary>`, whose handler does the toggle, and it\n exposes `aria-expanded`. -->\n <button\n type=\"button\"\n variant=\"tertiary\"\n [iconOnly]=\"true\"\n size=\"sm\"\n [attr.aria-expanded]=\"!isCollapsed()\"\n [tooltip]=\"'filters.toggle' | transloco\"\n class=\"shrink-0 cursor-pointer [&_svg]:transition-transform [&_svg]:duration-150 group-open:[&_svg]:rotate-90\">\n <chevron-right class=\"shrink-0\" />\n <span class=\"sr-only\">{{ \"filters.toggle\" | transloco }}</span>\n </button>\n }\n</ng-template>\n\n<ng-template #bodySlot>\n <div class=\"flex min-h-0 flex-1 flex-col\">\n @if (canSearch()) {\n <!-- The field is always in the DOM and folded to nothing until it is asked for: a grid row that goes from 0fr to 1fr is\n the one height animation that needs no measuring. `inert` takes the folded field out of the tab order and of the\n accessibility tree. The margins live in the inner box: a padding on the folded element itself would keep it\n from reaching zero.\n\n No `hotkey`: this field is one of many facet search boxes on the page, so it must not advertise a global shortcut.\n A placeholder is not a label (RGAA 11.1): the name says WHICH facet this box filters — the one thing that tells a\n dozen identical fields apart. SearchInput hands its class to its inner field, so a margin on it would count twice,\n which is one more reason for the padding on the box around it. -->\n <div\n [id]=\"searchId\"\n class=\"grid shrink-0 transition-[grid-template-rows,opacity] duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] motion-reduce:transition-none\"\n [class]=\"\n showSearch()\n ? 'grid-rows-[1fr] opacity-100'\n : 'grid-rows-[0fr] opacity-0'\n \"\n [inert]=\"!showSearch()\">\n <!-- `-mx-1 px-1`: the box clips what is outside it, and the focus ring of the field is drawn outside the field,\n so it needs a few pixels of room on each side — taken back by the negative margin so nothing moves. -->\n <div class=\"-mx-1 min-h-0 overflow-hidden px-1\">\n <div [class]=\"headless() ? 'pt-0.5 pb-1' : 'pt-2 pb-1'\">\n <SearchInput\n [placeholder]=\"'search' | transloco\"\n [clearLabel]=\"'filters.clearSearch' | transloco\"\n [aria-label]=\"\n 'filters.searchInFacet'\n | transloco\n : { facet: (aggregation()?.display | syslang | transloco) }\n \"\n [(value)]=\"searchText\"\n (keydown.escape)=\"onSearchEscape($event)\">\n <ng-content select=\"[search-addon]\" />\n </SearchInput>\n </div>\n </div>\n </div>\n }\n\n @if (itemsLength() > 0 || hasFilters() || selection()) {\n <!-- The bar under the search field, in a popover and in the filters drawer alike: clear and apply, and nothing else, so\n both keep their full text in the little room there is. Clear is a button with a text, not an icon, and stays in\n place (disabled) when there is nothing to clear. Apply only shows once something is pending, as it always did, and\n sits at the same level. -->\n <div\n data-bar=\"actions\"\n class=\"mb-1 flex shrink-0 items-center justify-end gap-1\">\n @if (headless() && canSearch()) {\n <ng-container [ngTemplateOutlet]=\"searchToggleSlot\" />\n }\n <button\n type=\"button\"\n variant=\"tertiary\"\n size=\"sm\"\n class=\"shrink-0\"\n [disabled]=\"!hasFilters() && selectedCount() === 0\"\n [attr.aria-label]=\"'filters.clearFilters' | transloco\"\n (click)=\"cleared.emit()\">\n {{ \"filters.clearAll\" | transloco }}\n </button>\n @if (selection()) {\n <!-- No icon: the room is short, and the word says it. -->\n <button\n type=\"button\"\n variant=\"accent\"\n size=\"sm\"\n class=\"shrink-0 overflow-hidden\"\n (click)=\"applied.emit()\">\n <span class=\"truncate\">{{ \"filters.apply\" | transloco }}</span>\n </button>\n }\n </div>\n }\n\n @if (itemsLength() > 0) {\n <!-- Select all is the first row of the list, written like one: the box lines up with the boxes of the items it drives and\n carries its own text, which a bare box next to two buttons never did. The whole row is the label, so it is as easy\n to hit as an item. The count of checked items is part of that same label (\"Select all (3)\"), so the two can never\n overlap: when the room is short the label is cut with an ellipsis as a whole. It stays out of the scroller of the list, so it does not scroll away with the items. -->\n <div\n data-bar=\"select-all\"\n class=\"mb-1 flex shrink-0 items-center pr-3 pl-1.5 text-sm\">\n <checkbox\n class=\"min-w-0 flex-1 items-center py-1 [&_.checkbox-label]:truncate [&_.checkbox-text-block]:min-w-0\"\n [checked]=\"isAllSelected()\"\n [indeterminate]=\"selectedCount() > 0 && !isAllSelected()\"\n (checkedChange)=\"$event ? allSelected.emit() : allUnselected.emit()\">\n {{\n (isAllSelected() ? \"filters.unselectAll\" : \"filters.selectAll\")\n | transloco\n }}\n @if (selectedCount() > 0) {\n <span class=\"text-(--font-primary-muted)\"\n >({{ selectedCount() }})</span\n >\n }\n </checkbox>\n <span class=\"sr-only\" aria-live=\"polite\">{{\n \"filters.selectedCount\" | transloco: { count: selectedCount() }\n }}</span>\n </div>\n }\n\n <ng-content />\n\n @if (hasMore() && searchedItemsLength() === 0) {\n <button\n type=\"button\"\n class=\"mt-1 flex w-full shrink-0 justify-center\"\n [attr.aria-label]=\"'loadMore' | transloco\"\n (click)=\"loadedMore.emit()\">\n {{ \"loadMore\" | transloco }}\n </button>\n }\n </div>\n</ng-template>\n\n@if (headless()) {\n <div class=\"flex min-h-0 flex-1 flex-col p-0.5\">\n <ng-container [ngTemplateOutlet]=\"bodySlot\" />\n </div>\n} @else {\n <details\n [attr.open]=\"expanded()\"\n [attr.name]=\"id()\"\n class=\"group flex min-h-0 flex-1 flex-col p-0.5 details-content:flex details-content:min-h-0 details-content:flex-col\"\n (toggle)=\"onToggle($event)\">\n <summary\n tabindex=\"-1\"\n [class.cursor-pointer]=\"collapsible() && !isEmpty()\"\n [class]=\"\n (isEmpty() ? 'text-(--font-neutral-muted) ' : '') + rowStateClass\n \"\n class=\"m-0 flex h-8 w-full shrink-0 scroll-mt-16 scroll-mb-3 items-center gap-1 pl-1 font-semibold select-none\"\n (click)=\"onHeaderClick($event)\">\n <ng-container [ngTemplateOutlet]=\"labelSlot\" />\n <ng-container [ngTemplateOutlet]=\"headerActionsSlot\" />\n </summary>\n <ng-container [ngTemplateOutlet]=\"bodySlot\" />\n </details>\n}\n",
|
|
25299
|
+
styles: [":host{interpolate-size:allow-keywords}details::details-content{flex:1 1 auto;block-size:0;overflow:clip;overflow-clip-margin:.25rem;transition:block-size .2s cubic-bezier(.32,.72,0,1),content-visibility .2s allow-discrete}details[open]::details-content{block-size:auto}@media(prefers-reduced-motion:reduce){details::details-content{transition:none}}\n"]
|
|
25175
25300
|
}]
|
|
25176
25301
|
}],
|
|
25177
|
-
ctorParameters: () => [],
|
|
25178
25302
|
propDecorators: {
|
|
25179
25303
|
id: [{
|
|
25180
25304
|
type: i0.Input,
|
|
@@ -25338,6 +25462,10 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
25338
25462
|
searchBox: [{
|
|
25339
25463
|
type: i0.ViewChild,
|
|
25340
25464
|
args: [i0.forwardRef(() => SearchInputComponent), { isSignal: true }]
|
|
25465
|
+
}],
|
|
25466
|
+
searchToggle: [{
|
|
25467
|
+
type: i0.ViewChild,
|
|
25468
|
+
args: ["searchToggle", { isSignal: true }]
|
|
25341
25469
|
}]
|
|
25342
25470
|
}
|
|
25343
25471
|
});
|
|
@@ -26038,7 +26166,7 @@ var AggregationTreeComponent = class AggregationTreeComponent {
|
|
|
26038
26166
|
isSignal: true
|
|
26039
26167
|
}],
|
|
26040
26168
|
ngImport: i0,
|
|
26041
|
-
template: "@if (!aggregation()?.isTree) {\n <div role=\"alert\" class=\"p-2 text-sm text-red-500\">\n <triangle-alert-icon class=\"mr-1 shrink-0\" />\n The aggregationTree component does not support list aggregations. Please use\n the <Aggregation /> component instead.\n </div>\n}\n<aggregation-panel\n [id]=\"id()\"\n [collapsible]=\"collapsible()\"\n [collapsed]=\"collapsed()\"\n [isEmpty]=\"isEmpty()\"\n [isDate]=\"isDate()\"\n [aggregation]=\"aggregation()\"\n [showFiltersCount]=\"showFiltersCount()\"\n [filtersCount]=\"filtersCount()\"\n [hasFilters]=\"hasFilters()\"\n [selection]=\"selection()\"\n [isAllSelected]=\"isAllSelected()\"\n [headless]=\"headless()\"\n [selectedCount]=\"selected().length\"\n [(searchText)]=\"searchText\"\n [itemsLength]=\"items().length\"\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\n [searchedItemsLength]=\"searchedItems().length\"\n (cleared)=\"clear()\"\n (applied)=\"apply()\"\n (allSelected)=\"selectAll()\"\n (allUnselected)=\"unselectAll()\"\n (loadedMore)=\"loadMore()\">\n <!-- Relay the host's `label` projection into the panel's `label` slot. When the\n host projects nothing, the local <ng-content> falls back to the icon + display\n (mirrors AggregationPanel's own fallback), so the header is never empty. -->\n <ng-container ngProjectAs=\"label\">\n <ng-content select=\"label\">\n @let labelIcon = aggregation()?.icon;\n @if (labelIcon) {\n <FaIcon [faClass]=\"labelIcon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow truncate\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n </ng-container>\n <div class=\"min-h-0 w-full flex-1 scrollbar-thin overflow-auto pb-3\">\n <ul\n ngTree\n #tree=\"ngTree\"\n [multi]=\"true\"\n [(value)]=\"selected\"\n (valueChange)=\"onSelectionChange($event)\"\n /* Translated like the flat list's own label (aggregation-list.html): this was the raw\n `display`, so the accessible name of a whole facet came out in the source language.\n The pipes apply to the whole nullish chain, `|` being the loosest operator Angular\n parses — no parentheses needed. */\n [attr.aria-label]=\"aggregation()?.display ?? name() | syslang | transloco\"\n class=\"basic-tree m-1\">\n <ng-template\n [ngTemplateOutlet]=\"treeNodes\"\n [ngTemplateOutletContext]=\"{ nodes: nodes(), parent: tree }\" />\n </ul>\n </div>\n</aggregation-panel>\n\n<ng-template #treeNodes let-nodes=\"nodes\" let-parent=\"parent\">\n @for (node of nodes; track node.$path) {\n @let label = node.display ?? node.value ?? \"\";\n <li\n ngTreeItem\n #treeItem=\"ngTreeItem\"\n [parent]=\"parent\"\n [value]=\"node.$path\"\n [label]=\"label\"\n [disabled]=\"node.disabled\"\n [(expanded)]=\"node.$opened\"\n (expandedChange)=\"onExpandedChange(node, $event)\"\n [attr.aria-label]=\"label\"\n [attr.aria-describedby]=\"\n nodeDescription(node) ? descriptionId(node.$path) : null\n \"\n [class]=\"cn('m-0 block w-full p-0', itemStateClass)\"\n (keydown.enter)=\"onTreeItemEnterKey(node, $event)\"\n (keydown.space)=\"onTreeItemSpaceKey(node, $event)\">\n <!-- 2. Ce div prend tout l'espace et intercepte tous les clics de la ligne. `ps-0.5`: the checkbox of a list option sits\n 2px further in than a tree's (the option has its own padding), so the checkboxes of the two facets, and the select-all of\n the bar of a popover, are on the same vertical line. -->\n <div\n class=\"flex w-full cursor-pointer items-center gap-2 py-0.5 ps-0.5 text-sm leading-6\"\n (click)=\"onTreeItemRowClick(node, $event)\"\n (mousedown)=\"$event.stopPropagation()\"\n (pointerdown)=\"$event.stopPropagation()\">\n @if (showCheckbox()) {\n <!-- `inert` keeps the native input out of the tab order and out of hit-testing:\n a role=\"treeitem\" admits no focusable descendant, and the click must reach\n the row handler above. Selection is announced by the <li>'s aria-selected. -->\n <!-- --spacing-xs: 0 kills the gap the checkbox keeps between its box and its\n (here empty) text block. Set as a custom property rather than a utility\n class: the CVA already sets gap-(--spacing-xs), and two same-specificity\n classes would be resolved by stylesheet order, not by us. -->\n <span\n inert\n aria-hidden=\"true\"\n class=\"inline-flex shrink-0\"\n style=\"--spacing-xs: 0\">\n <checkbox\n [checked]=\"treeItem.selected() || node.$selectedVisually || false\"\n [disabled]=\"node.disabled\"\n [class]=\"\n cn(\n 'pointer-events-none',\n (treeItem.selected() || node.$selectedVisually) &&\n 'selected-icon'\n )\n \" />\n </span>\n }\n <!-- Depth indent sits on the chevron, after the checkbox: the checkboxes of every\n level stay flush left (aligned with a flat list's), and since this adds no extra\n flex child, the step between consecutive levels is exactly one indent. -->\n @let depthIndent =\n \"calc(var(--agg-tree-indent, 0.5rem) * \" +\n (treeItem.level() - 1) +\n \")\";\n @if (node.hasChildren) {\n <!-- Expand/collapse affordance for the mouse only, hence a <span> and not a <button>:\n the keyboard already expands with ArrowRight / ArrowLeft, handled by ngTreeItem, and\n a role=\"treeitem\" admits no focusable descendant — so this was a <button> nobody\n could reach, already carrying aria-hidden and tabindex=\"-1\" to say so. Every audit\n reads it as a button with no accessible name (RGAA 7.1, 11.9). Same shape as the leaf\n branch below. -->\n <span\n aria-hidden=\"true\"\n (click)=\"toggleTreeNode(treeItem, $event)\"\n [style.marginInlineStart]=\"depthIndent\"\n class=\"inline-flex cursor-pointer items-center justify-center\">\n <ChevronRightIcon\n [class]=\"\n cn('expand-icon shrink-0', node.$opened && 'rotate-90')\n \" />\n <FolderIcon class=\"shrink-0\" />\n </span>\n } @else {\n <span\n aria-hidden=\"true\"\n [style.marginInlineStart]=\"depthIndent\"\n class=\"inline-flex items-center justify-center\">\n <!-- Reserves the chevron's own width (`.expand-icon` is a styling hook with\n no rule of its own), so a leaf lines up with its parent nodes. -->\n <span class=\"expand-icon w-4 shrink-0\"></span>\n <FileIcon class=\"size-4 shrink-0\" />\n </span>\n }\n @if (resolvedItemTemplate(); as tpl) {\n <span\n class=\"grow truncate select-none\"\n (click)=\"onTreeItemTextClick(node, $event)\">\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: node,\n field: aggregation()?.column,\n selected: treeItem.selected() ?? false,\n count: node.count,\n name: label,\n searchText: searchText(),\n level: node.$level ?? 0,\n expanded: node.$opened ?? false,\n hasChildren: node.hasChildren ?? false\n }\" />\n </span>\n } @else {\n <!-- ce span est essentiel pour le style -->\n @let appliesOnLabelClick = quickFilter() || applyOnSelect();\n <span\n [class]=\"\n cn(\n 'grow truncate select-none',\n appliesOnLabelClick && 'hover:underline'\n )\n \"\n [title]=\"\n appliesOnLabelClick\n ? ((isTreeItemFiltered(node)\n ? 'filters.removeFilter'\n : 'filters.addFilter'\n ) | transloco) +\n ': ' +\n label\n : label\n \"\n (click)=\"onTreeItemTextClick(node, $event)\">\n @for (\n chunk of label | highlightWord: searchText() : 10;\n track $index\n ) {\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\n chunk.text\n }}</span>\n }\n </span>\n @if (showCount() && node.count > 0) {\n <span class=\"ml-auto px-1 text-xs\" aria-hidden=\"true\">{{\n node.count\n }}</span>\n }\n }\n @let description = nodeDescription(node);\n @if (description) {\n <!-- `hidden`, not `sr-only`: read through aria-describedby only. A visually-hidden\n span would also be counted in the treeitem's name (computed from its content). -->\n <span hidden [id]=\"descriptionId(node.$path)\">{{ description }}</span>\n }\n </div>\n </li>\n @if (node.hasChildren) {\n <!-- No indentation here: it would shift the checkbox too. Depth is applied\n inside the row, after the checkbox, so every checkbox stays flush left. -->\n <!-- The <ul> sits INSIDE the deferred content, not around it: `ngTreeItemGroup` only\n instantiates its view when the node opens, so a <ul> declared outside stayed in the DOM,\n empty, for every collapsed node — a structural tag with nothing to structure (RGAA 8.9).\n The @if cannot move onto the <ul> either: the directive registers itself with its tree\n item on init and has to exist for as long as the item does. -->\n <ng-template\n ngTreeItemGroup\n [ownedBy]=\"treeItem\"\n #group=\"ngTreeItemGroup\">\n <ul role=\"group\">\n <ng-template\n [ngTemplateOutlet]=\"treeNodes\"\n [ngTemplateOutletContext]=\"{ nodes: node.items, parent: group }\" />\n </ul>\n </ng-template>\n }\n }\n</ng-template>\n",
|
|
26169
|
+
template: "@if (!aggregation()?.isTree) {\n <div role=\"alert\" class=\"p-2 text-sm text-red-500\">\n <triangle-alert-icon class=\"mr-1 shrink-0\" />\n The aggregationTree component does not support list aggregations. Please use\n the <Aggregation /> component instead.\n </div>\n}\n<aggregation-panel\n [id]=\"id()\"\n [collapsible]=\"collapsible()\"\n [collapsed]=\"collapsed()\"\n [isEmpty]=\"isEmpty()\"\n [isDate]=\"isDate()\"\n [aggregation]=\"aggregation()\"\n [showFiltersCount]=\"showFiltersCount()\"\n [filtersCount]=\"filtersCount()\"\n [hasFilters]=\"hasFilters()\"\n [selection]=\"selection()\"\n [isAllSelected]=\"isAllSelected()\"\n [headless]=\"headless()\"\n [selectedCount]=\"selected().length\"\n [(searchText)]=\"searchText\"\n [itemsLength]=\"items().length\"\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\n [searchedItemsLength]=\"searchedItems().length\"\n (cleared)=\"clear()\"\n (applied)=\"apply()\"\n (allSelected)=\"selectAll()\"\n (allUnselected)=\"unselectAll()\"\n (loadedMore)=\"loadMore()\">\n <!-- Relay the host's `label` projection into the panel's `label` slot. When the\n host projects nothing, the local <ng-content> falls back to the icon + display\n (mirrors AggregationPanel's own fallback), so the header is never empty. -->\n <ng-container ngProjectAs=\"label\">\n <ng-content select=\"label\">\n @let labelIcon = aggregation()?.icon;\n @if (labelIcon) {\n <FaIcon [faClass]=\"labelIcon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow truncate\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n </ng-container>\n <div class=\"min-h-0 w-full flex-1 scrollbar-thin overflow-auto pb-3\">\n <ul\n ngTree\n #tree=\"ngTree\"\n [multi]=\"true\"\n [(value)]=\"selected\"\n (valueChange)=\"onSelectionChange($event)\"\n /* Translated like the flat list's own label (aggregation-list.html): this was the raw\n `display`, so the accessible name of a whole facet came out in the source language.\n The pipes apply to the whole nullish chain, `|` being the loosest operator Angular\n parses — no parentheses needed. */\n [attr.aria-label]=\"aggregation()?.display ?? name() | syslang | transloco\"\n class=\"basic-tree m-1\">\n <ng-template\n [ngTemplateOutlet]=\"treeNodes\"\n [ngTemplateOutletContext]=\"{ nodes: nodes(), parent: tree }\" />\n </ul>\n </div>\n</aggregation-panel>\n\n<ng-template #treeNodes let-nodes=\"nodes\" let-parent=\"parent\">\n @for (node of nodes; track node.$path) {\n @let label = node.display ?? node.value ?? \"\";\n <li\n ngTreeItem\n #treeItem=\"ngTreeItem\"\n [parent]=\"parent\"\n [value]=\"node.$path\"\n [label]=\"label\"\n [disabled]=\"node.disabled\"\n [(expanded)]=\"node.$opened\"\n (expandedChange)=\"onExpandedChange(node, $event)\"\n [attr.aria-label]=\"label\"\n [attr.aria-describedby]=\"\n nodeDescription(node) ? descriptionId(node.$path) : null\n \"\n [class]=\"cn('m-0 block w-full p-0', itemStateClass)\"\n (keydown.enter)=\"onTreeItemEnterKey(node, $event)\"\n (keydown.space)=\"onTreeItemSpaceKey(node, $event)\">\n <!-- 2. Ce div prend tout l'espace et intercepte tous les clics de la ligne. `ps-0.5`: the checkbox of a list option sits\n 2px further in than a tree's (the option has its own padding), so the checkboxes of the two facets, and the select-all of\n the bar of a popover, are on the same vertical line. -->\n <div\n class=\"flex w-full cursor-pointer items-center gap-2 py-0.5 ps-0.5 text-sm leading-6\"\n (click)=\"onTreeItemRowClick(node, $event)\"\n (mousedown)=\"$event.stopPropagation()\"\n (pointerdown)=\"$event.stopPropagation()\">\n @if (showCheckbox()) {\n <!-- `inert` keeps the native input out of the tab order and out of hit-testing:\n a role=\"treeitem\" admits no focusable descendant, and the click must reach\n the row handler above. Selection is announced by the <li>'s aria-selected. -->\n <!-- --spacing-xs: 0 kills the gap the checkbox keeps between its box and its\n (here empty) text block. Set as a custom property rather than a utility\n class: the CVA already sets gap-(--spacing-xs), and two same-specificity\n classes would be resolved by stylesheet order, not by us. -->\n <span\n inert\n aria-hidden=\"true\"\n class=\"inline-flex shrink-0\"\n style=\"--spacing-xs: 0\">\n <checkbox\n [checked]=\"treeItem.selected() || node.$selectedVisually || false\"\n [disabled]=\"node.disabled\"\n [class]=\"\n cn(\n 'pointer-events-none',\n (treeItem.selected() || node.$selectedVisually) &&\n 'selected-icon'\n )\n \" />\n </span>\n }\n <!-- Depth indent sits on the chevron, after the checkbox: the checkboxes of every\n level stay flush left (aligned with a flat list's), and since this adds no extra\n flex child, the step between consecutive levels is exactly one indent. -->\n @let depthIndent =\n \"calc(var(--agg-tree-indent, 0.5rem) * \" +\n (treeItem.level() - 1) +\n \")\";\n @if (node.hasChildren) {\n <!-- Expand/collapse affordance for the mouse only, hence a <span> and not a <button>:\n the keyboard already expands with ArrowRight / ArrowLeft, handled by ngTreeItem, and\n a role=\"treeitem\" admits no focusable descendant — so this was a <button> nobody\n could reach, already carrying aria-hidden and tabindex=\"-1\" to say so. Every audit\n reads it as a button with no accessible name (RGAA 7.1, 11.9). Same shape as the leaf\n branch below. -->\n <span\n aria-hidden=\"true\"\n (click)=\"toggleTreeNode(treeItem, $event)\"\n [style.marginInlineStart]=\"depthIndent\"\n class=\"inline-flex cursor-pointer items-center justify-center\">\n <ChevronRightIcon\n [class]=\"\n cn('expand-icon shrink-0', node.$opened && 'rotate-90')\n \" />\n <FolderIcon class=\"shrink-0\" />\n </span>\n } @else {\n <span\n aria-hidden=\"true\"\n [style.marginInlineStart]=\"depthIndent\"\n class=\"inline-flex items-center justify-center\">\n <!-- Reserves the chevron's own width (`.expand-icon` is a styling hook with\n no rule of its own), so a leaf lines up with its parent nodes. -->\n <span class=\"expand-icon w-4 shrink-0\"></span>\n <FileIcon class=\"size-4 shrink-0\" />\n </span>\n }\n @if (resolvedItemTemplate(); as tpl) {\n <!-- Cut off, the label shows whole in a tooltip; a label that fits needs none. -->\n <span\n class=\"grow truncate select-none\"\n truncated\n #cutCustom=\"truncated\"\n [tooltip]=\"label\"\n [tooltipDisabled]=\"!cutCustom.isTruncated()\"\n (click)=\"onTreeItemTextClick(node, $event)\">\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: node,\n field: aggregation()?.column,\n selected: treeItem.selected() ?? false,\n count: node.count,\n name: label,\n searchText: searchText(),\n level: node.$level ?? 0,\n expanded: node.$opened ?? false,\n hasChildren: node.hasChildren ?? false\n }\" />\n </span>\n } @else {\n <!-- ce span est essentiel pour le style -->\n @let appliesOnLabelClick = quickFilter() || applyOnSelect();\n <!-- Cut off, the label shows whole in a tooltip (`truncated` measures it); a label that fits has none. -->\n <span\n [class]=\"\n cn(\n 'grow truncate select-none',\n appliesOnLabelClick && 'hover:underline'\n )\n \"\n truncated\n #cut=\"truncated\"\n [tooltip]=\"label\"\n [tooltipDisabled]=\"!cut.isTruncated()\"\n (click)=\"onTreeItemTextClick(node, $event)\">\n @for (\n chunk of label | highlightWord: searchText() : 10;\n track $index\n ) {\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\n chunk.text\n }}</span>\n }\n </span>\n @if (showCount() && node.count > 0) {\n <span class=\"ml-auto px-1 text-xs\" aria-hidden=\"true\">{{\n node.count\n }}</span>\n }\n }\n @let description = nodeDescription(node);\n @if (description) {\n <!-- `hidden`, not `sr-only`: read through aria-describedby only. A visually-hidden\n span would also be counted in the treeitem's name (computed from its content). -->\n <span hidden [id]=\"descriptionId(node.$path)\">{{ description }}</span>\n }\n </div>\n </li>\n @if (node.hasChildren) {\n <!-- No indentation here: it would shift the checkbox too. Depth is applied\n inside the row, after the checkbox, so every checkbox stays flush left. -->\n <!-- The <ul> sits INSIDE the deferred content, not around it: `ngTreeItemGroup` only\n instantiates its view when the node opens, so a <ul> declared outside stayed in the DOM,\n empty, for every collapsed node — a structural tag with nothing to structure (RGAA 8.9).\n The @if cannot move onto the <ul> either: the directive registers itself with its tree\n item on init and has to exist for as long as the item does. -->\n <ng-template\n ngTreeItemGroup\n [ownedBy]=\"treeItem\"\n #group=\"ngTreeItemGroup\">\n <ul role=\"group\">\n <ng-template\n [ngTemplateOutlet]=\"treeNodes\"\n [ngTemplateOutletContext]=\"{ nodes: node.items, parent: group }\" />\n </ul>\n </ng-template>\n }\n }\n</ng-template>\n",
|
|
26042
26170
|
dependencies: [
|
|
26043
26171
|
{
|
|
26044
26172
|
kind: "directive",
|
|
@@ -26088,12 +26216,45 @@ var AggregationTreeComponent = class AggregationTreeComponent {
|
|
|
26088
26216
|
selector: "folder, folder-icon, Folder, FolderIcon",
|
|
26089
26217
|
inputs: ["class"]
|
|
26090
26218
|
},
|
|
26219
|
+
{
|
|
26220
|
+
kind: "directive",
|
|
26221
|
+
type: TooltipDirective,
|
|
26222
|
+
selector: "[tooltip]",
|
|
26223
|
+
inputs: [
|
|
26224
|
+
"tooltip",
|
|
26225
|
+
"tooltipPosition",
|
|
26226
|
+
"tooltipEvent",
|
|
26227
|
+
"showDelay",
|
|
26228
|
+
"hideDelay",
|
|
26229
|
+
"life",
|
|
26230
|
+
"tooltipOffset",
|
|
26231
|
+
"autoHide",
|
|
26232
|
+
"hideOnEscape",
|
|
26233
|
+
"escape",
|
|
26234
|
+
"tooltipDisabled",
|
|
26235
|
+
"tooltipArrow",
|
|
26236
|
+
"tooltipStyleClass",
|
|
26237
|
+
"tooltipOptions",
|
|
26238
|
+
"tooltip-position",
|
|
26239
|
+
"tooltip-delay",
|
|
26240
|
+
"tooltip-duration",
|
|
26241
|
+
"tooltip-offset"
|
|
26242
|
+
],
|
|
26243
|
+
exportAs: ["tooltip"]
|
|
26244
|
+
},
|
|
26091
26245
|
{
|
|
26092
26246
|
kind: "component",
|
|
26093
26247
|
type: TriangleAlertIcon,
|
|
26094
26248
|
selector: "triangle-alert, triangle-alert-icon, TriangleAlert, TriangleAlertIcon",
|
|
26095
26249
|
inputs: ["class"]
|
|
26096
26250
|
},
|
|
26251
|
+
{
|
|
26252
|
+
kind: "directive",
|
|
26253
|
+
type: TruncatedDirective,
|
|
26254
|
+
selector: "[truncated]",
|
|
26255
|
+
inputs: ["truncated"],
|
|
26256
|
+
exportAs: ["truncated"]
|
|
26257
|
+
},
|
|
26097
26258
|
{
|
|
26098
26259
|
kind: "directive",
|
|
26099
26260
|
type: Tree,
|
|
@@ -26205,7 +26366,9 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
26205
26366
|
FaIconComponent,
|
|
26206
26367
|
FileIcon,
|
|
26207
26368
|
FolderIcon,
|
|
26369
|
+
TooltipDirective,
|
|
26208
26370
|
TriangleAlertIcon,
|
|
26371
|
+
TruncatedDirective,
|
|
26209
26372
|
HighlightWordPipe,
|
|
26210
26373
|
SyslangPipe,
|
|
26211
26374
|
TranslocoPipe,
|
|
@@ -26215,7 +26378,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
26215
26378
|
AggregationPanelComponent
|
|
26216
26379
|
],
|
|
26217
26380
|
host: { "[class]": "cn(\"flex h-[inherit] max-h-[inherit] flex-col\", class(), (isMissing() || (hideWhenEmpty() && isHidden())) && \"hidden\")" },
|
|
26218
|
-
template: "@if (!aggregation()?.isTree) {\n <div role=\"alert\" class=\"p-2 text-sm text-red-500\">\n <triangle-alert-icon class=\"mr-1 shrink-0\" />\n The aggregationTree component does not support list aggregations. Please use\n the <Aggregation /> component instead.\n </div>\n}\n<aggregation-panel\n [id]=\"id()\"\n [collapsible]=\"collapsible()\"\n [collapsed]=\"collapsed()\"\n [isEmpty]=\"isEmpty()\"\n [isDate]=\"isDate()\"\n [aggregation]=\"aggregation()\"\n [showFiltersCount]=\"showFiltersCount()\"\n [filtersCount]=\"filtersCount()\"\n [hasFilters]=\"hasFilters()\"\n [selection]=\"selection()\"\n [isAllSelected]=\"isAllSelected()\"\n [headless]=\"headless()\"\n [selectedCount]=\"selected().length\"\n [(searchText)]=\"searchText\"\n [itemsLength]=\"items().length\"\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\n [searchedItemsLength]=\"searchedItems().length\"\n (cleared)=\"clear()\"\n (applied)=\"apply()\"\n (allSelected)=\"selectAll()\"\n (allUnselected)=\"unselectAll()\"\n (loadedMore)=\"loadMore()\">\n <!-- Relay the host's `label` projection into the panel's `label` slot. When the\n host projects nothing, the local <ng-content> falls back to the icon + display\n (mirrors AggregationPanel's own fallback), so the header is never empty. -->\n <ng-container ngProjectAs=\"label\">\n <ng-content select=\"label\">\n @let labelIcon = aggregation()?.icon;\n @if (labelIcon) {\n <FaIcon [faClass]=\"labelIcon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow truncate\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n </ng-container>\n <div class=\"min-h-0 w-full flex-1 scrollbar-thin overflow-auto pb-3\">\n <ul\n ngTree\n #tree=\"ngTree\"\n [multi]=\"true\"\n [(value)]=\"selected\"\n (valueChange)=\"onSelectionChange($event)\"\n /* Translated like the flat list's own label (aggregation-list.html): this was the raw\n `display`, so the accessible name of a whole facet came out in the source language.\n The pipes apply to the whole nullish chain, `|` being the loosest operator Angular\n parses — no parentheses needed. */\n [attr.aria-label]=\"aggregation()?.display ?? name() | syslang | transloco\"\n class=\"basic-tree m-1\">\n <ng-template\n [ngTemplateOutlet]=\"treeNodes\"\n [ngTemplateOutletContext]=\"{ nodes: nodes(), parent: tree }\" />\n </ul>\n </div>\n</aggregation-panel>\n\n<ng-template #treeNodes let-nodes=\"nodes\" let-parent=\"parent\">\n @for (node of nodes; track node.$path) {\n @let label = node.display ?? node.value ?? \"\";\n <li\n ngTreeItem\n #treeItem=\"ngTreeItem\"\n [parent]=\"parent\"\n [value]=\"node.$path\"\n [label]=\"label\"\n [disabled]=\"node.disabled\"\n [(expanded)]=\"node.$opened\"\n (expandedChange)=\"onExpandedChange(node, $event)\"\n [attr.aria-label]=\"label\"\n [attr.aria-describedby]=\"\n nodeDescription(node) ? descriptionId(node.$path) : null\n \"\n [class]=\"cn('m-0 block w-full p-0', itemStateClass)\"\n (keydown.enter)=\"onTreeItemEnterKey(node, $event)\"\n (keydown.space)=\"onTreeItemSpaceKey(node, $event)\">\n <!-- 2. Ce div prend tout l'espace et intercepte tous les clics de la ligne. `ps-0.5`: the checkbox of a list option sits\n 2px further in than a tree's (the option has its own padding), so the checkboxes of the two facets, and the select-all of\n the bar of a popover, are on the same vertical line. -->\n <div\n class=\"flex w-full cursor-pointer items-center gap-2 py-0.5 ps-0.5 text-sm leading-6\"\n (click)=\"onTreeItemRowClick(node, $event)\"\n (mousedown)=\"$event.stopPropagation()\"\n (pointerdown)=\"$event.stopPropagation()\">\n @if (showCheckbox()) {\n <!-- `inert` keeps the native input out of the tab order and out of hit-testing:\n a role=\"treeitem\" admits no focusable descendant, and the click must reach\n the row handler above. Selection is announced by the <li>'s aria-selected. -->\n <!-- --spacing-xs: 0 kills the gap the checkbox keeps between its box and its\n (here empty) text block. Set as a custom property rather than a utility\n class: the CVA already sets gap-(--spacing-xs), and two same-specificity\n classes would be resolved by stylesheet order, not by us. -->\n <span\n inert\n aria-hidden=\"true\"\n class=\"inline-flex shrink-0\"\n style=\"--spacing-xs: 0\">\n <checkbox\n [checked]=\"treeItem.selected() || node.$selectedVisually || false\"\n [disabled]=\"node.disabled\"\n [class]=\"\n cn(\n 'pointer-events-none',\n (treeItem.selected() || node.$selectedVisually) &&\n 'selected-icon'\n )\n \" />\n </span>\n }\n <!-- Depth indent sits on the chevron, after the checkbox: the checkboxes of every\n level stay flush left (aligned with a flat list's), and since this adds no extra\n flex child, the step between consecutive levels is exactly one indent. -->\n @let depthIndent =\n \"calc(var(--agg-tree-indent, 0.5rem) * \" +\n (treeItem.level() - 1) +\n \")\";\n @if (node.hasChildren) {\n <!-- Expand/collapse affordance for the mouse only, hence a <span> and not a <button>:\n the keyboard already expands with ArrowRight / ArrowLeft, handled by ngTreeItem, and\n a role=\"treeitem\" admits no focusable descendant — so this was a <button> nobody\n could reach, already carrying aria-hidden and tabindex=\"-1\" to say so. Every audit\n reads it as a button with no accessible name (RGAA 7.1, 11.9). Same shape as the leaf\n branch below. -->\n <span\n aria-hidden=\"true\"\n (click)=\"toggleTreeNode(treeItem, $event)\"\n [style.marginInlineStart]=\"depthIndent\"\n class=\"inline-flex cursor-pointer items-center justify-center\">\n <ChevronRightIcon\n [class]=\"\n cn('expand-icon shrink-0', node.$opened && 'rotate-90')\n \" />\n <FolderIcon class=\"shrink-0\" />\n </span>\n } @else {\n <span\n aria-hidden=\"true\"\n [style.marginInlineStart]=\"depthIndent\"\n class=\"inline-flex items-center justify-center\">\n <!-- Reserves the chevron's own width (`.expand-icon` is a styling hook with\n no rule of its own), so a leaf lines up with its parent nodes. -->\n <span class=\"expand-icon w-4 shrink-0\"></span>\n <FileIcon class=\"size-4 shrink-0\" />\n </span>\n }\n @if (resolvedItemTemplate(); as tpl) {\n <span\n class=\"grow truncate select-none\"\n (click)=\"onTreeItemTextClick(node, $event)\">\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: node,\n field: aggregation()?.column,\n selected: treeItem.selected() ?? false,\n count: node.count,\n name: label,\n searchText: searchText(),\n level: node.$level ?? 0,\n expanded: node.$opened ?? false,\n hasChildren: node.hasChildren ?? false\n }\" />\n </span>\n } @else {\n <!-- ce span est essentiel pour le style -->\n @let appliesOnLabelClick = quickFilter() || applyOnSelect();\n <span\n [class]=\"\n cn(\n 'grow truncate select-none',\n appliesOnLabelClick && 'hover:underline'\n )\n \"\n [title]=\"\n appliesOnLabelClick\n ? ((isTreeItemFiltered(node)\n ? 'filters.removeFilter'\n : 'filters.addFilter'\n ) | transloco) +\n ': ' +\n label\n : label\n \"\n (click)=\"onTreeItemTextClick(node, $event)\">\n @for (\n chunk of label | highlightWord: searchText() : 10;\n track $index\n ) {\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\n chunk.text\n }}</span>\n }\n </span>\n @if (showCount() && node.count > 0) {\n <span class=\"ml-auto px-1 text-xs\" aria-hidden=\"true\">{{\n node.count\n }}</span>\n }\n }\n @let description = nodeDescription(node);\n @if (description) {\n <!-- `hidden`, not `sr-only`: read through aria-describedby only. A visually-hidden\n span would also be counted in the treeitem's name (computed from its content). -->\n <span hidden [id]=\"descriptionId(node.$path)\">{{ description }}</span>\n }\n </div>\n </li>\n @if (node.hasChildren) {\n <!-- No indentation here: it would shift the checkbox too. Depth is applied\n inside the row, after the checkbox, so every checkbox stays flush left. -->\n <!-- The <ul> sits INSIDE the deferred content, not around it: `ngTreeItemGroup` only\n instantiates its view when the node opens, so a <ul> declared outside stayed in the DOM,\n empty, for every collapsed node — a structural tag with nothing to structure (RGAA 8.9).\n The @if cannot move onto the <ul> either: the directive registers itself with its tree\n item on init and has to exist for as long as the item does. -->\n <ng-template\n ngTreeItemGroup\n [ownedBy]=\"treeItem\"\n #group=\"ngTreeItemGroup\">\n <ul role=\"group\">\n <ng-template\n [ngTemplateOutlet]=\"treeNodes\"\n [ngTemplateOutletContext]=\"{ nodes: node.items, parent: group }\" />\n </ul>\n </ng-template>\n }\n }\n</ng-template>\n"
|
|
26381
|
+
template: "@if (!aggregation()?.isTree) {\n <div role=\"alert\" class=\"p-2 text-sm text-red-500\">\n <triangle-alert-icon class=\"mr-1 shrink-0\" />\n The aggregationTree component does not support list aggregations. Please use\n the <Aggregation /> component instead.\n </div>\n}\n<aggregation-panel\n [id]=\"id()\"\n [collapsible]=\"collapsible()\"\n [collapsed]=\"collapsed()\"\n [isEmpty]=\"isEmpty()\"\n [isDate]=\"isDate()\"\n [aggregation]=\"aggregation()\"\n [showFiltersCount]=\"showFiltersCount()\"\n [filtersCount]=\"filtersCount()\"\n [hasFilters]=\"hasFilters()\"\n [selection]=\"selection()\"\n [isAllSelected]=\"isAllSelected()\"\n [headless]=\"headless()\"\n [selectedCount]=\"selected().length\"\n [(searchText)]=\"searchText\"\n [itemsLength]=\"items().length\"\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\n [searchedItemsLength]=\"searchedItems().length\"\n (cleared)=\"clear()\"\n (applied)=\"apply()\"\n (allSelected)=\"selectAll()\"\n (allUnselected)=\"unselectAll()\"\n (loadedMore)=\"loadMore()\">\n <!-- Relay the host's `label` projection into the panel's `label` slot. When the\n host projects nothing, the local <ng-content> falls back to the icon + display\n (mirrors AggregationPanel's own fallback), so the header is never empty. -->\n <ng-container ngProjectAs=\"label\">\n <ng-content select=\"label\">\n @let labelIcon = aggregation()?.icon;\n @if (labelIcon) {\n <FaIcon [faClass]=\"labelIcon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow truncate\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n </ng-container>\n <div class=\"min-h-0 w-full flex-1 scrollbar-thin overflow-auto pb-3\">\n <ul\n ngTree\n #tree=\"ngTree\"\n [multi]=\"true\"\n [(value)]=\"selected\"\n (valueChange)=\"onSelectionChange($event)\"\n /* Translated like the flat list's own label (aggregation-list.html): this was the raw\n `display`, so the accessible name of a whole facet came out in the source language.\n The pipes apply to the whole nullish chain, `|` being the loosest operator Angular\n parses — no parentheses needed. */\n [attr.aria-label]=\"aggregation()?.display ?? name() | syslang | transloco\"\n class=\"basic-tree m-1\">\n <ng-template\n [ngTemplateOutlet]=\"treeNodes\"\n [ngTemplateOutletContext]=\"{ nodes: nodes(), parent: tree }\" />\n </ul>\n </div>\n</aggregation-panel>\n\n<ng-template #treeNodes let-nodes=\"nodes\" let-parent=\"parent\">\n @for (node of nodes; track node.$path) {\n @let label = node.display ?? node.value ?? \"\";\n <li\n ngTreeItem\n #treeItem=\"ngTreeItem\"\n [parent]=\"parent\"\n [value]=\"node.$path\"\n [label]=\"label\"\n [disabled]=\"node.disabled\"\n [(expanded)]=\"node.$opened\"\n (expandedChange)=\"onExpandedChange(node, $event)\"\n [attr.aria-label]=\"label\"\n [attr.aria-describedby]=\"\n nodeDescription(node) ? descriptionId(node.$path) : null\n \"\n [class]=\"cn('m-0 block w-full p-0', itemStateClass)\"\n (keydown.enter)=\"onTreeItemEnterKey(node, $event)\"\n (keydown.space)=\"onTreeItemSpaceKey(node, $event)\">\n <!-- 2. Ce div prend tout l'espace et intercepte tous les clics de la ligne. `ps-0.5`: the checkbox of a list option sits\n 2px further in than a tree's (the option has its own padding), so the checkboxes of the two facets, and the select-all of\n the bar of a popover, are on the same vertical line. -->\n <div\n class=\"flex w-full cursor-pointer items-center gap-2 py-0.5 ps-0.5 text-sm leading-6\"\n (click)=\"onTreeItemRowClick(node, $event)\"\n (mousedown)=\"$event.stopPropagation()\"\n (pointerdown)=\"$event.stopPropagation()\">\n @if (showCheckbox()) {\n <!-- `inert` keeps the native input out of the tab order and out of hit-testing:\n a role=\"treeitem\" admits no focusable descendant, and the click must reach\n the row handler above. Selection is announced by the <li>'s aria-selected. -->\n <!-- --spacing-xs: 0 kills the gap the checkbox keeps between its box and its\n (here empty) text block. Set as a custom property rather than a utility\n class: the CVA already sets gap-(--spacing-xs), and two same-specificity\n classes would be resolved by stylesheet order, not by us. -->\n <span\n inert\n aria-hidden=\"true\"\n class=\"inline-flex shrink-0\"\n style=\"--spacing-xs: 0\">\n <checkbox\n [checked]=\"treeItem.selected() || node.$selectedVisually || false\"\n [disabled]=\"node.disabled\"\n [class]=\"\n cn(\n 'pointer-events-none',\n (treeItem.selected() || node.$selectedVisually) &&\n 'selected-icon'\n )\n \" />\n </span>\n }\n <!-- Depth indent sits on the chevron, after the checkbox: the checkboxes of every\n level stay flush left (aligned with a flat list's), and since this adds no extra\n flex child, the step between consecutive levels is exactly one indent. -->\n @let depthIndent =\n \"calc(var(--agg-tree-indent, 0.5rem) * \" +\n (treeItem.level() - 1) +\n \")\";\n @if (node.hasChildren) {\n <!-- Expand/collapse affordance for the mouse only, hence a <span> and not a <button>:\n the keyboard already expands with ArrowRight / ArrowLeft, handled by ngTreeItem, and\n a role=\"treeitem\" admits no focusable descendant — so this was a <button> nobody\n could reach, already carrying aria-hidden and tabindex=\"-1\" to say so. Every audit\n reads it as a button with no accessible name (RGAA 7.1, 11.9). Same shape as the leaf\n branch below. -->\n <span\n aria-hidden=\"true\"\n (click)=\"toggleTreeNode(treeItem, $event)\"\n [style.marginInlineStart]=\"depthIndent\"\n class=\"inline-flex cursor-pointer items-center justify-center\">\n <ChevronRightIcon\n [class]=\"\n cn('expand-icon shrink-0', node.$opened && 'rotate-90')\n \" />\n <FolderIcon class=\"shrink-0\" />\n </span>\n } @else {\n <span\n aria-hidden=\"true\"\n [style.marginInlineStart]=\"depthIndent\"\n class=\"inline-flex items-center justify-center\">\n <!-- Reserves the chevron's own width (`.expand-icon` is a styling hook with\n no rule of its own), so a leaf lines up with its parent nodes. -->\n <span class=\"expand-icon w-4 shrink-0\"></span>\n <FileIcon class=\"size-4 shrink-0\" />\n </span>\n }\n @if (resolvedItemTemplate(); as tpl) {\n <!-- Cut off, the label shows whole in a tooltip; a label that fits needs none. -->\n <span\n class=\"grow truncate select-none\"\n truncated\n #cutCustom=\"truncated\"\n [tooltip]=\"label\"\n [tooltipDisabled]=\"!cutCustom.isTruncated()\"\n (click)=\"onTreeItemTextClick(node, $event)\">\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: node,\n field: aggregation()?.column,\n selected: treeItem.selected() ?? false,\n count: node.count,\n name: label,\n searchText: searchText(),\n level: node.$level ?? 0,\n expanded: node.$opened ?? false,\n hasChildren: node.hasChildren ?? false\n }\" />\n </span>\n } @else {\n <!-- ce span est essentiel pour le style -->\n @let appliesOnLabelClick = quickFilter() || applyOnSelect();\n <!-- Cut off, the label shows whole in a tooltip (`truncated` measures it); a label that fits has none. -->\n <span\n [class]=\"\n cn(\n 'grow truncate select-none',\n appliesOnLabelClick && 'hover:underline'\n )\n \"\n truncated\n #cut=\"truncated\"\n [tooltip]=\"label\"\n [tooltipDisabled]=\"!cut.isTruncated()\"\n (click)=\"onTreeItemTextClick(node, $event)\">\n @for (\n chunk of label | highlightWord: searchText() : 10;\n track $index\n ) {\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\n chunk.text\n }}</span>\n }\n </span>\n @if (showCount() && node.count > 0) {\n <span class=\"ml-auto px-1 text-xs\" aria-hidden=\"true\">{{\n node.count\n }}</span>\n }\n }\n @let description = nodeDescription(node);\n @if (description) {\n <!-- `hidden`, not `sr-only`: read through aria-describedby only. A visually-hidden\n span would also be counted in the treeitem's name (computed from its content). -->\n <span hidden [id]=\"descriptionId(node.$path)\">{{ description }}</span>\n }\n </div>\n </li>\n @if (node.hasChildren) {\n <!-- No indentation here: it would shift the checkbox too. Depth is applied\n inside the row, after the checkbox, so every checkbox stays flush left. -->\n <!-- The <ul> sits INSIDE the deferred content, not around it: `ngTreeItemGroup` only\n instantiates its view when the node opens, so a <ul> declared outside stayed in the DOM,\n empty, for every collapsed node — a structural tag with nothing to structure (RGAA 8.9).\n The @if cannot move onto the <ul> either: the directive registers itself with its tree\n item on init and has to exist for as long as the item does. -->\n <ng-template\n ngTreeItemGroup\n [ownedBy]=\"treeItem\"\n #group=\"ngTreeItemGroup\">\n <ul role=\"group\">\n <ng-template\n [ngTemplateOutlet]=\"treeNodes\"\n [ngTemplateOutletContext]=\"{ nodes: node.items, parent: group }\" />\n </ul>\n </ng-template>\n }\n }\n</ng-template>\n"
|
|
26219
26382
|
}]
|
|
26220
26383
|
}],
|
|
26221
26384
|
ctorParameters: () => [],
|
|
@@ -26923,7 +27086,7 @@ var AggregationListComponent = class AggregationListComponent {
|
|
|
26923
27086
|
}
|
|
26924
27087
|
],
|
|
26925
27088
|
ngImport: i0,
|
|
26926
|
-
template: "@if (aggregation()?.isTree) {\n <div class=\"p-2 text-sm text-red-500\">\n <triangle-alert-icon class=\"mr-1 shrink-0\" />\n The aggregation component no longer supports tree aggregations. Please use\n the <AggregationTree /> component instead.\n </div>\n}\n<aggregation-panel\n [id]=\"id()\"\n [collapsible]=\"collapsible()\"\n [collapsed]=\"collapsed()\"\n [isEmpty]=\"isEmpty()\"\n [isDate]=\"isDate()\"\n [aggregation]=\"aggregation()\"\n [showFiltersCount]=\"showFiltersCount()\"\n [filtersCount]=\"filtersCount()\"\n [hasFilters]=\"hasFilters()\"\n [selection]=\"selection()\"\n [isAllSelected]=\"isAllSelected()\"\n [headless]=\"headless()\"\n [selectedCount]=\"selectedValues().length\"\n [(searchText)]=\"searchText\"\n [itemsLength]=\"items().length\"\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\n [searchedItemsLength]=\"searchedItems().length\"\n (cleared)=\"clear()\"\n (applied)=\"apply()\"\n (allSelected)=\"selectAll()\"\n (allUnselected)=\"unselectAll()\"\n (loadedMore)=\"loadMore()\">\n <!-- Relay the host's `label` projection into the panel's `label` slot. When the\n host projects nothing, the local <ng-content> falls back to the icon + display\n (mirrors AggregationPanel's own fallback), so the header is never empty. -->\n <ng-container ngProjectAs=\"label\">\n <ng-content select=\"label\">\n @let labelIcon = aggregation()?.icon;\n @if (labelIcon) {\n <FaIcon [faClass]=\"labelIcon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow truncate\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n </ng-container>\n <div\n #scrollElement\n class=\"min-h-0 w-full flex-1 scrollbar-thin overflow-auto px-1 pt-1 pb-3\">\n <div\n ngListbox\n multi\n selectionMode=\"explicit\"\n [value]=\"selectedValues()\"\n (valueChange)=\"syncListboxValue($event)\"\n [wrap]=\"false\"\n [softDisabled]=\"false\"\n class=\"relative w-full\"\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\n role=\"listbox\"\n aria-multiselectable=\"true\"\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\n @let item = items()[vItem.index];\n @let description = rowDescription(item);\n <div\n #row\n ngOption\n [attr.aria-describedby]=\"\n description ? descriptionId(vItem.index) : null\n \"\n [attr.data-index]=\"vItem.index\"\n [value]=\"item.value\"\n [disabled]=\"item.count === 0\"\n [class]=\"cn('absolute w-full', menuItemBaseClass)\"\n (keydown.enter)=\"onOptionEnterKey(item, $event)\"\n (keydown.space)=\"\n onListItemRowClick(item, $event); $event.preventDefault()\n \"\n [style.transform]=\"'translateY(' + vItem.start + 'px)'\"\n role=\"option\">\n <ng-container\n [ngTemplateOutlet]=\"listItemTpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: item,\n field: aggregation()?.column\n }\">\n </ng-container>\n @if (description) {\n <!-- `hidden`, not `sr-only`: read through aria-describedby only. A visually-hidden\n span would also be counted in the option's name (computed from its content). -->\n <span hidden [id]=\"descriptionId(vItem.index)\">{{\n description\n }}</span>\n }\n </div>\n }\n </div>\n </div>\n</aggregation-panel>\n\n<ng-template #listItemTpl let-item let-field=\"field\">\n <a\n [attr.aria-label]=\"listItemName(item) | syslang\"\n [class]=\"\n cn(\n '-m-1.5 flex min-w-0 grow items-center gap-2 leading-6',\n item.count === 0 && 'pointer-events-none text-(--font-neutral-muted)'\n )\n \"\n (click)=\"onListItemRowClick(item, $event)\">\n @if (showCheckbox()) {\n <!-- `inert` keeps the native input out of the tab order and out of hit-testing:\n keyboard navigation belongs to ngListbox/ngOption (roving tabindex), the\n selection is already exposed by role=\"option\" + aria-selected, and the click\n must reach the <a> above. Same treatment as the tree.\n --spacing-xs: 0 kills the gap the checkbox keeps between its box and its\n (here empty) text block. -->\n <span\n inert\n aria-hidden=\"true\"\n class=\"inline-flex shrink-0\"\n style=\"--spacing-xs: 0\">\n <checkbox\n [checked]=\"item.$selected ?? false\"\n [disabled]=\"item.count === 0\"\n [class]=\"'pointer-events-none'\" />\n </span>\n }\n @if (resolvedItemTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: item,\n field: field,\n selected: item.$selected ?? false,\n count: item.count,\n name: (listItemName(item) | syslang) ?? '',\n searchText: searchText()\n }\" />\n } @else {\n @let icon = $any(item).icon;\n @if (icon) {\n <FaIcon\n [faClass]=\"icon\"\n class=\"shrink-0 self-center justify-self-center\" />\n }\n @let appliesOnLabelClick = quickFilter() || applyOnSelect();\n <span\n [class]=\"\n cn(\n 'line-clamp-1 cursor-pointer break-all text-ellipsis select-none',\n appliesOnLabelClick && 'hover:underline'\n )\n \"\n
|
|
27089
|
+
template: "@if (aggregation()?.isTree) {\n <div class=\"p-2 text-sm text-red-500\">\n <triangle-alert-icon class=\"mr-1 shrink-0\" />\n The aggregation component no longer supports tree aggregations. Please use\n the <AggregationTree /> component instead.\n </div>\n}\n<aggregation-panel\n [id]=\"id()\"\n [collapsible]=\"collapsible()\"\n [collapsed]=\"collapsed()\"\n [isEmpty]=\"isEmpty()\"\n [isDate]=\"isDate()\"\n [aggregation]=\"aggregation()\"\n [showFiltersCount]=\"showFiltersCount()\"\n [filtersCount]=\"filtersCount()\"\n [hasFilters]=\"hasFilters()\"\n [selection]=\"selection()\"\n [isAllSelected]=\"isAllSelected()\"\n [headless]=\"headless()\"\n [selectedCount]=\"selectedValues().length\"\n [(searchText)]=\"searchText\"\n [itemsLength]=\"items().length\"\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\n [searchedItemsLength]=\"searchedItems().length\"\n (cleared)=\"clear()\"\n (applied)=\"apply()\"\n (allSelected)=\"selectAll()\"\n (allUnselected)=\"unselectAll()\"\n (loadedMore)=\"loadMore()\">\n <!-- Relay the host's `label` projection into the panel's `label` slot. When the\n host projects nothing, the local <ng-content> falls back to the icon + display\n (mirrors AggregationPanel's own fallback), so the header is never empty. -->\n <ng-container ngProjectAs=\"label\">\n <ng-content select=\"label\">\n @let labelIcon = aggregation()?.icon;\n @if (labelIcon) {\n <FaIcon [faClass]=\"labelIcon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow truncate\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n </ng-container>\n <div\n #scrollElement\n class=\"min-h-0 w-full flex-1 scrollbar-thin overflow-auto px-1 pt-1 pb-3\">\n <div\n ngListbox\n multi\n selectionMode=\"explicit\"\n [value]=\"selectedValues()\"\n (valueChange)=\"syncListboxValue($event)\"\n [wrap]=\"false\"\n [softDisabled]=\"false\"\n class=\"relative w-full\"\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\n role=\"listbox\"\n aria-multiselectable=\"true\"\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\n @let item = items()[vItem.index];\n @let description = rowDescription(item);\n <div\n #row\n ngOption\n [attr.aria-describedby]=\"\n description ? descriptionId(vItem.index) : null\n \"\n [attr.data-index]=\"vItem.index\"\n [value]=\"item.value\"\n [disabled]=\"item.count === 0\"\n [class]=\"cn('absolute w-full', menuItemBaseClass)\"\n (keydown.enter)=\"onOptionEnterKey(item, $event)\"\n (keydown.space)=\"\n onListItemRowClick(item, $event); $event.preventDefault()\n \"\n [style.transform]=\"'translateY(' + vItem.start + 'px)'\"\n role=\"option\">\n <ng-container\n [ngTemplateOutlet]=\"listItemTpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: item,\n field: aggregation()?.column\n }\">\n </ng-container>\n @if (description) {\n <!-- `hidden`, not `sr-only`: read through aria-describedby only. A visually-hidden\n span would also be counted in the option's name (computed from its content). -->\n <span hidden [id]=\"descriptionId(vItem.index)\">{{\n description\n }}</span>\n }\n </div>\n }\n </div>\n </div>\n</aggregation-panel>\n\n<ng-template #listItemTpl let-item let-field=\"field\">\n <a\n [attr.aria-label]=\"listItemName(item) | syslang\"\n [class]=\"\n cn(\n '-m-1.5 flex min-w-0 grow items-center gap-2 leading-6',\n item.count === 0 && 'pointer-events-none text-(--font-neutral-muted)'\n )\n \"\n (click)=\"onListItemRowClick(item, $event)\">\n @if (showCheckbox()) {\n <!-- `inert` keeps the native input out of the tab order and out of hit-testing:\n keyboard navigation belongs to ngListbox/ngOption (roving tabindex), the\n selection is already exposed by role=\"option\" + aria-selected, and the click\n must reach the <a> above. Same treatment as the tree.\n --spacing-xs: 0 kills the gap the checkbox keeps between its box and its\n (here empty) text block. -->\n <span\n inert\n aria-hidden=\"true\"\n class=\"inline-flex shrink-0\"\n style=\"--spacing-xs: 0\">\n <checkbox\n [checked]=\"item.$selected ?? false\"\n [disabled]=\"item.count === 0\"\n [class]=\"'pointer-events-none'\" />\n </span>\n }\n @if (resolvedItemTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: item,\n field: field,\n selected: item.$selected ?? false,\n count: item.count,\n name: (listItemName(item) | syslang) ?? '',\n searchText: searchText()\n }\" />\n } @else {\n @let icon = $any(item).icon;\n @if (icon) {\n <FaIcon\n [faClass]=\"icon\"\n class=\"shrink-0 self-center justify-self-center\" />\n }\n @let appliesOnLabelClick = quickFilter() || applyOnSelect();\n <!-- Cut off, the label shows whole in a tooltip (`truncated` measures it); a label that fits has none. -->\n <span\n [class]=\"\n cn(\n 'line-clamp-1 cursor-pointer break-all text-ellipsis select-none',\n appliesOnLabelClick && 'hover:underline'\n )\n \"\n truncated\n #cut=\"truncated\"\n [tooltip]=\"listItemName(item) | syslang\"\n [tooltipDisabled]=\"!cut.isTruncated()\"\n (click)=\"onListItemTextClick(item, $event)\">\n @for (\n chunk of (listItemName(item) | syslang) ?? \"\"\n | highlightWord: searchText() : 10;\n track $index\n ) {\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\n chunk.text\n }}</span>\n }\n </span>\n @if (showCount() && item.count > 0) {\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\n item.count\n }}</span>\n }\n }\n </a>\n</ng-template>\n",
|
|
26927
27090
|
dependencies: [
|
|
26928
27091
|
{
|
|
26929
27092
|
kind: "directive",
|
|
@@ -26974,12 +27137,45 @@ var AggregationListComponent = class AggregationListComponent {
|
|
|
26974
27137
|
selector: "fa-icon, FaIcon",
|
|
26975
27138
|
inputs: ["faClass", "class"]
|
|
26976
27139
|
},
|
|
27140
|
+
{
|
|
27141
|
+
kind: "directive",
|
|
27142
|
+
type: TooltipDirective,
|
|
27143
|
+
selector: "[tooltip]",
|
|
27144
|
+
inputs: [
|
|
27145
|
+
"tooltip",
|
|
27146
|
+
"tooltipPosition",
|
|
27147
|
+
"tooltipEvent",
|
|
27148
|
+
"showDelay",
|
|
27149
|
+
"hideDelay",
|
|
27150
|
+
"life",
|
|
27151
|
+
"tooltipOffset",
|
|
27152
|
+
"autoHide",
|
|
27153
|
+
"hideOnEscape",
|
|
27154
|
+
"escape",
|
|
27155
|
+
"tooltipDisabled",
|
|
27156
|
+
"tooltipArrow",
|
|
27157
|
+
"tooltipStyleClass",
|
|
27158
|
+
"tooltipOptions",
|
|
27159
|
+
"tooltip-position",
|
|
27160
|
+
"tooltip-delay",
|
|
27161
|
+
"tooltip-duration",
|
|
27162
|
+
"tooltip-offset"
|
|
27163
|
+
],
|
|
27164
|
+
exportAs: ["tooltip"]
|
|
27165
|
+
},
|
|
26977
27166
|
{
|
|
26978
27167
|
kind: "component",
|
|
26979
27168
|
type: TriangleAlertIcon,
|
|
26980
27169
|
selector: "triangle-alert, triangle-alert-icon, TriangleAlert, TriangleAlertIcon",
|
|
26981
27170
|
inputs: ["class"]
|
|
26982
27171
|
},
|
|
27172
|
+
{
|
|
27173
|
+
kind: "directive",
|
|
27174
|
+
type: TruncatedDirective,
|
|
27175
|
+
selector: "[truncated]",
|
|
27176
|
+
inputs: ["truncated"],
|
|
27177
|
+
exportAs: ["truncated"]
|
|
27178
|
+
},
|
|
26983
27179
|
{
|
|
26984
27180
|
kind: "component",
|
|
26985
27181
|
type: AggregationPanelComponent,
|
|
@@ -27060,7 +27256,9 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
27060
27256
|
SyslangPipe,
|
|
27061
27257
|
TranslocoPipe,
|
|
27062
27258
|
FaIconComponent,
|
|
27259
|
+
TooltipDirective,
|
|
27063
27260
|
TriangleAlertIcon,
|
|
27261
|
+
TruncatedDirective,
|
|
27064
27262
|
HighlightWordPipe,
|
|
27065
27263
|
AggregationPanelComponent,
|
|
27066
27264
|
CheckboxComponent
|
|
@@ -27069,7 +27267,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
27069
27267
|
"[class]": "cn(\"flex h-[inherit] max-h-[inherit] flex-col\", class(), (isMissing() || (hideWhenEmpty() && isEmpty())) && \"hidden\")",
|
|
27070
27268
|
"(keydown.enter)": "onHostEnterKey($event)"
|
|
27071
27269
|
},
|
|
27072
|
-
template: "@if (aggregation()?.isTree) {\n <div class=\"p-2 text-sm text-red-500\">\n <triangle-alert-icon class=\"mr-1 shrink-0\" />\n The aggregation component no longer supports tree aggregations. Please use\n the <AggregationTree /> component instead.\n </div>\n}\n<aggregation-panel\n [id]=\"id()\"\n [collapsible]=\"collapsible()\"\n [collapsed]=\"collapsed()\"\n [isEmpty]=\"isEmpty()\"\n [isDate]=\"isDate()\"\n [aggregation]=\"aggregation()\"\n [showFiltersCount]=\"showFiltersCount()\"\n [filtersCount]=\"filtersCount()\"\n [hasFilters]=\"hasFilters()\"\n [selection]=\"selection()\"\n [isAllSelected]=\"isAllSelected()\"\n [headless]=\"headless()\"\n [selectedCount]=\"selectedValues().length\"\n [(searchText)]=\"searchText\"\n [itemsLength]=\"items().length\"\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\n [searchedItemsLength]=\"searchedItems().length\"\n (cleared)=\"clear()\"\n (applied)=\"apply()\"\n (allSelected)=\"selectAll()\"\n (allUnselected)=\"unselectAll()\"\n (loadedMore)=\"loadMore()\">\n <!-- Relay the host's `label` projection into the panel's `label` slot. When the\n host projects nothing, the local <ng-content> falls back to the icon + display\n (mirrors AggregationPanel's own fallback), so the header is never empty. -->\n <ng-container ngProjectAs=\"label\">\n <ng-content select=\"label\">\n @let labelIcon = aggregation()?.icon;\n @if (labelIcon) {\n <FaIcon [faClass]=\"labelIcon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow truncate\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n </ng-container>\n <div\n #scrollElement\n class=\"min-h-0 w-full flex-1 scrollbar-thin overflow-auto px-1 pt-1 pb-3\">\n <div\n ngListbox\n multi\n selectionMode=\"explicit\"\n [value]=\"selectedValues()\"\n (valueChange)=\"syncListboxValue($event)\"\n [wrap]=\"false\"\n [softDisabled]=\"false\"\n class=\"relative w-full\"\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\n role=\"listbox\"\n aria-multiselectable=\"true\"\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\n @let item = items()[vItem.index];\n @let description = rowDescription(item);\n <div\n #row\n ngOption\n [attr.aria-describedby]=\"\n description ? descriptionId(vItem.index) : null\n \"\n [attr.data-index]=\"vItem.index\"\n [value]=\"item.value\"\n [disabled]=\"item.count === 0\"\n [class]=\"cn('absolute w-full', menuItemBaseClass)\"\n (keydown.enter)=\"onOptionEnterKey(item, $event)\"\n (keydown.space)=\"\n onListItemRowClick(item, $event); $event.preventDefault()\n \"\n [style.transform]=\"'translateY(' + vItem.start + 'px)'\"\n role=\"option\">\n <ng-container\n [ngTemplateOutlet]=\"listItemTpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: item,\n field: aggregation()?.column\n }\">\n </ng-container>\n @if (description) {\n <!-- `hidden`, not `sr-only`: read through aria-describedby only. A visually-hidden\n span would also be counted in the option's name (computed from its content). -->\n <span hidden [id]=\"descriptionId(vItem.index)\">{{\n description\n }}</span>\n }\n </div>\n }\n </div>\n </div>\n</aggregation-panel>\n\n<ng-template #listItemTpl let-item let-field=\"field\">\n <a\n [attr.aria-label]=\"listItemName(item) | syslang\"\n [class]=\"\n cn(\n '-m-1.5 flex min-w-0 grow items-center gap-2 leading-6',\n item.count === 0 && 'pointer-events-none text-(--font-neutral-muted)'\n )\n \"\n (click)=\"onListItemRowClick(item, $event)\">\n @if (showCheckbox()) {\n <!-- `inert` keeps the native input out of the tab order and out of hit-testing:\n keyboard navigation belongs to ngListbox/ngOption (roving tabindex), the\n selection is already exposed by role=\"option\" + aria-selected, and the click\n must reach the <a> above. Same treatment as the tree.\n --spacing-xs: 0 kills the gap the checkbox keeps between its box and its\n (here empty) text block. -->\n <span\n inert\n aria-hidden=\"true\"\n class=\"inline-flex shrink-0\"\n style=\"--spacing-xs: 0\">\n <checkbox\n [checked]=\"item.$selected ?? false\"\n [disabled]=\"item.count === 0\"\n [class]=\"'pointer-events-none'\" />\n </span>\n }\n @if (resolvedItemTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: item,\n field: field,\n selected: item.$selected ?? false,\n count: item.count,\n name: (listItemName(item) | syslang) ?? '',\n searchText: searchText()\n }\" />\n } @else {\n @let icon = $any(item).icon;\n @if (icon) {\n <FaIcon\n [faClass]=\"icon\"\n class=\"shrink-0 self-center justify-self-center\" />\n }\n @let appliesOnLabelClick = quickFilter() || applyOnSelect();\n <span\n [class]=\"\n cn(\n 'line-clamp-1 cursor-pointer break-all text-ellipsis select-none',\n appliesOnLabelClick && 'hover:underline'\n )\n \"\n
|
|
27270
|
+
template: "@if (aggregation()?.isTree) {\n <div class=\"p-2 text-sm text-red-500\">\n <triangle-alert-icon class=\"mr-1 shrink-0\" />\n The aggregation component no longer supports tree aggregations. Please use\n the <AggregationTree /> component instead.\n </div>\n}\n<aggregation-panel\n [id]=\"id()\"\n [collapsible]=\"collapsible()\"\n [collapsed]=\"collapsed()\"\n [isEmpty]=\"isEmpty()\"\n [isDate]=\"isDate()\"\n [aggregation]=\"aggregation()\"\n [showFiltersCount]=\"showFiltersCount()\"\n [filtersCount]=\"filtersCount()\"\n [hasFilters]=\"hasFilters()\"\n [selection]=\"selection()\"\n [isAllSelected]=\"isAllSelected()\"\n [headless]=\"headless()\"\n [selectedCount]=\"selectedValues().length\"\n [(searchText)]=\"searchText\"\n [itemsLength]=\"items().length\"\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\n [searchedItemsLength]=\"searchedItems().length\"\n (cleared)=\"clear()\"\n (applied)=\"apply()\"\n (allSelected)=\"selectAll()\"\n (allUnselected)=\"unselectAll()\"\n (loadedMore)=\"loadMore()\">\n <!-- Relay the host's `label` projection into the panel's `label` slot. When the\n host projects nothing, the local <ng-content> falls back to the icon + display\n (mirrors AggregationPanel's own fallback), so the header is never empty. -->\n <ng-container ngProjectAs=\"label\">\n <ng-content select=\"label\">\n @let labelIcon = aggregation()?.icon;\n @if (labelIcon) {\n <FaIcon [faClass]=\"labelIcon\" class=\"mr-1 shrink-0\" />\n }\n <span class=\"grow truncate\">{{\n aggregation()?.display | syslang | transloco\n }}</span>\n </ng-content>\n </ng-container>\n <div\n #scrollElement\n class=\"min-h-0 w-full flex-1 scrollbar-thin overflow-auto px-1 pt-1 pb-3\">\n <div\n ngListbox\n multi\n selectionMode=\"explicit\"\n [value]=\"selectedValues()\"\n (valueChange)=\"syncListboxValue($event)\"\n [wrap]=\"false\"\n [softDisabled]=\"false\"\n class=\"relative w-full\"\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\n role=\"listbox\"\n aria-multiselectable=\"true\"\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\n @let item = items()[vItem.index];\n @let description = rowDescription(item);\n <div\n #row\n ngOption\n [attr.aria-describedby]=\"\n description ? descriptionId(vItem.index) : null\n \"\n [attr.data-index]=\"vItem.index\"\n [value]=\"item.value\"\n [disabled]=\"item.count === 0\"\n [class]=\"cn('absolute w-full', menuItemBaseClass)\"\n (keydown.enter)=\"onOptionEnterKey(item, $event)\"\n (keydown.space)=\"\n onListItemRowClick(item, $event); $event.preventDefault()\n \"\n [style.transform]=\"'translateY(' + vItem.start + 'px)'\"\n role=\"option\">\n <ng-container\n [ngTemplateOutlet]=\"listItemTpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: item,\n field: aggregation()?.column\n }\">\n </ng-container>\n @if (description) {\n <!-- `hidden`, not `sr-only`: read through aria-describedby only. A visually-hidden\n span would also be counted in the option's name (computed from its content). -->\n <span hidden [id]=\"descriptionId(vItem.index)\">{{\n description\n }}</span>\n }\n </div>\n }\n </div>\n </div>\n</aggregation-panel>\n\n<ng-template #listItemTpl let-item let-field=\"field\">\n <a\n [attr.aria-label]=\"listItemName(item) | syslang\"\n [class]=\"\n cn(\n '-m-1.5 flex min-w-0 grow items-center gap-2 leading-6',\n item.count === 0 && 'pointer-events-none text-(--font-neutral-muted)'\n )\n \"\n (click)=\"onListItemRowClick(item, $event)\">\n @if (showCheckbox()) {\n <!-- `inert` keeps the native input out of the tab order and out of hit-testing:\n keyboard navigation belongs to ngListbox/ngOption (roving tabindex), the\n selection is already exposed by role=\"option\" + aria-selected, and the click\n must reach the <a> above. Same treatment as the tree.\n --spacing-xs: 0 kills the gap the checkbox keeps between its box and its\n (here empty) text block. -->\n <span\n inert\n aria-hidden=\"true\"\n class=\"inline-flex shrink-0\"\n style=\"--spacing-xs: 0\">\n <checkbox\n [checked]=\"item.$selected ?? false\"\n [disabled]=\"item.count === 0\"\n [class]=\"'pointer-events-none'\" />\n </span>\n }\n @if (resolvedItemTemplate(); as tpl) {\n <ng-container\n [ngTemplateOutlet]=\"tpl\"\n [ngTemplateOutletContext]=\"{\n $implicit: item,\n field: field,\n selected: item.$selected ?? false,\n count: item.count,\n name: (listItemName(item) | syslang) ?? '',\n searchText: searchText()\n }\" />\n } @else {\n @let icon = $any(item).icon;\n @if (icon) {\n <FaIcon\n [faClass]=\"icon\"\n class=\"shrink-0 self-center justify-self-center\" />\n }\n @let appliesOnLabelClick = quickFilter() || applyOnSelect();\n <!-- Cut off, the label shows whole in a tooltip (`truncated` measures it); a label that fits has none. -->\n <span\n [class]=\"\n cn(\n 'line-clamp-1 cursor-pointer break-all text-ellipsis select-none',\n appliesOnLabelClick && 'hover:underline'\n )\n \"\n truncated\n #cut=\"truncated\"\n [tooltip]=\"listItemName(item) | syslang\"\n [tooltipDisabled]=\"!cut.isTruncated()\"\n (click)=\"onListItemTextClick(item, $event)\">\n @for (\n chunk of (listItemName(item) | syslang) ?? \"\"\n | highlightWord: searchText() : 10;\n track $index\n ) {\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\n chunk.text\n }}</span>\n }\n </span>\n @if (showCount() && item.count > 0) {\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\n item.count\n }}</span>\n }\n }\n </a>\n</ng-template>\n"
|
|
27073
27271
|
}]
|
|
27074
27272
|
}],
|
|
27075
27273
|
ctorParameters: () => [],
|
|
@@ -28095,6 +28293,17 @@ function chipsForLeaf(leaf, source) {
|
|
|
28095
28293
|
}
|
|
28096
28294
|
var AppliedFiltersComponent = class AppliedFiltersComponent {
|
|
28097
28295
|
queryParamsStore = inject(QueryParamsStore);
|
|
28296
|
+
static nextId = 0;
|
|
28297
|
+
listId = `applied-filters-${AppliedFiltersComponent.nextId++}`;
|
|
28298
|
+
list = viewChild("list", ...ngDevMode ? [{ debugName: "list" }] : /* istanbul ignore next */ []);
|
|
28299
|
+
foldable = signal(false, ...ngDevMode ? [{ debugName: "foldable" }] : /* istanbul ignore next */ []);
|
|
28300
|
+
expanded = signal(false, ...ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []);
|
|
28301
|
+
foldedHeight = signal(0, ...ngDevMode ? [{ debugName: "foldedHeight" }] : /* istanbul ignore next */ []);
|
|
28302
|
+
fullHeight = signal(0, ...ngDevMode ? [{ debugName: "fullHeight" }] : /* istanbul ignore next */ []);
|
|
28303
|
+
listHeight = computed(() => this.foldable() ? this.expanded() ? this.fullHeight() : this.foldedHeight() : null, ...ngDevMode ? [{ debugName: "listHeight" }] : /* istanbul ignore next */ []);
|
|
28304
|
+
toggle() {
|
|
28305
|
+
this.expanded.update((open) => !open);
|
|
28306
|
+
}
|
|
28098
28307
|
filters = input(void 0, ...ngDevMode ? [{ debugName: "filters" }] : /* istanbul ignore next */ []);
|
|
28099
28308
|
syncUrl = input(true, {
|
|
28100
28309
|
...ngDevMode ? { debugName: "syncUrl" } : /* istanbul ignore next */ {},
|
|
@@ -28108,10 +28317,62 @@ var AppliedFiltersComponent = class AppliedFiltersComponent {
|
|
|
28108
28317
|
scheme = input("grey", ...ngDevMode ? [{ debugName: "scheme" }] : /* istanbul ignore next */ []);
|
|
28109
28318
|
size = input("sm", ...ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []);
|
|
28110
28319
|
chipClass = input("", ...ngDevMode ? [{ debugName: "chipClass" }] : /* istanbul ignore next */ []);
|
|
28320
|
+
maxLines = input(2, {
|
|
28321
|
+
...ngDevMode ? { debugName: "maxLines" } : /* istanbul ignore next */ {},
|
|
28322
|
+
transform: numberAttribute
|
|
28323
|
+
});
|
|
28324
|
+
showClearAll = input(true, {
|
|
28325
|
+
...ngDevMode ? { debugName: "showClearAll" } : /* istanbul ignore next */ {},
|
|
28326
|
+
transform: booleanAttribute
|
|
28327
|
+
});
|
|
28111
28328
|
removed = output();
|
|
28112
28329
|
cleared = output();
|
|
28113
28330
|
filtersChange = output();
|
|
28114
28331
|
changed = output();
|
|
28332
|
+
constructor() {
|
|
28333
|
+
const destroyRef = inject(DestroyRef);
|
|
28334
|
+
afterRenderEffect(() => {
|
|
28335
|
+
this.chips();
|
|
28336
|
+
this.maxLines();
|
|
28337
|
+
this.expanded();
|
|
28338
|
+
this.measure();
|
|
28339
|
+
});
|
|
28340
|
+
afterRenderEffect((onCleanup) => {
|
|
28341
|
+
const element = this.list()?.nativeElement;
|
|
28342
|
+
if (!element || typeof ResizeObserver === "undefined") return;
|
|
28343
|
+
const observer = new ResizeObserver(() => this.measure());
|
|
28344
|
+
observer.observe(element);
|
|
28345
|
+
onCleanup(() => observer.disconnect());
|
|
28346
|
+
});
|
|
28347
|
+
destroyRef.onDestroy(() => this.foldable.set(false));
|
|
28348
|
+
}
|
|
28349
|
+
measure() {
|
|
28350
|
+
const element = this.list()?.nativeElement;
|
|
28351
|
+
const max = this.maxLines();
|
|
28352
|
+
if (!element || max <= 0) {
|
|
28353
|
+
this.foldable.set(false);
|
|
28354
|
+
return;
|
|
28355
|
+
}
|
|
28356
|
+
const items = Array.from(element.children);
|
|
28357
|
+
const first = items[0];
|
|
28358
|
+
if (!first) {
|
|
28359
|
+
this.foldable.set(false);
|
|
28360
|
+
return;
|
|
28361
|
+
}
|
|
28362
|
+
const origin = first.offsetTop;
|
|
28363
|
+
const tops = Array.from(new Set(items.map((item) => item.offsetTop - origin))).sort((a, b) => a - b);
|
|
28364
|
+
const gap = Number.parseFloat(getComputedStyle(element).rowGap) || 0;
|
|
28365
|
+
if (tops.length <= max) {
|
|
28366
|
+
this.foldable.set(false);
|
|
28367
|
+
items.forEach((item) => item.inert = false);
|
|
28368
|
+
return;
|
|
28369
|
+
}
|
|
28370
|
+
this.foldable.set(true);
|
|
28371
|
+
this.foldedHeight.set(Math.max(0, tops[max] - gap));
|
|
28372
|
+
this.fullHeight.set(element.scrollHeight);
|
|
28373
|
+
const folded = this.foldedHeight();
|
|
28374
|
+
items.forEach((item) => item.inert = !this.expanded() && item.offsetTop - origin >= folded);
|
|
28375
|
+
}
|
|
28115
28376
|
chips = computed(() => {
|
|
28116
28377
|
return (this.filters() ?? this.queryParamsStore.filters()).flatMap((source) => filterLeaves(source).flatMap((leaf) => chipsForLeaf(leaf, source)));
|
|
28117
28378
|
}, ...ngDevMode ? [{ debugName: "chips" }] : /* istanbul ignore next */ []);
|
|
@@ -28207,6 +28468,20 @@ var AppliedFiltersComponent = class AppliedFiltersComponent {
|
|
|
28207
28468
|
isSignal: true,
|
|
28208
28469
|
isRequired: false,
|
|
28209
28470
|
transformFunction: null
|
|
28471
|
+
},
|
|
28472
|
+
maxLines: {
|
|
28473
|
+
classPropertyName: "maxLines",
|
|
28474
|
+
publicName: "maxLines",
|
|
28475
|
+
isSignal: true,
|
|
28476
|
+
isRequired: false,
|
|
28477
|
+
transformFunction: null
|
|
28478
|
+
},
|
|
28479
|
+
showClearAll: {
|
|
28480
|
+
classPropertyName: "showClearAll",
|
|
28481
|
+
publicName: "showClearAll",
|
|
28482
|
+
isSignal: true,
|
|
28483
|
+
isRequired: false,
|
|
28484
|
+
transformFunction: null
|
|
28210
28485
|
}
|
|
28211
28486
|
},
|
|
28212
28487
|
outputs: {
|
|
@@ -28217,39 +28492,75 @@ var AppliedFiltersComponent = class AppliedFiltersComponent {
|
|
|
28217
28492
|
},
|
|
28218
28493
|
host: { properties: { "class.hidden": "chips().length === 0" } },
|
|
28219
28494
|
providers: [provideTranslocoScope("filters")],
|
|
28495
|
+
viewQueries: [{
|
|
28496
|
+
propertyName: "list",
|
|
28497
|
+
first: true,
|
|
28498
|
+
predicate: ["list"],
|
|
28499
|
+
descendants: true,
|
|
28500
|
+
isSignal: true
|
|
28501
|
+
}],
|
|
28220
28502
|
ngImport: i0,
|
|
28221
28503
|
template: `
|
|
28222
28504
|
@if (chips().length > 0) {
|
|
28223
|
-
|
|
28224
|
-
|
|
28225
|
-
|
|
28226
|
-
|
|
28227
|
-
|
|
28228
|
-
|
|
28229
|
-
|
|
28230
|
-
|
|
28231
|
-
|
|
28232
|
-
|
|
28233
|
-
|
|
28234
|
-
|
|
28235
|
-
|
|
28236
|
-
|
|
28237
|
-
|
|
28238
|
-
|
|
28239
|
-
[
|
|
28240
|
-
|
|
28241
|
-
[
|
|
28242
|
-
[
|
|
28243
|
-
(click)="remove(chip)"
|
|
28505
|
+
<!-- The zone the "show more" button belongs to: it appears when the pointer is on it or the focus is in it. -->
|
|
28506
|
+
<div class="group/applied relative" [class.pb-7]="foldable() && expanded()">
|
|
28507
|
+
<!-- Past maxLines lines the list is folded to that height — the rest is behind a fade, and a height that grows and
|
|
28508
|
+
shrinks shows it. The chips that fall in the folded part are inert (see measure): a remove button nobody can see
|
|
28509
|
+
must not stay in the tab order. The clip keeps a margin of a few pixels, so the focus ring of the visible chips is whole. -->
|
|
28510
|
+
<div
|
|
28511
|
+
#list
|
|
28512
|
+
[id]="listId"
|
|
28513
|
+
class="flex flex-wrap content-start items-center gap-1.5 [overflow-clip-margin:0.25rem] overflow-clip transition-[height] duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] motion-reduce:transition-none"
|
|
28514
|
+
[style.height.px]="listHeight()"
|
|
28515
|
+
[style.mask-image]="foldable() && !expanded() ? 'linear-gradient(to bottom, black calc(100% - 1.5rem), transparent)' : null"
|
|
28516
|
+
role="list"
|
|
28517
|
+
>
|
|
28518
|
+
@for (chip of chips(); track chip.id) {
|
|
28519
|
+
<tag
|
|
28520
|
+
role="listitem"
|
|
28521
|
+
[variant]="variant()"
|
|
28522
|
+
[scheme]="scheme()"
|
|
28523
|
+
[size]="size()"
|
|
28524
|
+
[class]="'max-w-full items-center gap-1 ' + chipClass()"
|
|
28244
28525
|
>
|
|
28245
|
-
|
|
28526
|
+
@if (showField()) {
|
|
28527
|
+
<span>{{ (chip.name ?? chip.field) | syslang }}:</span>
|
|
28528
|
+
}
|
|
28529
|
+
<span class="truncate">{{ chip.display | syslang }}</span>
|
|
28530
|
+
<button
|
|
28531
|
+
variant="none"
|
|
28532
|
+
size="xs"
|
|
28533
|
+
[iconOnly]="true"
|
|
28534
|
+
class="shrink-0"
|
|
28535
|
+
[attr.aria-label]="('filters.removeFilter' | transloco) + ': ' + chip.display"
|
|
28536
|
+
[title]="('filters.removeFilter' | transloco) + ': ' + chip.display"
|
|
28537
|
+
(click)="remove(chip)"
|
|
28538
|
+
>
|
|
28539
|
+
<x-mark-icon class="size-3" />
|
|
28540
|
+
</button>
|
|
28541
|
+
</tag>
|
|
28542
|
+
}
|
|
28543
|
+
|
|
28544
|
+
@if (showClearAll() && chips().length > 1) {
|
|
28545
|
+
<button variant="tertiary" size="xs" (click)="clear()">
|
|
28546
|
+
{{ "filters.clearAllFilters" | transloco }}
|
|
28246
28547
|
</button>
|
|
28247
|
-
|
|
28248
|
-
|
|
28548
|
+
}
|
|
28549
|
+
</div>
|
|
28249
28550
|
|
|
28250
|
-
@if (
|
|
28251
|
-
|
|
28252
|
-
|
|
28551
|
+
@if (foldable()) {
|
|
28552
|
+
<!-- Out of sight until the zone is hovered or holds the focus — and always there on a screen with no hover. Still in the
|
|
28553
|
+
tab order, and revealed by the focus. Its own surface, since it lies over the chips. -->
|
|
28554
|
+
<button
|
|
28555
|
+
type="button"
|
|
28556
|
+
variant="secondary"
|
|
28557
|
+
size="xs"
|
|
28558
|
+
class="absolute right-0 bottom-0 bg-(--bg-neutral-white) opacity-0 shadow-(--shadow-1) transition-opacity duration-150 group-hover/applied:opacity-100 group-focus-within/applied:opacity-100 focus-visible:opacity-100 motion-reduce:transition-none [@media(hover:none)]:opacity-100"
|
|
28559
|
+
[attr.aria-expanded]="expanded()"
|
|
28560
|
+
[attr.aria-controls]="listId"
|
|
28561
|
+
(click)="toggle()"
|
|
28562
|
+
>
|
|
28563
|
+
{{ (expanded() ? "filters.showLess" : "filters.showMore") | transloco }}
|
|
28253
28564
|
</button>
|
|
28254
28565
|
}
|
|
28255
28566
|
</div>
|
|
@@ -28320,36 +28631,65 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
28320
28631
|
providers: [provideTranslocoScope("filters")],
|
|
28321
28632
|
template: `
|
|
28322
28633
|
@if (chips().length > 0) {
|
|
28323
|
-
|
|
28324
|
-
|
|
28325
|
-
|
|
28326
|
-
|
|
28327
|
-
|
|
28328
|
-
|
|
28329
|
-
|
|
28330
|
-
|
|
28331
|
-
|
|
28332
|
-
|
|
28333
|
-
|
|
28334
|
-
|
|
28335
|
-
|
|
28336
|
-
|
|
28337
|
-
|
|
28338
|
-
|
|
28339
|
-
[
|
|
28340
|
-
|
|
28341
|
-
[
|
|
28342
|
-
[
|
|
28343
|
-
(click)="remove(chip)"
|
|
28634
|
+
<!-- The zone the "show more" button belongs to: it appears when the pointer is on it or the focus is in it. -->
|
|
28635
|
+
<div class="group/applied relative" [class.pb-7]="foldable() && expanded()">
|
|
28636
|
+
<!-- Past maxLines lines the list is folded to that height — the rest is behind a fade, and a height that grows and
|
|
28637
|
+
shrinks shows it. The chips that fall in the folded part are inert (see measure): a remove button nobody can see
|
|
28638
|
+
must not stay in the tab order. The clip keeps a margin of a few pixels, so the focus ring of the visible chips is whole. -->
|
|
28639
|
+
<div
|
|
28640
|
+
#list
|
|
28641
|
+
[id]="listId"
|
|
28642
|
+
class="flex flex-wrap content-start items-center gap-1.5 [overflow-clip-margin:0.25rem] overflow-clip transition-[height] duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] motion-reduce:transition-none"
|
|
28643
|
+
[style.height.px]="listHeight()"
|
|
28644
|
+
[style.mask-image]="foldable() && !expanded() ? 'linear-gradient(to bottom, black calc(100% - 1.5rem), transparent)' : null"
|
|
28645
|
+
role="list"
|
|
28646
|
+
>
|
|
28647
|
+
@for (chip of chips(); track chip.id) {
|
|
28648
|
+
<tag
|
|
28649
|
+
role="listitem"
|
|
28650
|
+
[variant]="variant()"
|
|
28651
|
+
[scheme]="scheme()"
|
|
28652
|
+
[size]="size()"
|
|
28653
|
+
[class]="'max-w-full items-center gap-1 ' + chipClass()"
|
|
28344
28654
|
>
|
|
28345
|
-
|
|
28655
|
+
@if (showField()) {
|
|
28656
|
+
<span>{{ (chip.name ?? chip.field) | syslang }}:</span>
|
|
28657
|
+
}
|
|
28658
|
+
<span class="truncate">{{ chip.display | syslang }}</span>
|
|
28659
|
+
<button
|
|
28660
|
+
variant="none"
|
|
28661
|
+
size="xs"
|
|
28662
|
+
[iconOnly]="true"
|
|
28663
|
+
class="shrink-0"
|
|
28664
|
+
[attr.aria-label]="('filters.removeFilter' | transloco) + ': ' + chip.display"
|
|
28665
|
+
[title]="('filters.removeFilter' | transloco) + ': ' + chip.display"
|
|
28666
|
+
(click)="remove(chip)"
|
|
28667
|
+
>
|
|
28668
|
+
<x-mark-icon class="size-3" />
|
|
28669
|
+
</button>
|
|
28670
|
+
</tag>
|
|
28671
|
+
}
|
|
28672
|
+
|
|
28673
|
+
@if (showClearAll() && chips().length > 1) {
|
|
28674
|
+
<button variant="tertiary" size="xs" (click)="clear()">
|
|
28675
|
+
{{ "filters.clearAllFilters" | transloco }}
|
|
28346
28676
|
</button>
|
|
28347
|
-
|
|
28348
|
-
|
|
28677
|
+
}
|
|
28678
|
+
</div>
|
|
28349
28679
|
|
|
28350
|
-
@if (
|
|
28351
|
-
|
|
28352
|
-
|
|
28680
|
+
@if (foldable()) {
|
|
28681
|
+
<!-- Out of sight until the zone is hovered or holds the focus — and always there on a screen with no hover. Still in the
|
|
28682
|
+
tab order, and revealed by the focus. Its own surface, since it lies over the chips. -->
|
|
28683
|
+
<button
|
|
28684
|
+
type="button"
|
|
28685
|
+
variant="secondary"
|
|
28686
|
+
size="xs"
|
|
28687
|
+
class="absolute right-0 bottom-0 bg-(--bg-neutral-white) opacity-0 shadow-(--shadow-1) transition-opacity duration-150 group-hover/applied:opacity-100 group-focus-within/applied:opacity-100 focus-visible:opacity-100 motion-reduce:transition-none [@media(hover:none)]:opacity-100"
|
|
28688
|
+
[attr.aria-expanded]="expanded()"
|
|
28689
|
+
[attr.aria-controls]="listId"
|
|
28690
|
+
(click)="toggle()"
|
|
28691
|
+
>
|
|
28692
|
+
{{ (expanded() ? "filters.showLess" : "filters.showMore") | transloco }}
|
|
28353
28693
|
</button>
|
|
28354
28694
|
}
|
|
28355
28695
|
</div>
|
|
@@ -28358,7 +28698,12 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
28358
28698
|
host: { "[class.hidden]": "chips().length === 0" }
|
|
28359
28699
|
}]
|
|
28360
28700
|
}],
|
|
28701
|
+
ctorParameters: () => [],
|
|
28361
28702
|
propDecorators: {
|
|
28703
|
+
list: [{
|
|
28704
|
+
type: i0.ViewChild,
|
|
28705
|
+
args: ["list", { isSignal: true }]
|
|
28706
|
+
}],
|
|
28362
28707
|
filters: [{
|
|
28363
28708
|
type: i0.Input,
|
|
28364
28709
|
args: [{
|
|
@@ -28415,6 +28760,22 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
28415
28760
|
required: false
|
|
28416
28761
|
}]
|
|
28417
28762
|
}],
|
|
28763
|
+
maxLines: [{
|
|
28764
|
+
type: i0.Input,
|
|
28765
|
+
args: [{
|
|
28766
|
+
isSignal: true,
|
|
28767
|
+
alias: "maxLines",
|
|
28768
|
+
required: false
|
|
28769
|
+
}]
|
|
28770
|
+
}],
|
|
28771
|
+
showClearAll: [{
|
|
28772
|
+
type: i0.Input,
|
|
28773
|
+
args: [{
|
|
28774
|
+
isSignal: true,
|
|
28775
|
+
alias: "showClearAll",
|
|
28776
|
+
required: false
|
|
28777
|
+
}]
|
|
28778
|
+
}],
|
|
28418
28779
|
removed: [{
|
|
28419
28780
|
type: i0.Output,
|
|
28420
28781
|
args: ["removed"]
|
|
@@ -31619,6 +31980,1049 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
31619
31980
|
}]
|
|
31620
31981
|
}
|
|
31621
31982
|
});
|
|
31983
|
+
function filterLabel(filter) {
|
|
31984
|
+
return filter.name || filter.field || "";
|
|
31985
|
+
}
|
|
31986
|
+
function filterValue(filter) {
|
|
31987
|
+
if (filter.filters?.length) return filter.filters.map((f) => filterLabel(f) ? `${filterLabel(f)}: ${filterValue(f)}` : filterValue(f)).join(", ");
|
|
31988
|
+
if (filter.values?.length) return filter.values.join(", ");
|
|
31989
|
+
return filter.display ?? filter.value ?? [filter.start, filter.end].filter((v) => v !== void 0 && v !== "").join(" – ");
|
|
31990
|
+
}
|
|
31991
|
+
var QuickFiltersListComponent = class QuickFiltersListComponent {
|
|
31992
|
+
searchable = input(false, ...ngDevMode ? [{ debugName: "searchable" }] : /* istanbul ignore next */ []);
|
|
31993
|
+
deletable = input(true, ...ngDevMode ? [{ debugName: "deletable" }] : /* istanbul ignore next */ []);
|
|
31994
|
+
emptyHint = input(...ngDevMode ? [void 0, { debugName: "emptyHint" }] : /* istanbul ignore next */ []);
|
|
31995
|
+
applied = output();
|
|
31996
|
+
deleted = output();
|
|
31997
|
+
userSettingsStore = inject(UserSettingsStore);
|
|
31998
|
+
queryParamsStore = inject(QueryParamsStore);
|
|
31999
|
+
appStore = inject(AppStore);
|
|
32000
|
+
transloco = inject(TranslocoService);
|
|
32001
|
+
host = inject(ElementRef);
|
|
32002
|
+
injector = inject(Injector);
|
|
32003
|
+
deleteKeys = isApplePlatform() ? ["Delete", "Backspace"] : ["Delete"];
|
|
32004
|
+
quickFilters = computed(() => this.userSettingsStore.quickFilters() ?? [], ...ngDevMode ? [{ debugName: "quickFilters" }] : /* istanbul ignore next */ []);
|
|
32005
|
+
query = signal("", ...ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []);
|
|
32006
|
+
filtered = computed(() => {
|
|
32007
|
+
const query = this.query().trim().toLowerCase();
|
|
32008
|
+
return query ? this.quickFilters().filter((quickFilter) => quickFilter.name.toLowerCase().includes(query)) : this.quickFilters();
|
|
32009
|
+
}, ...ngDevMode ? [{ debugName: "filtered" }] : /* istanbul ignore next */ []);
|
|
32010
|
+
announcement = signal("", ...ngDevMode ? [{ debugName: "announcement" }] : /* istanbul ignore next */ []);
|
|
32011
|
+
details = signal(null, ...ngDevMode ? [{ debugName: "details" }] : /* istanbul ignore next */ []);
|
|
32012
|
+
detailsCard = viewChild("detailsCard", ...ngDevMode ? [{ debugName: "detailsCard" }] : /* istanbul ignore next */ []);
|
|
32013
|
+
hideTimer;
|
|
32014
|
+
deleting = signal(false, ...ngDevMode ? [{ debugName: "deleting" }] : /* istanbul ignore next */ []);
|
|
32015
|
+
constructor() {
|
|
32016
|
+
injectFloatingPanel({
|
|
32017
|
+
panel: () => this.detailsCard()?.nativeElement,
|
|
32018
|
+
anchor: () => this.details()?.anchor,
|
|
32019
|
+
placement: "bottom-start",
|
|
32020
|
+
offset: 4,
|
|
32021
|
+
topLayer: true
|
|
32022
|
+
});
|
|
32023
|
+
inject(DestroyRef).onDestroy(() => this.cancelHideDetails());
|
|
32024
|
+
}
|
|
32025
|
+
chips(quickFilter) {
|
|
32026
|
+
return quickFilter.filters.map((filter) => {
|
|
32027
|
+
const icon = (filter.field ? this.appStore.getAggregationCustomization(filter.field, filter.name) : void 0)?.icon;
|
|
32028
|
+
const value = filterValue(filter);
|
|
32029
|
+
const label = filterLabel(filter);
|
|
32030
|
+
return {
|
|
32031
|
+
text: icon || !label || !value ? value || label : `${label}: ${value}`,
|
|
32032
|
+
icon
|
|
32033
|
+
};
|
|
32034
|
+
}).filter((chip) => chip.text);
|
|
32035
|
+
}
|
|
32036
|
+
showDetails(quickFilter, anchor) {
|
|
32037
|
+
this.cancelHideDetails();
|
|
32038
|
+
this.details.set({
|
|
32039
|
+
quickFilter,
|
|
32040
|
+
anchor
|
|
32041
|
+
});
|
|
32042
|
+
}
|
|
32043
|
+
scheduleHideDetails() {
|
|
32044
|
+
this.cancelHideDetails();
|
|
32045
|
+
this.hideTimer = setTimeout(() => this.details.set(null), 100);
|
|
32046
|
+
}
|
|
32047
|
+
cancelHideDetails() {
|
|
32048
|
+
clearTimeout(this.hideTimer);
|
|
32049
|
+
}
|
|
32050
|
+
onEscape(event) {
|
|
32051
|
+
if (!this.details()) return;
|
|
32052
|
+
event.stopPropagation();
|
|
32053
|
+
this.cancelHideDetails();
|
|
32054
|
+
this.details.set(null);
|
|
32055
|
+
}
|
|
32056
|
+
picked = signal([], ...ngDevMode ? [{ debugName: "picked" }] : /* istanbul ignore next */ []);
|
|
32057
|
+
pick(values) {
|
|
32058
|
+
const quickFilter = values.at(-1);
|
|
32059
|
+
this.picked.set([]);
|
|
32060
|
+
if (quickFilter) this.apply(quickFilter);
|
|
32061
|
+
}
|
|
32062
|
+
apply(quickFilter) {
|
|
32063
|
+
this.cancelHideDetails();
|
|
32064
|
+
this.details.set(null);
|
|
32065
|
+
this.queryParamsStore.patch({ filters: structuredClone(quickFilter.filters) });
|
|
32066
|
+
this.applied.emit(quickFilter);
|
|
32067
|
+
}
|
|
32068
|
+
async remove(quickFilter, index, event) {
|
|
32069
|
+
event.stopPropagation();
|
|
32070
|
+
if (this.deleting()) return;
|
|
32071
|
+
this.deleting.set(true);
|
|
32072
|
+
this.cancelHideDetails();
|
|
32073
|
+
this.details.set(null);
|
|
32074
|
+
const hadFocus = this.host.nativeElement.contains(document.activeElement);
|
|
32075
|
+
try {
|
|
32076
|
+
await this.userSettingsStore.removeQuickFilter(quickFilter.id);
|
|
32077
|
+
const message = this.transloco.translate("quickFilters.deleted", { name: quickFilter.name });
|
|
32078
|
+
notify.success(message, { duration: 2e3 });
|
|
32079
|
+
this.announcement.set(message);
|
|
32080
|
+
this.deleted.emit(quickFilter);
|
|
32081
|
+
if (hadFocus) afterNextRender(() => {
|
|
32082
|
+
const rows = this.host.nativeElement.querySelectorAll("[role='option']");
|
|
32083
|
+
(rows.length ? rows[Math.min(index, rows.length - 1)] : this.host.nativeElement).focus();
|
|
32084
|
+
}, { injector: this.injector });
|
|
32085
|
+
} finally {
|
|
32086
|
+
this.deleting.set(false);
|
|
32087
|
+
}
|
|
32088
|
+
}
|
|
32089
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
32090
|
+
minVersion: "12.0.0",
|
|
32091
|
+
version: "22.2.1",
|
|
32092
|
+
ngImport: i0,
|
|
32093
|
+
type: QuickFiltersListComponent,
|
|
32094
|
+
deps: [],
|
|
32095
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
32096
|
+
});
|
|
32097
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
32098
|
+
minVersion: "17.0.0",
|
|
32099
|
+
version: "22.2.1",
|
|
32100
|
+
type: QuickFiltersListComponent,
|
|
32101
|
+
isStandalone: true,
|
|
32102
|
+
selector: "QuickFiltersList, quick-filters-list",
|
|
32103
|
+
inputs: {
|
|
32104
|
+
searchable: {
|
|
32105
|
+
classPropertyName: "searchable",
|
|
32106
|
+
publicName: "searchable",
|
|
32107
|
+
isSignal: true,
|
|
32108
|
+
isRequired: false,
|
|
32109
|
+
transformFunction: null
|
|
32110
|
+
},
|
|
32111
|
+
deletable: {
|
|
32112
|
+
classPropertyName: "deletable",
|
|
32113
|
+
publicName: "deletable",
|
|
32114
|
+
isSignal: true,
|
|
32115
|
+
isRequired: false,
|
|
32116
|
+
transformFunction: null
|
|
32117
|
+
},
|
|
32118
|
+
emptyHint: {
|
|
32119
|
+
classPropertyName: "emptyHint",
|
|
32120
|
+
publicName: "emptyHint",
|
|
32121
|
+
isSignal: true,
|
|
32122
|
+
isRequired: false,
|
|
32123
|
+
transformFunction: null
|
|
32124
|
+
}
|
|
32125
|
+
},
|
|
32126
|
+
outputs: {
|
|
32127
|
+
applied: "applied",
|
|
32128
|
+
deleted: "deleted"
|
|
32129
|
+
},
|
|
32130
|
+
host: {
|
|
32131
|
+
attributes: { "tabindex": "-1" },
|
|
32132
|
+
listeners: { "keydown.escape": "onEscape($event)" },
|
|
32133
|
+
classAttribute: "flex min-h-0 flex-col gap-(--spacing-xs) outline-none"
|
|
32134
|
+
},
|
|
32135
|
+
providers: [provideTranslocoScope("quick-filters")],
|
|
32136
|
+
viewQueries: [{
|
|
32137
|
+
propertyName: "detailsCard",
|
|
32138
|
+
first: true,
|
|
32139
|
+
predicate: ["detailsCard"],
|
|
32140
|
+
descendants: true,
|
|
32141
|
+
isSignal: true
|
|
32142
|
+
}],
|
|
32143
|
+
ngImport: i0,
|
|
32144
|
+
template: `
|
|
32145
|
+
@if (searchable() && quickFilters().length > 0) {
|
|
32146
|
+
<SearchInput
|
|
32147
|
+
size="md"
|
|
32148
|
+
class="border-(--stroke-primary-light)"
|
|
32149
|
+
[(value)]="query"
|
|
32150
|
+
[placeholder]="'quickFilters.search' | transloco"
|
|
32151
|
+
[aria-label]="'quickFilters.search' | transloco"
|
|
32152
|
+
[clearLabel]="'quickFilters.clearSearch' | transloco" />
|
|
32153
|
+
}
|
|
32154
|
+
|
|
32155
|
+
@if (filtered().length) {
|
|
32156
|
+
<!-- The galactik List, as the design's List Item: a listbox of menu rows with an icon, the name and the number of filters.
|
|
32157
|
+
Choosing a row (click, Enter) applies it; the selection is not kept. The cross at the end of the row deletes it. -->
|
|
32158
|
+
<List
|
|
32159
|
+
variant="list-menu"
|
|
32160
|
+
size="medium"
|
|
32161
|
+
selectionMode="explicit"
|
|
32162
|
+
class="-mx-1 min-h-0 flex-1 overflow-auto px-1 [scrollbar-color:color-mix(in_srgb,currentColor_40%,transparent)_transparent] [scrollbar-width:thin]"
|
|
32163
|
+
[attr.aria-label]="'quickFilters.list' | transloco"
|
|
32164
|
+
[value]="picked()"
|
|
32165
|
+
(valueChange)="pick($event)">
|
|
32166
|
+
@for (quickFilter of filtered(); track quickFilter.id; let index = $index) {
|
|
32167
|
+
<ListItem
|
|
32168
|
+
[value]="quickFilter"
|
|
32169
|
+
[label]="quickFilter.name"
|
|
32170
|
+
(focusin)="showDetails(quickFilter, count)"
|
|
32171
|
+
(focusout)="scheduleHideDetails()">
|
|
32172
|
+
<ListItemLeft>
|
|
32173
|
+
<ListItemIcon><quick-filter-icon /></ListItemIcon>
|
|
32174
|
+
<ListItemText>
|
|
32175
|
+
<!-- The tooltip shows the whole name, and only when it is cut off: a full name needs none. -->
|
|
32176
|
+
<span truncated #cut="truncated" [tooltip]="quickFilter.name" [tooltipDisabled]="!cut.isTruncated()" class="block truncate">{{ quickFilter.name }}</span>
|
|
32177
|
+
<!-- The line of the count of filters is the one that shows what the quick filter holds, on hover (the row itself only applies
|
|
32178
|
+
it); on the keyboard, the row having the focus shows it. It takes the look of the subtext of a row. -->
|
|
32179
|
+
<span
|
|
32180
|
+
#count
|
|
32181
|
+
data-count
|
|
32182
|
+
class="list-item-subtext block w-fit! decoration-dotted underline-offset-2 hover:underline"
|
|
32183
|
+
(mouseenter)="showDetails(quickFilter, count)"
|
|
32184
|
+
(mouseleave)="scheduleHideDetails()">
|
|
32185
|
+
{{ (quickFilter.filters.length === 1 ? "quickFilters.countOne" : "quickFilters.countOther") | transloco: { count: quickFilter.filters.length } }}
|
|
32186
|
+
</span>
|
|
32187
|
+
</ListItemText>
|
|
32188
|
+
</ListItemLeft>
|
|
32189
|
+
@if (deletable()) {
|
|
32190
|
+
<ListItemAction
|
|
32191
|
+
[ariaLabel]="'quickFilters.delete' | transloco: { name: quickFilter.name }"
|
|
32192
|
+
[keys]="deleteKeys"
|
|
32193
|
+
[shortcut]="'quickFilters.deleteShortcut' | transloco"
|
|
32194
|
+
[description]="'quickFilters.deleteDescription' | transloco: { key: 'quickFilters.deleteShortcut' | transloco }"
|
|
32195
|
+
(click)="remove(quickFilter, index, $event)">
|
|
32196
|
+
<x-mark-icon />
|
|
32197
|
+
</ListItemAction>
|
|
32198
|
+
}
|
|
32199
|
+
</ListItem>
|
|
32200
|
+
}
|
|
32201
|
+
</List>
|
|
32202
|
+
|
|
32203
|
+
<!-- The hover card of the design: the filters a quick filter holds, as chips, hanging under its row. Placed with Floating UI in
|
|
32204
|
+
the top layer, so it is above whatever clips the list. It stays while the pointer is on it, and while the focus is on the row. -->
|
|
32205
|
+
@if (details(); as shown) {
|
|
32206
|
+
<div
|
|
32207
|
+
#detailsCard
|
|
32208
|
+
role="tooltip"
|
|
32209
|
+
class="fixed m-0 flex max-w-72 flex-wrap gap-(--spacing-3xs) rounded-(--radius-lg) border border-solid border-(--stroke-neutral-white) bg-(--bg-neutral-white) p-(--spacing-md) shadow-(--shadow-2)"
|
|
32210
|
+
(mouseenter)="cancelHideDetails()"
|
|
32211
|
+
(mouseleave)="scheduleHideDetails()">
|
|
32212
|
+
@for (chip of chips(shown.quickFilter); track $index) {
|
|
32213
|
+
<span
|
|
32214
|
+
data-chip
|
|
32215
|
+
class="inline-flex max-w-full min-w-0 items-center gap-(--spacing-3xs) rounded-(--radius-rounded) bg-(--bg-neutral-base) py-(--spacing-4xs) pr-(--spacing-2xs) pl-(--spacing-2xs) text-(length:--size-sm) font-medium text-(--font-neutral-base)">
|
|
32216
|
+
<!-- the icon the application gives the aggregation in the filters custom JSON, as on the filter button -->
|
|
32217
|
+
@if (chip.icon) {
|
|
32218
|
+
<fa-icon [faClass]="chip.icon" class="shrink-0" />
|
|
32219
|
+
}
|
|
32220
|
+
<!-- A chip that is cut off shows its whole text in a tooltip; one that fits has none. -->
|
|
32221
|
+
<span class="truncate" truncated #cutChip="truncated" [tooltip]="chip.text" [tooltipDisabled]="!cutChip.isTruncated()">{{ chip.text }}</span>
|
|
32222
|
+
</span>
|
|
32223
|
+
}
|
|
32224
|
+
</div>
|
|
32225
|
+
}
|
|
32226
|
+
} @else {
|
|
32227
|
+
<!-- The empty-state rule of the list features: the feature's icon on a coloured tile, a title, one hint. -->
|
|
32228
|
+
<div class="flex flex-col items-center gap-(--spacing-xs) px-(--spacing-md) py-(--spacing-lg) text-center">
|
|
32229
|
+
<span class="inline-flex size-12 items-center justify-center rounded-(--radius-md) bg-(--bg-cyan-base-alt) text-(--font-cyan-muted)" aria-hidden="true">
|
|
32230
|
+
<quick-filter-icon class="size-6 opacity-80" />
|
|
32231
|
+
</span>
|
|
32232
|
+
<span class="text-(length:--size-base) font-semibold text-(--font-neutral-base)">{{ (query() ? "quickFilters.noMatch" : "quickFilters.empty") | transloco }}</span>
|
|
32233
|
+
@if (!query()) {
|
|
32234
|
+
<span class="text-(length:--size-sm) text-(--font-neutral-muted)">{{ emptyHint() ?? ("quickFilters.emptyHint" | transloco) }}</span>
|
|
32235
|
+
}
|
|
32236
|
+
</div>
|
|
32237
|
+
}
|
|
32238
|
+
|
|
32239
|
+
<span role="status" class="sr-only">{{ announcement() }}</span>
|
|
32240
|
+
`,
|
|
32241
|
+
isInline: true,
|
|
32242
|
+
dependencies: [
|
|
32243
|
+
{
|
|
32244
|
+
kind: "component",
|
|
32245
|
+
type: FaIconComponent,
|
|
32246
|
+
selector: "fa-icon, FaIcon",
|
|
32247
|
+
inputs: ["faClass", "class"]
|
|
32248
|
+
},
|
|
32249
|
+
{
|
|
32250
|
+
kind: "directive",
|
|
32251
|
+
type: ListComponent,
|
|
32252
|
+
selector: "List,list",
|
|
32253
|
+
inputs: [
|
|
32254
|
+
"class",
|
|
32255
|
+
"variant",
|
|
32256
|
+
"size"
|
|
32257
|
+
],
|
|
32258
|
+
exportAs: ["list"]
|
|
32259
|
+
},
|
|
32260
|
+
{
|
|
32261
|
+
kind: "directive",
|
|
32262
|
+
type: ListItemComponent,
|
|
32263
|
+
selector: "ListItem,list-item,listitem",
|
|
32264
|
+
inputs: [
|
|
32265
|
+
"class",
|
|
32266
|
+
"variant",
|
|
32267
|
+
"size",
|
|
32268
|
+
"depth"
|
|
32269
|
+
],
|
|
32270
|
+
exportAs: ["listItem"]
|
|
32271
|
+
},
|
|
32272
|
+
{
|
|
32273
|
+
kind: "component",
|
|
32274
|
+
type: ListItemActionComponent,
|
|
32275
|
+
selector: "ListItemAction,list-item-action,listitemaction",
|
|
32276
|
+
inputs: [
|
|
32277
|
+
"ariaLabel",
|
|
32278
|
+
"disabled",
|
|
32279
|
+
"keys",
|
|
32280
|
+
"shortcut",
|
|
32281
|
+
"description"
|
|
32282
|
+
],
|
|
32283
|
+
exportAs: ["listItemAction"]
|
|
32284
|
+
},
|
|
32285
|
+
{
|
|
32286
|
+
kind: "directive",
|
|
32287
|
+
type: ListItemIconDirective,
|
|
32288
|
+
selector: "ListItemIcon,list-item-icon,listitemicon",
|
|
32289
|
+
inputs: ["class"]
|
|
32290
|
+
},
|
|
32291
|
+
{
|
|
32292
|
+
kind: "directive",
|
|
32293
|
+
type: ListItemLeftDirective,
|
|
32294
|
+
selector: "ListItemLeft,list-item-left,listitemleft",
|
|
32295
|
+
inputs: ["class"]
|
|
32296
|
+
},
|
|
32297
|
+
{
|
|
32298
|
+
kind: "component",
|
|
32299
|
+
type: ListItemTextComponent,
|
|
32300
|
+
selector: "ListItemText,list-item-text,listitemtext",
|
|
32301
|
+
inputs: ["class", "subtext"],
|
|
32302
|
+
exportAs: ["listItemText"]
|
|
32303
|
+
},
|
|
32304
|
+
{
|
|
32305
|
+
kind: "component",
|
|
32306
|
+
type: QuickFilterIcon,
|
|
32307
|
+
selector: "quick-filter, quick-filter-icon, QuickFilter, QuickFilterIcon",
|
|
32308
|
+
inputs: ["class"]
|
|
32309
|
+
},
|
|
32310
|
+
{
|
|
32311
|
+
kind: "component",
|
|
32312
|
+
type: SearchInputComponent,
|
|
32313
|
+
selector: "SearchInput:not([suggestions]), search-input:not([suggestions]), searchinput:not([suggestions])"
|
|
32314
|
+
},
|
|
32315
|
+
{
|
|
32316
|
+
kind: "directive",
|
|
32317
|
+
type: TooltipDirective,
|
|
32318
|
+
selector: "[tooltip]",
|
|
32319
|
+
inputs: [
|
|
32320
|
+
"tooltip",
|
|
32321
|
+
"tooltipPosition",
|
|
32322
|
+
"tooltipEvent",
|
|
32323
|
+
"showDelay",
|
|
32324
|
+
"hideDelay",
|
|
32325
|
+
"life",
|
|
32326
|
+
"tooltipOffset",
|
|
32327
|
+
"autoHide",
|
|
32328
|
+
"hideOnEscape",
|
|
32329
|
+
"escape",
|
|
32330
|
+
"tooltipDisabled",
|
|
32331
|
+
"tooltipArrow",
|
|
32332
|
+
"tooltipStyleClass",
|
|
32333
|
+
"tooltipOptions",
|
|
32334
|
+
"tooltip-position",
|
|
32335
|
+
"tooltip-delay",
|
|
32336
|
+
"tooltip-duration",
|
|
32337
|
+
"tooltip-offset"
|
|
32338
|
+
],
|
|
32339
|
+
exportAs: ["tooltip"]
|
|
32340
|
+
},
|
|
32341
|
+
{
|
|
32342
|
+
kind: "directive",
|
|
32343
|
+
type: TruncatedDirective,
|
|
32344
|
+
selector: "[truncated]",
|
|
32345
|
+
inputs: ["truncated"],
|
|
32346
|
+
exportAs: ["truncated"]
|
|
32347
|
+
},
|
|
32348
|
+
{
|
|
32349
|
+
kind: "component",
|
|
32350
|
+
type: XMarkIcon,
|
|
32351
|
+
selector: "x-mark, x-mark-icon, XMark, XMarkIcon",
|
|
32352
|
+
inputs: ["class"]
|
|
32353
|
+
},
|
|
32354
|
+
{
|
|
32355
|
+
kind: "pipe",
|
|
32356
|
+
type: TranslocoPipe,
|
|
32357
|
+
name: "transloco"
|
|
32358
|
+
}
|
|
32359
|
+
]
|
|
32360
|
+
});
|
|
32361
|
+
};
|
|
32362
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
32363
|
+
minVersion: "12.0.0",
|
|
32364
|
+
version: "22.2.1",
|
|
32365
|
+
ngImport: i0,
|
|
32366
|
+
type: QuickFiltersListComponent,
|
|
32367
|
+
decorators: [{
|
|
32368
|
+
type: Component,
|
|
32369
|
+
args: [{
|
|
32370
|
+
selector: "QuickFiltersList, quick-filters-list",
|
|
32371
|
+
imports: [
|
|
32372
|
+
TranslocoPipe,
|
|
32373
|
+
FaIconComponent,
|
|
32374
|
+
ListComponent,
|
|
32375
|
+
ListItemComponent,
|
|
32376
|
+
ListItemActionComponent,
|
|
32377
|
+
ListItemIconDirective,
|
|
32378
|
+
ListItemLeftDirective,
|
|
32379
|
+
ListItemTextComponent,
|
|
32380
|
+
QuickFilterIcon,
|
|
32381
|
+
SearchInputComponent,
|
|
32382
|
+
TooltipDirective,
|
|
32383
|
+
TruncatedDirective,
|
|
32384
|
+
XMarkIcon
|
|
32385
|
+
],
|
|
32386
|
+
providers: [provideTranslocoScope("quick-filters")],
|
|
32387
|
+
host: {
|
|
32388
|
+
class: "flex min-h-0 flex-col gap-(--spacing-xs) outline-none",
|
|
32389
|
+
tabindex: "-1",
|
|
32390
|
+
"(keydown.escape)": "onEscape($event)"
|
|
32391
|
+
},
|
|
32392
|
+
template: `
|
|
32393
|
+
@if (searchable() && quickFilters().length > 0) {
|
|
32394
|
+
<SearchInput
|
|
32395
|
+
size="md"
|
|
32396
|
+
class="border-(--stroke-primary-light)"
|
|
32397
|
+
[(value)]="query"
|
|
32398
|
+
[placeholder]="'quickFilters.search' | transloco"
|
|
32399
|
+
[aria-label]="'quickFilters.search' | transloco"
|
|
32400
|
+
[clearLabel]="'quickFilters.clearSearch' | transloco" />
|
|
32401
|
+
}
|
|
32402
|
+
|
|
32403
|
+
@if (filtered().length) {
|
|
32404
|
+
<!-- The galactik List, as the design's List Item: a listbox of menu rows with an icon, the name and the number of filters.
|
|
32405
|
+
Choosing a row (click, Enter) applies it; the selection is not kept. The cross at the end of the row deletes it. -->
|
|
32406
|
+
<List
|
|
32407
|
+
variant="list-menu"
|
|
32408
|
+
size="medium"
|
|
32409
|
+
selectionMode="explicit"
|
|
32410
|
+
class="-mx-1 min-h-0 flex-1 overflow-auto px-1 [scrollbar-color:color-mix(in_srgb,currentColor_40%,transparent)_transparent] [scrollbar-width:thin]"
|
|
32411
|
+
[attr.aria-label]="'quickFilters.list' | transloco"
|
|
32412
|
+
[value]="picked()"
|
|
32413
|
+
(valueChange)="pick($event)">
|
|
32414
|
+
@for (quickFilter of filtered(); track quickFilter.id; let index = $index) {
|
|
32415
|
+
<ListItem
|
|
32416
|
+
[value]="quickFilter"
|
|
32417
|
+
[label]="quickFilter.name"
|
|
32418
|
+
(focusin)="showDetails(quickFilter, count)"
|
|
32419
|
+
(focusout)="scheduleHideDetails()">
|
|
32420
|
+
<ListItemLeft>
|
|
32421
|
+
<ListItemIcon><quick-filter-icon /></ListItemIcon>
|
|
32422
|
+
<ListItemText>
|
|
32423
|
+
<!-- The tooltip shows the whole name, and only when it is cut off: a full name needs none. -->
|
|
32424
|
+
<span truncated #cut="truncated" [tooltip]="quickFilter.name" [tooltipDisabled]="!cut.isTruncated()" class="block truncate">{{ quickFilter.name }}</span>
|
|
32425
|
+
<!-- The line of the count of filters is the one that shows what the quick filter holds, on hover (the row itself only applies
|
|
32426
|
+
it); on the keyboard, the row having the focus shows it. It takes the look of the subtext of a row. -->
|
|
32427
|
+
<span
|
|
32428
|
+
#count
|
|
32429
|
+
data-count
|
|
32430
|
+
class="list-item-subtext block w-fit! decoration-dotted underline-offset-2 hover:underline"
|
|
32431
|
+
(mouseenter)="showDetails(quickFilter, count)"
|
|
32432
|
+
(mouseleave)="scheduleHideDetails()">
|
|
32433
|
+
{{ (quickFilter.filters.length === 1 ? "quickFilters.countOne" : "quickFilters.countOther") | transloco: { count: quickFilter.filters.length } }}
|
|
32434
|
+
</span>
|
|
32435
|
+
</ListItemText>
|
|
32436
|
+
</ListItemLeft>
|
|
32437
|
+
@if (deletable()) {
|
|
32438
|
+
<ListItemAction
|
|
32439
|
+
[ariaLabel]="'quickFilters.delete' | transloco: { name: quickFilter.name }"
|
|
32440
|
+
[keys]="deleteKeys"
|
|
32441
|
+
[shortcut]="'quickFilters.deleteShortcut' | transloco"
|
|
32442
|
+
[description]="'quickFilters.deleteDescription' | transloco: { key: 'quickFilters.deleteShortcut' | transloco }"
|
|
32443
|
+
(click)="remove(quickFilter, index, $event)">
|
|
32444
|
+
<x-mark-icon />
|
|
32445
|
+
</ListItemAction>
|
|
32446
|
+
}
|
|
32447
|
+
</ListItem>
|
|
32448
|
+
}
|
|
32449
|
+
</List>
|
|
32450
|
+
|
|
32451
|
+
<!-- The hover card of the design: the filters a quick filter holds, as chips, hanging under its row. Placed with Floating UI in
|
|
32452
|
+
the top layer, so it is above whatever clips the list. It stays while the pointer is on it, and while the focus is on the row. -->
|
|
32453
|
+
@if (details(); as shown) {
|
|
32454
|
+
<div
|
|
32455
|
+
#detailsCard
|
|
32456
|
+
role="tooltip"
|
|
32457
|
+
class="fixed m-0 flex max-w-72 flex-wrap gap-(--spacing-3xs) rounded-(--radius-lg) border border-solid border-(--stroke-neutral-white) bg-(--bg-neutral-white) p-(--spacing-md) shadow-(--shadow-2)"
|
|
32458
|
+
(mouseenter)="cancelHideDetails()"
|
|
32459
|
+
(mouseleave)="scheduleHideDetails()">
|
|
32460
|
+
@for (chip of chips(shown.quickFilter); track $index) {
|
|
32461
|
+
<span
|
|
32462
|
+
data-chip
|
|
32463
|
+
class="inline-flex max-w-full min-w-0 items-center gap-(--spacing-3xs) rounded-(--radius-rounded) bg-(--bg-neutral-base) py-(--spacing-4xs) pr-(--spacing-2xs) pl-(--spacing-2xs) text-(length:--size-sm) font-medium text-(--font-neutral-base)">
|
|
32464
|
+
<!-- the icon the application gives the aggregation in the filters custom JSON, as on the filter button -->
|
|
32465
|
+
@if (chip.icon) {
|
|
32466
|
+
<fa-icon [faClass]="chip.icon" class="shrink-0" />
|
|
32467
|
+
}
|
|
32468
|
+
<!-- A chip that is cut off shows its whole text in a tooltip; one that fits has none. -->
|
|
32469
|
+
<span class="truncate" truncated #cutChip="truncated" [tooltip]="chip.text" [tooltipDisabled]="!cutChip.isTruncated()">{{ chip.text }}</span>
|
|
32470
|
+
</span>
|
|
32471
|
+
}
|
|
32472
|
+
</div>
|
|
32473
|
+
}
|
|
32474
|
+
} @else {
|
|
32475
|
+
<!-- The empty-state rule of the list features: the feature's icon on a coloured tile, a title, one hint. -->
|
|
32476
|
+
<div class="flex flex-col items-center gap-(--spacing-xs) px-(--spacing-md) py-(--spacing-lg) text-center">
|
|
32477
|
+
<span class="inline-flex size-12 items-center justify-center rounded-(--radius-md) bg-(--bg-cyan-base-alt) text-(--font-cyan-muted)" aria-hidden="true">
|
|
32478
|
+
<quick-filter-icon class="size-6 opacity-80" />
|
|
32479
|
+
</span>
|
|
32480
|
+
<span class="text-(length:--size-base) font-semibold text-(--font-neutral-base)">{{ (query() ? "quickFilters.noMatch" : "quickFilters.empty") | transloco }}</span>
|
|
32481
|
+
@if (!query()) {
|
|
32482
|
+
<span class="text-(length:--size-sm) text-(--font-neutral-muted)">{{ emptyHint() ?? ("quickFilters.emptyHint" | transloco) }}</span>
|
|
32483
|
+
}
|
|
32484
|
+
</div>
|
|
32485
|
+
}
|
|
32486
|
+
|
|
32487
|
+
<span role="status" class="sr-only">{{ announcement() }}</span>
|
|
32488
|
+
`
|
|
32489
|
+
}]
|
|
32490
|
+
}],
|
|
32491
|
+
ctorParameters: () => [],
|
|
32492
|
+
propDecorators: {
|
|
32493
|
+
searchable: [{
|
|
32494
|
+
type: i0.Input,
|
|
32495
|
+
args: [{
|
|
32496
|
+
isSignal: true,
|
|
32497
|
+
alias: "searchable",
|
|
32498
|
+
required: false
|
|
32499
|
+
}]
|
|
32500
|
+
}],
|
|
32501
|
+
deletable: [{
|
|
32502
|
+
type: i0.Input,
|
|
32503
|
+
args: [{
|
|
32504
|
+
isSignal: true,
|
|
32505
|
+
alias: "deletable",
|
|
32506
|
+
required: false
|
|
32507
|
+
}]
|
|
32508
|
+
}],
|
|
32509
|
+
emptyHint: [{
|
|
32510
|
+
type: i0.Input,
|
|
32511
|
+
args: [{
|
|
32512
|
+
isSignal: true,
|
|
32513
|
+
alias: "emptyHint",
|
|
32514
|
+
required: false
|
|
32515
|
+
}]
|
|
32516
|
+
}],
|
|
32517
|
+
applied: [{
|
|
32518
|
+
type: i0.Output,
|
|
32519
|
+
args: ["applied"]
|
|
32520
|
+
}],
|
|
32521
|
+
deleted: [{
|
|
32522
|
+
type: i0.Output,
|
|
32523
|
+
args: ["deleted"]
|
|
32524
|
+
}],
|
|
32525
|
+
detailsCard: [{
|
|
32526
|
+
type: i0.ViewChild,
|
|
32527
|
+
args: ["detailsCard", { isSignal: true }]
|
|
32528
|
+
}]
|
|
32529
|
+
}
|
|
32530
|
+
});
|
|
32531
|
+
let nextPanelId = 0;
|
|
32532
|
+
var FiltersPanelComponent = class FiltersPanelComponent {
|
|
32533
|
+
documentCount = input(0, ...ngDevMode ? [{ debugName: "documentCount" }] : /* istanbul ignore next */ []);
|
|
32534
|
+
closed = output();
|
|
32535
|
+
appStore = inject(AppStore);
|
|
32536
|
+
aggregationsStore = inject(AggregationsStore);
|
|
32537
|
+
queryParamsStore = inject(QueryParamsStore);
|
|
32538
|
+
userSettingsStore = inject(UserSettingsStore);
|
|
32539
|
+
transloco = inject(TranslocoService);
|
|
32540
|
+
injector = inject(Injector);
|
|
32541
|
+
nameId = `filters-panel-quick-filter-name-${nextPanelId++}`;
|
|
32542
|
+
tab = signal("all", ...ngDevMode ? [{ debugName: "tab" }] : /* istanbul ignore next */ []);
|
|
32543
|
+
query = signal("", ...ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []);
|
|
32544
|
+
saving = signal(false, ...ngDevMode ? [{ debugName: "saving" }] : /* istanbul ignore next */ []);
|
|
32545
|
+
announcement = signal("", ...ngDevMode ? [{ debugName: "announcement" }] : /* istanbul ignore next */ []);
|
|
32546
|
+
name = signal("", ...ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []);
|
|
32547
|
+
appliedCount = computed(() => this.queryParamsStore.filters().flatMap((filter) => this.leaves(filter)).reduce((count, leaf) => count + this.valuesOf(leaf), 0), ...ngDevMode ? [{ debugName: "appliedCount" }] : /* istanbul ignore next */ []);
|
|
32548
|
+
nameInput = viewChild("nameInput", ...ngDevMode ? [{ debugName: "nameInput" }] : /* istanbul ignore next */ []);
|
|
32549
|
+
saveButton = viewChild("saveButton", ...ngDevMode ? [{ debugName: "saveButton" }] : /* istanbul ignore next */ []);
|
|
32550
|
+
matching = computed(() => {
|
|
32551
|
+
const needle = this.query().trim().toLocaleLowerCase();
|
|
32552
|
+
if (!needle) return [];
|
|
32553
|
+
const aside = this.appStore.filters().filter((f) => f.position === "left" || f.position === "both");
|
|
32554
|
+
return this.appStore.getAuthorized(aside).map((f) => this.aggregationsStore.getAggregation(f.name, "name") ?? this.aggregationsStore.getAggregation(f.column, "column")).filter((agg) => !!agg && `${agg.name} ${agg.column}`.toLocaleLowerCase().includes(needle)).map((agg) => agg.name);
|
|
32555
|
+
}, ...ngDevMode ? [{ debugName: "matching" }] : /* istanbul ignore next */ []);
|
|
32556
|
+
selectTab(values) {
|
|
32557
|
+
const next = values.at(-1);
|
|
32558
|
+
if (next) this.tab.set(next);
|
|
32559
|
+
}
|
|
32560
|
+
leaves(filter) {
|
|
32561
|
+
return filter.filters?.length ? filter.filters.flatMap((f) => this.leaves(f)) : [filter];
|
|
32562
|
+
}
|
|
32563
|
+
valuesOf(leaf) {
|
|
32564
|
+
if (leaf.values?.length) return leaf.values.length;
|
|
32565
|
+
return leaf.value !== void 0 && leaf.value !== null ? 1 : 0;
|
|
32566
|
+
}
|
|
32567
|
+
startSaving() {
|
|
32568
|
+
this.name.set("");
|
|
32569
|
+
this.saving.set(true);
|
|
32570
|
+
afterNextRender(() => this.nameInput()?.nativeElement.focus(), { injector: this.injector });
|
|
32571
|
+
}
|
|
32572
|
+
closeSaving() {
|
|
32573
|
+
this.saving.set(false);
|
|
32574
|
+
afterNextRender(() => this.saveButton()?.nativeElement.focus(), { injector: this.injector });
|
|
32575
|
+
}
|
|
32576
|
+
onSaveEscape(event) {
|
|
32577
|
+
event.stopPropagation();
|
|
32578
|
+
this.closeSaving();
|
|
32579
|
+
}
|
|
32580
|
+
async saveQuickFilter(event) {
|
|
32581
|
+
event.preventDefault();
|
|
32582
|
+
const saved = await this.userSettingsStore.addQuickFilter(this.name(), structuredClone(this.queryParamsStore.filters()));
|
|
32583
|
+
if (!saved) return;
|
|
32584
|
+
this.closeSaving();
|
|
32585
|
+
this.announcement.set(this.transloco.translate("filters.panelQuickFilterSaved", { name: saved.name }));
|
|
32586
|
+
}
|
|
32587
|
+
onQuickFilterApplied(quickFilter) {
|
|
32588
|
+
this.announcement.set(this.transloco.translate("filters.panelQuickFilterApplied", { name: quickFilter.name }));
|
|
32589
|
+
}
|
|
32590
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
32591
|
+
minVersion: "12.0.0",
|
|
32592
|
+
version: "22.2.1",
|
|
32593
|
+
ngImport: i0,
|
|
32594
|
+
type: FiltersPanelComponent,
|
|
32595
|
+
deps: [],
|
|
32596
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
32597
|
+
});
|
|
32598
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
32599
|
+
minVersion: "17.0.0",
|
|
32600
|
+
version: "22.2.1",
|
|
32601
|
+
type: FiltersPanelComponent,
|
|
32602
|
+
isStandalone: true,
|
|
32603
|
+
selector: "filters-panel, FiltersPanel, filterspanel",
|
|
32604
|
+
inputs: { documentCount: {
|
|
32605
|
+
classPropertyName: "documentCount",
|
|
32606
|
+
publicName: "documentCount",
|
|
32607
|
+
isSignal: true,
|
|
32608
|
+
isRequired: false,
|
|
32609
|
+
transformFunction: null
|
|
32610
|
+
} },
|
|
32611
|
+
outputs: { closed: "closed" },
|
|
32612
|
+
host: { classAttribute: "block h-full min-h-0" },
|
|
32613
|
+
providers: [provideTranslocoScope("filters")],
|
|
32614
|
+
viewQueries: [{
|
|
32615
|
+
propertyName: "nameInput",
|
|
32616
|
+
first: true,
|
|
32617
|
+
predicate: ["nameInput"],
|
|
32618
|
+
descendants: true,
|
|
32619
|
+
isSignal: true
|
|
32620
|
+
}, {
|
|
32621
|
+
propertyName: "saveButton",
|
|
32622
|
+
first: true,
|
|
32623
|
+
predicate: ["saveButton"],
|
|
32624
|
+
descendants: true,
|
|
32625
|
+
isSignal: true
|
|
32626
|
+
}],
|
|
32627
|
+
ngImport: i0,
|
|
32628
|
+
template: `
|
|
32629
|
+
<div class="flex h-full min-h-0 flex-col gap-(--spacing-xs)">
|
|
32630
|
+
<header class="flex items-center justify-between gap-(--spacing-xs)">
|
|
32631
|
+
<h2 class="flex items-center gap-(--spacing-2xs) text-(length:--size-lg) font-semibold">
|
|
32632
|
+
<filter-icon class="text-(length:--icon-sm) [&_svg]:fill-current" aria-hidden="true" />
|
|
32633
|
+
{{ "filters.panelTitle" | transloco }}
|
|
32634
|
+
</h2>
|
|
32635
|
+
<button variant="tertiary" size="sm" [iconOnly]="true" (click)="closed.emit()" [tooltip]="'filters.panelClose' | transloco" tooltipPosition="bottom">
|
|
32636
|
+
<x-mark-icon />
|
|
32637
|
+
<span class="sr-only">{{ "filters.panelClose" | transloco }}</span>
|
|
32638
|
+
</button>
|
|
32639
|
+
</header>
|
|
32640
|
+
|
|
32641
|
+
<!-- A two-way switch that must always have one side on: the group lets a click on the active item
|
|
32642
|
+
clear the selection, which would leave the panel with no content, so that case is ignored. -->
|
|
32643
|
+
<ToggleGroup class="w-full" [attr.aria-label]="'filters.panelViews' | transloco" [value]="[tab()]" (valueChange)="selectTab($event)">
|
|
32644
|
+
<Toggle class="flex-1 text-(length:--size-xs) font-semibold" value="all">{{ "filters.panelTabAll" | transloco }}</Toggle>
|
|
32645
|
+
<Toggle class="flex-1 text-(length:--size-xs) font-semibold" value="quick">{{ "filters.panelTabQuick" | transloco }}</Toggle>
|
|
32646
|
+
</ToggleGroup>
|
|
32647
|
+
|
|
32648
|
+
@if (tab() === "all") {
|
|
32649
|
+
<SearchInput
|
|
32650
|
+
size="md"
|
|
32651
|
+
class="[&>*]:h-8 [&>*]:rounded-full"
|
|
32652
|
+
[(value)]="query"
|
|
32653
|
+
[placeholder]="'filters.panelSearchPlaceholder' | transloco"
|
|
32654
|
+
[aria-label]="'filters.searchFilters' | transloco"
|
|
32655
|
+
[clearLabel]="'filters.clearSearch' | transloco" />
|
|
32656
|
+
|
|
32657
|
+
<!-- The bar is the anchor of the card: the card hangs under the button, as wide as the panel's content, and covers the facets
|
|
32658
|
+
below it until it is closed. -->
|
|
32659
|
+
<div class="relative flex h-8 items-center justify-between gap-(--spacing-xs) text-(length:--size-sm) text-(--font-primary-muted)">
|
|
32660
|
+
<span>{{ (documentCount() === 1 ? "filters.panelDocumentsOne" : "filters.panelDocumentsOther") | transloco: { count: documentCount() | localizedNumber } }}</span>
|
|
32661
|
+
<button
|
|
32662
|
+
#saveButton
|
|
32663
|
+
variant="secondary"
|
|
32664
|
+
size="sm"
|
|
32665
|
+
aria-haspopup="dialog"
|
|
32666
|
+
[attr.aria-expanded]="saving()"
|
|
32667
|
+
[disabled]="appliedCount() === 0"
|
|
32668
|
+
(click)="saving() ? closeSaving() : startSaving()">
|
|
32669
|
+
<quick-filter-icon />
|
|
32670
|
+
{{ "filters.panelSaveQuickFilter" | transloco }}
|
|
32671
|
+
</button>
|
|
32672
|
+
|
|
32673
|
+
@if (saving()) {
|
|
32674
|
+
<form
|
|
32675
|
+
role="dialog"
|
|
32676
|
+
[attr.aria-label]="'filters.panelSaveQuickFilter' | transloco"
|
|
32677
|
+
class="absolute inset-x-0 top-full z-10 mt-(--spacing-2xs) flex flex-col gap-(--spacing-sm) rounded-(--radius-lg) border border-(--stroke-neutral-white) bg-(--bg-neutral-white) p-(--spacing-md) text-(--font-neutral-black) shadow-(--shadow-2)"
|
|
32678
|
+
(submit)="saveQuickFilter($event)"
|
|
32679
|
+
(keydown.escape)="onSaveEscape($event)">
|
|
32680
|
+
<div class="flex flex-col gap-(--spacing-3xs)">
|
|
32681
|
+
<div class="flex items-center justify-between">
|
|
32682
|
+
<label [for]="nameId" class="text-(length:--size-sm) font-medium">{{ "filters.panelNameLabel" | transloco }}</label>
|
|
32683
|
+
<span class="rounded-(--radius-xs) bg-(--bg-neutral-base) px-1.5 py-px text-(length:--size-xs) text-(--font-neutral-muted)">{{ "filters.panelRequired" | transloco }}</span>
|
|
32684
|
+
</div>
|
|
32685
|
+
<input-group size="sm" class="w-full">
|
|
32686
|
+
<input
|
|
32687
|
+
#nameInput
|
|
32688
|
+
input-control
|
|
32689
|
+
[id]="nameId"
|
|
32690
|
+
type="text"
|
|
32691
|
+
autocomplete="off"
|
|
32692
|
+
required
|
|
32693
|
+
[placeholder]="'filters.panelQuickFilterName' | transloco"
|
|
32694
|
+
[value]="name()"
|
|
32695
|
+
(input)="name.set(nameInput.value)" />
|
|
32696
|
+
</input-group>
|
|
32697
|
+
</div>
|
|
32698
|
+
<div class="flex justify-end gap-(--spacing-2xs)">
|
|
32699
|
+
<button type="button" variant="secondary" size="sm" (click)="closeSaving()">{{ "filters.panelCancel" | transloco }}</button>
|
|
32700
|
+
<button type="submit" variant="primary" size="sm" [disabled]="!name().trim()">{{ "filters.panelSave" | transloco }}</button>
|
|
32701
|
+
</div>
|
|
32702
|
+
</form>
|
|
32703
|
+
}
|
|
32704
|
+
</div>
|
|
32705
|
+
|
|
32706
|
+
<!-- single outer scroll lives here; aside-filters' own overflow is neutralized so it is not a second, default-styled scroll container.
|
|
32707
|
+
relative, and it is load-bearing: a scroll container only clips the descendants it is a containing block for. The facets hold sr-only
|
|
32708
|
+
elements, which are position: absolute; with this div left static they escaped the clip, sat at their flow position far below the
|
|
32709
|
+
visible part of a long facet, and made the whole page scroll — a second scrollbar next to the facet's own. -->
|
|
32710
|
+
<div class="relative -mx-1 flex min-h-0 flex-1 scrollbar-thin flex-col overflow-y-auto px-1">
|
|
32711
|
+
@if (query() && matching().length === 0) {
|
|
32712
|
+
<p class="py-(--spacing-md) text-center text-(length:--size-sm) text-(--font-neutral-muted)">{{ "filters.noFilterMatch" | transloco }}</p>
|
|
32713
|
+
} @else {
|
|
32714
|
+
<aside-filters class="overflow-visible" [aggregations]="query() ? matching() : []" />
|
|
32715
|
+
}
|
|
32716
|
+
</div>
|
|
32717
|
+
} @else {
|
|
32718
|
+
<!-- The list of the quick filters pill, inline: it owns the rows, the hover card, the cross and the empty state. -->
|
|
32719
|
+
<quick-filters-list
|
|
32720
|
+
class="flex-1"
|
|
32721
|
+
[emptyHint]="'filters.panelNoQuickFiltersHint' | transloco"
|
|
32722
|
+
(applied)="onQuickFilterApplied($event)" />
|
|
32723
|
+
}
|
|
32724
|
+
|
|
32725
|
+
<span role="status" class="sr-only">{{ announcement() }}</span>
|
|
32726
|
+
</div>
|
|
32727
|
+
`,
|
|
32728
|
+
isInline: true,
|
|
32729
|
+
dependencies: [
|
|
32730
|
+
{
|
|
32731
|
+
kind: "component",
|
|
32732
|
+
type: AsideFiltersComponent,
|
|
32733
|
+
selector: "aside-filters, AsideFilters, asidefilters",
|
|
32734
|
+
inputs: [
|
|
32735
|
+
"class",
|
|
32736
|
+
"aggregations",
|
|
32737
|
+
"exclusive",
|
|
32738
|
+
"collapsible",
|
|
32739
|
+
"collapsed",
|
|
32740
|
+
"hideWhenEmpty"
|
|
32741
|
+
]
|
|
32742
|
+
},
|
|
32743
|
+
{
|
|
32744
|
+
kind: "directive",
|
|
32745
|
+
type: ButtonComponent,
|
|
32746
|
+
selector: "button",
|
|
32747
|
+
inputs: [
|
|
32748
|
+
"class",
|
|
32749
|
+
"variant",
|
|
32750
|
+
"scheme",
|
|
32751
|
+
"iconOnly",
|
|
32752
|
+
"size"
|
|
32753
|
+
]
|
|
32754
|
+
},
|
|
32755
|
+
{
|
|
32756
|
+
kind: "component",
|
|
32757
|
+
type: FilterIcon,
|
|
32758
|
+
selector: "filter, filter-icon, Filter, FilterIcon",
|
|
32759
|
+
inputs: ["class"]
|
|
32760
|
+
},
|
|
32761
|
+
{
|
|
32762
|
+
kind: "directive",
|
|
32763
|
+
type: InputComponent,
|
|
32764
|
+
selector: "input-group, inputgroup, InputGroup",
|
|
32765
|
+
inputs: [
|
|
32766
|
+
"class",
|
|
32767
|
+
"size",
|
|
32768
|
+
"scheme",
|
|
32769
|
+
"filled",
|
|
32770
|
+
"numbers",
|
|
32771
|
+
"autoHeight",
|
|
32772
|
+
"textSize"
|
|
32773
|
+
]
|
|
32774
|
+
},
|
|
32775
|
+
{
|
|
32776
|
+
kind: "directive",
|
|
32777
|
+
type: InputControlDirective,
|
|
32778
|
+
selector: "input[input-control], input[inputControl]",
|
|
32779
|
+
inputs: ["class"]
|
|
32780
|
+
},
|
|
32781
|
+
{
|
|
32782
|
+
kind: "component",
|
|
32783
|
+
type: QuickFilterIcon,
|
|
32784
|
+
selector: "quick-filter, quick-filter-icon, QuickFilter, QuickFilterIcon",
|
|
32785
|
+
inputs: ["class"]
|
|
32786
|
+
},
|
|
32787
|
+
{
|
|
32788
|
+
kind: "component",
|
|
32789
|
+
type: QuickFiltersListComponent,
|
|
32790
|
+
selector: "QuickFiltersList, quick-filters-list",
|
|
32791
|
+
inputs: [
|
|
32792
|
+
"searchable",
|
|
32793
|
+
"deletable",
|
|
32794
|
+
"emptyHint"
|
|
32795
|
+
],
|
|
32796
|
+
outputs: ["applied", "deleted"]
|
|
32797
|
+
},
|
|
32798
|
+
{
|
|
32799
|
+
kind: "component",
|
|
32800
|
+
type: SearchInputComponent,
|
|
32801
|
+
selector: "SearchInput:not([suggestions]), search-input:not([suggestions]), searchinput:not([suggestions])"
|
|
32802
|
+
},
|
|
32803
|
+
{
|
|
32804
|
+
kind: "directive",
|
|
32805
|
+
type: ToggleGroupComponent,
|
|
32806
|
+
selector: "ToggleGroup, toggle-group, togglegroup",
|
|
32807
|
+
inputs: [
|
|
32808
|
+
"class",
|
|
32809
|
+
"value",
|
|
32810
|
+
"multi"
|
|
32811
|
+
],
|
|
32812
|
+
outputs: ["valueChange"],
|
|
32813
|
+
exportAs: ["toggleGroup"]
|
|
32814
|
+
},
|
|
32815
|
+
{
|
|
32816
|
+
kind: "directive",
|
|
32817
|
+
type: ToggleItemComponent,
|
|
32818
|
+
selector: "Toggle[value]",
|
|
32819
|
+
inputs: [
|
|
32820
|
+
"value",
|
|
32821
|
+
"class",
|
|
32822
|
+
"variant",
|
|
32823
|
+
"size",
|
|
32824
|
+
"iconOnly",
|
|
32825
|
+
"showIcon"
|
|
32826
|
+
],
|
|
32827
|
+
exportAs: ["toggle"]
|
|
32828
|
+
},
|
|
32829
|
+
{
|
|
32830
|
+
kind: "directive",
|
|
32831
|
+
type: TooltipDirective,
|
|
32832
|
+
selector: "[tooltip]",
|
|
32833
|
+
inputs: [
|
|
32834
|
+
"tooltip",
|
|
32835
|
+
"tooltipPosition",
|
|
32836
|
+
"tooltipEvent",
|
|
32837
|
+
"showDelay",
|
|
32838
|
+
"hideDelay",
|
|
32839
|
+
"life",
|
|
32840
|
+
"tooltipOffset",
|
|
32841
|
+
"autoHide",
|
|
32842
|
+
"hideOnEscape",
|
|
32843
|
+
"escape",
|
|
32844
|
+
"tooltipDisabled",
|
|
32845
|
+
"tooltipArrow",
|
|
32846
|
+
"tooltipStyleClass",
|
|
32847
|
+
"tooltipOptions",
|
|
32848
|
+
"tooltip-position",
|
|
32849
|
+
"tooltip-delay",
|
|
32850
|
+
"tooltip-duration",
|
|
32851
|
+
"tooltip-offset"
|
|
32852
|
+
],
|
|
32853
|
+
exportAs: ["tooltip"]
|
|
32854
|
+
},
|
|
32855
|
+
{
|
|
32856
|
+
kind: "component",
|
|
32857
|
+
type: XMarkIcon,
|
|
32858
|
+
selector: "x-mark, x-mark-icon, XMark, XMarkIcon",
|
|
32859
|
+
inputs: ["class"]
|
|
32860
|
+
},
|
|
32861
|
+
{
|
|
32862
|
+
kind: "pipe",
|
|
32863
|
+
type: TranslocoPipe,
|
|
32864
|
+
name: "transloco"
|
|
32865
|
+
},
|
|
32866
|
+
{
|
|
32867
|
+
kind: "pipe",
|
|
32868
|
+
type: LocalizedNumberPipe,
|
|
32869
|
+
name: "localizedNumber"
|
|
32870
|
+
}
|
|
32871
|
+
]
|
|
32872
|
+
});
|
|
32873
|
+
};
|
|
32874
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
32875
|
+
minVersion: "12.0.0",
|
|
32876
|
+
version: "22.2.1",
|
|
32877
|
+
ngImport: i0,
|
|
32878
|
+
type: FiltersPanelComponent,
|
|
32879
|
+
decorators: [{
|
|
32880
|
+
type: Component,
|
|
32881
|
+
args: [{
|
|
32882
|
+
selector: "filters-panel, FiltersPanel, filterspanel",
|
|
32883
|
+
imports: [
|
|
32884
|
+
TranslocoPipe,
|
|
32885
|
+
LocalizedNumberPipe,
|
|
32886
|
+
AsideFiltersComponent,
|
|
32887
|
+
ButtonComponent,
|
|
32888
|
+
FilterIcon,
|
|
32889
|
+
InputComponent,
|
|
32890
|
+
InputControlDirective,
|
|
32891
|
+
QuickFilterIcon,
|
|
32892
|
+
QuickFiltersListComponent,
|
|
32893
|
+
SearchInputComponent,
|
|
32894
|
+
ToggleGroupComponent,
|
|
32895
|
+
ToggleItemComponent,
|
|
32896
|
+
TooltipDirective,
|
|
32897
|
+
XMarkIcon
|
|
32898
|
+
],
|
|
32899
|
+
providers: [provideTranslocoScope("filters")],
|
|
32900
|
+
host: { class: "block h-full min-h-0" },
|
|
32901
|
+
template: `
|
|
32902
|
+
<div class="flex h-full min-h-0 flex-col gap-(--spacing-xs)">
|
|
32903
|
+
<header class="flex items-center justify-between gap-(--spacing-xs)">
|
|
32904
|
+
<h2 class="flex items-center gap-(--spacing-2xs) text-(length:--size-lg) font-semibold">
|
|
32905
|
+
<filter-icon class="text-(length:--icon-sm) [&_svg]:fill-current" aria-hidden="true" />
|
|
32906
|
+
{{ "filters.panelTitle" | transloco }}
|
|
32907
|
+
</h2>
|
|
32908
|
+
<button variant="tertiary" size="sm" [iconOnly]="true" (click)="closed.emit()" [tooltip]="'filters.panelClose' | transloco" tooltipPosition="bottom">
|
|
32909
|
+
<x-mark-icon />
|
|
32910
|
+
<span class="sr-only">{{ "filters.panelClose" | transloco }}</span>
|
|
32911
|
+
</button>
|
|
32912
|
+
</header>
|
|
32913
|
+
|
|
32914
|
+
<!-- A two-way switch that must always have one side on: the group lets a click on the active item
|
|
32915
|
+
clear the selection, which would leave the panel with no content, so that case is ignored. -->
|
|
32916
|
+
<ToggleGroup class="w-full" [attr.aria-label]="'filters.panelViews' | transloco" [value]="[tab()]" (valueChange)="selectTab($event)">
|
|
32917
|
+
<Toggle class="flex-1 text-(length:--size-xs) font-semibold" value="all">{{ "filters.panelTabAll" | transloco }}</Toggle>
|
|
32918
|
+
<Toggle class="flex-1 text-(length:--size-xs) font-semibold" value="quick">{{ "filters.panelTabQuick" | transloco }}</Toggle>
|
|
32919
|
+
</ToggleGroup>
|
|
32920
|
+
|
|
32921
|
+
@if (tab() === "all") {
|
|
32922
|
+
<SearchInput
|
|
32923
|
+
size="md"
|
|
32924
|
+
class="[&>*]:h-8 [&>*]:rounded-full"
|
|
32925
|
+
[(value)]="query"
|
|
32926
|
+
[placeholder]="'filters.panelSearchPlaceholder' | transloco"
|
|
32927
|
+
[aria-label]="'filters.searchFilters' | transloco"
|
|
32928
|
+
[clearLabel]="'filters.clearSearch' | transloco" />
|
|
32929
|
+
|
|
32930
|
+
<!-- The bar is the anchor of the card: the card hangs under the button, as wide as the panel's content, and covers the facets
|
|
32931
|
+
below it until it is closed. -->
|
|
32932
|
+
<div class="relative flex h-8 items-center justify-between gap-(--spacing-xs) text-(length:--size-sm) text-(--font-primary-muted)">
|
|
32933
|
+
<span>{{ (documentCount() === 1 ? "filters.panelDocumentsOne" : "filters.panelDocumentsOther") | transloco: { count: documentCount() | localizedNumber } }}</span>
|
|
32934
|
+
<button
|
|
32935
|
+
#saveButton
|
|
32936
|
+
variant="secondary"
|
|
32937
|
+
size="sm"
|
|
32938
|
+
aria-haspopup="dialog"
|
|
32939
|
+
[attr.aria-expanded]="saving()"
|
|
32940
|
+
[disabled]="appliedCount() === 0"
|
|
32941
|
+
(click)="saving() ? closeSaving() : startSaving()">
|
|
32942
|
+
<quick-filter-icon />
|
|
32943
|
+
{{ "filters.panelSaveQuickFilter" | transloco }}
|
|
32944
|
+
</button>
|
|
32945
|
+
|
|
32946
|
+
@if (saving()) {
|
|
32947
|
+
<form
|
|
32948
|
+
role="dialog"
|
|
32949
|
+
[attr.aria-label]="'filters.panelSaveQuickFilter' | transloco"
|
|
32950
|
+
class="absolute inset-x-0 top-full z-10 mt-(--spacing-2xs) flex flex-col gap-(--spacing-sm) rounded-(--radius-lg) border border-(--stroke-neutral-white) bg-(--bg-neutral-white) p-(--spacing-md) text-(--font-neutral-black) shadow-(--shadow-2)"
|
|
32951
|
+
(submit)="saveQuickFilter($event)"
|
|
32952
|
+
(keydown.escape)="onSaveEscape($event)">
|
|
32953
|
+
<div class="flex flex-col gap-(--spacing-3xs)">
|
|
32954
|
+
<div class="flex items-center justify-between">
|
|
32955
|
+
<label [for]="nameId" class="text-(length:--size-sm) font-medium">{{ "filters.panelNameLabel" | transloco }}</label>
|
|
32956
|
+
<span class="rounded-(--radius-xs) bg-(--bg-neutral-base) px-1.5 py-px text-(length:--size-xs) text-(--font-neutral-muted)">{{ "filters.panelRequired" | transloco }}</span>
|
|
32957
|
+
</div>
|
|
32958
|
+
<input-group size="sm" class="w-full">
|
|
32959
|
+
<input
|
|
32960
|
+
#nameInput
|
|
32961
|
+
input-control
|
|
32962
|
+
[id]="nameId"
|
|
32963
|
+
type="text"
|
|
32964
|
+
autocomplete="off"
|
|
32965
|
+
required
|
|
32966
|
+
[placeholder]="'filters.panelQuickFilterName' | transloco"
|
|
32967
|
+
[value]="name()"
|
|
32968
|
+
(input)="name.set(nameInput.value)" />
|
|
32969
|
+
</input-group>
|
|
32970
|
+
</div>
|
|
32971
|
+
<div class="flex justify-end gap-(--spacing-2xs)">
|
|
32972
|
+
<button type="button" variant="secondary" size="sm" (click)="closeSaving()">{{ "filters.panelCancel" | transloco }}</button>
|
|
32973
|
+
<button type="submit" variant="primary" size="sm" [disabled]="!name().trim()">{{ "filters.panelSave" | transloco }}</button>
|
|
32974
|
+
</div>
|
|
32975
|
+
</form>
|
|
32976
|
+
}
|
|
32977
|
+
</div>
|
|
32978
|
+
|
|
32979
|
+
<!-- single outer scroll lives here; aside-filters' own overflow is neutralized so it is not a second, default-styled scroll container.
|
|
32980
|
+
relative, and it is load-bearing: a scroll container only clips the descendants it is a containing block for. The facets hold sr-only
|
|
32981
|
+
elements, which are position: absolute; with this div left static they escaped the clip, sat at their flow position far below the
|
|
32982
|
+
visible part of a long facet, and made the whole page scroll — a second scrollbar next to the facet's own. -->
|
|
32983
|
+
<div class="relative -mx-1 flex min-h-0 flex-1 scrollbar-thin flex-col overflow-y-auto px-1">
|
|
32984
|
+
@if (query() && matching().length === 0) {
|
|
32985
|
+
<p class="py-(--spacing-md) text-center text-(length:--size-sm) text-(--font-neutral-muted)">{{ "filters.noFilterMatch" | transloco }}</p>
|
|
32986
|
+
} @else {
|
|
32987
|
+
<aside-filters class="overflow-visible" [aggregations]="query() ? matching() : []" />
|
|
32988
|
+
}
|
|
32989
|
+
</div>
|
|
32990
|
+
} @else {
|
|
32991
|
+
<!-- The list of the quick filters pill, inline: it owns the rows, the hover card, the cross and the empty state. -->
|
|
32992
|
+
<quick-filters-list
|
|
32993
|
+
class="flex-1"
|
|
32994
|
+
[emptyHint]="'filters.panelNoQuickFiltersHint' | transloco"
|
|
32995
|
+
(applied)="onQuickFilterApplied($event)" />
|
|
32996
|
+
}
|
|
32997
|
+
|
|
32998
|
+
<span role="status" class="sr-only">{{ announcement() }}</span>
|
|
32999
|
+
</div>
|
|
33000
|
+
`
|
|
33001
|
+
}]
|
|
33002
|
+
}],
|
|
33003
|
+
propDecorators: {
|
|
33004
|
+
documentCount: [{
|
|
33005
|
+
type: i0.Input,
|
|
33006
|
+
args: [{
|
|
33007
|
+
isSignal: true,
|
|
33008
|
+
alias: "documentCount",
|
|
33009
|
+
required: false
|
|
33010
|
+
}]
|
|
33011
|
+
}],
|
|
33012
|
+
closed: [{
|
|
33013
|
+
type: i0.Output,
|
|
33014
|
+
args: ["closed"]
|
|
33015
|
+
}],
|
|
33016
|
+
nameInput: [{
|
|
33017
|
+
type: i0.ViewChild,
|
|
33018
|
+
args: ["nameInput", { isSignal: true }]
|
|
33019
|
+
}],
|
|
33020
|
+
saveButton: [{
|
|
33021
|
+
type: i0.ViewChild,
|
|
33022
|
+
args: ["saveButton", { isSignal: true }]
|
|
33023
|
+
}]
|
|
33024
|
+
}
|
|
33025
|
+
});
|
|
31622
33026
|
var LabelService = class LabelService {
|
|
31623
33027
|
appStore = inject(AppStore);
|
|
31624
33028
|
client = inject(ATOMIC_CLIENT);
|
|
@@ -32901,43 +34305,21 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
32901
34305
|
}
|
|
32902
34306
|
});
|
|
32903
34307
|
let popoverUid = 0;
|
|
32904
|
-
function filterLabel(filter) {
|
|
32905
|
-
return filter.name || filter.field || "";
|
|
32906
|
-
}
|
|
32907
|
-
function filterValue(filter) {
|
|
32908
|
-
if (filter.filters?.length) return filter.filters.map((f) => filterLabel(f) ? `${filterLabel(f)}: ${filterValue(f)}` : filterValue(f)).join(", ");
|
|
32909
|
-
if (filter.values?.length) return filter.values.join(", ");
|
|
32910
|
-
return filter.display ?? filter.value ?? [filter.start, filter.end].filter((v) => v !== void 0 && v !== "").join(" – ");
|
|
32911
|
-
}
|
|
32912
34308
|
var QuickFiltersComponent = class QuickFiltersComponent {
|
|
32913
34309
|
scheme = input("sage", ...ngDevMode ? [{ debugName: "scheme" }] : /* istanbul ignore next */ []);
|
|
32914
34310
|
appearance = input("pill", ...ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []);
|
|
32915
34311
|
position = input("bottom-start", ...ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []);
|
|
32916
34312
|
offset = input(8, ...ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []);
|
|
32917
34313
|
applied = output();
|
|
32918
|
-
userSettingsStore = inject(UserSettingsStore);
|
|
32919
|
-
queryParamsStore = inject(QueryParamsStore);
|
|
32920
|
-
appStore = inject(AppStore);
|
|
32921
34314
|
transloco = inject(TranslocoService);
|
|
32922
34315
|
host = inject(ElementRef);
|
|
32923
34316
|
popoverId = `quick-filters-popover-${popoverUid++}`;
|
|
32924
34317
|
resized = signal(false, ...ngDevMode ? [{ debugName: "resized" }] : /* istanbul ignore next */ []);
|
|
32925
34318
|
flippedAbove = signal(false, ...ngDevMode ? [{ debugName: "flippedAbove" }] : /* istanbul ignore next */ []);
|
|
32926
34319
|
placement = computed(() => this.resized() && this.flippedAbove() ? this.position().replace("bottom", "top") : this.position(), ...ngDevMode ? [{ debugName: "placement" }] : /* istanbul ignore next */ []);
|
|
32927
|
-
details = signal(null, ...ngDevMode ? [{ debugName: "details" }] : /* istanbul ignore next */ []);
|
|
32928
|
-
detailsCard = viewChild("detailsCard", ...ngDevMode ? [{ debugName: "detailsCard" }] : /* istanbul ignore next */ []);
|
|
32929
|
-
hideTimer;
|
|
32930
34320
|
popover = viewChild(PopoverDirective, ...ngDevMode ? [{ debugName: "popover" }] : /* istanbul ignore next */ []);
|
|
32931
34321
|
isOpen = computed(() => this.popover()?.state() === "open", ...ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []);
|
|
32932
34322
|
constructor() {
|
|
32933
|
-
injectFloatingPanel({
|
|
32934
|
-
panel: () => this.detailsCard()?.nativeElement,
|
|
32935
|
-
anchor: () => this.details()?.anchor,
|
|
32936
|
-
placement: "bottom-start",
|
|
32937
|
-
offset: 4,
|
|
32938
|
-
topLayer: true
|
|
32939
|
-
});
|
|
32940
|
-
inject(DestroyRef).onDestroy(() => this.cancelHideDetails());
|
|
32941
34323
|
effect((onCleanup) => {
|
|
32942
34324
|
if (!this.resized()) return;
|
|
32943
34325
|
const onScroll = (event) => {
|
|
@@ -32957,27 +34339,9 @@ var QuickFiltersComponent = class QuickFiltersComponent {
|
|
|
32957
34339
|
selected: false,
|
|
32958
34340
|
open: this.isOpen()
|
|
32959
34341
|
}) : this.isOpen() ? "border border-(--stroke-neutral-dark)/50" : "", ...ngDevMode ? [{ debugName: "triggerClass" }] : /* istanbul ignore next */ []);
|
|
32960
|
-
quickFilters = computed(() => this.userSettingsStore.quickFilters() ?? [], ...ngDevMode ? [{ debugName: "quickFilters" }] : /* istanbul ignore next */ []);
|
|
32961
|
-
query = signal("", ...ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []);
|
|
32962
|
-
filtered = computed(() => {
|
|
32963
|
-
const query = this.query().trim().toLowerCase();
|
|
32964
|
-
return query ? this.quickFilters().filter((quickFilter) => quickFilter.name.toLowerCase().includes(query)) : this.quickFilters();
|
|
32965
|
-
}, ...ngDevMode ? [{ debugName: "filtered" }] : /* istanbul ignore next */ []);
|
|
32966
34342
|
announcement = signal("", ...ngDevMode ? [{ debugName: "announcement" }] : /* istanbul ignore next */ []);
|
|
32967
|
-
chips(quickFilter) {
|
|
32968
|
-
return quickFilter.filters.map((filter) => {
|
|
32969
|
-
const icon = (filter.field ? this.appStore.getAggregationCustomization(filter.field, filter.name) : void 0)?.icon;
|
|
32970
|
-
const value = filterValue(filter);
|
|
32971
|
-
const label = filterLabel(filter);
|
|
32972
|
-
return {
|
|
32973
|
-
text: icon || !label || !value ? value || label : `${label}: ${value}`,
|
|
32974
|
-
icon
|
|
32975
|
-
};
|
|
32976
|
-
}).filter((chip) => chip.text);
|
|
32977
|
-
}
|
|
32978
34343
|
onToggle() {
|
|
32979
34344
|
if (this.popover()?.state() === "open") return;
|
|
32980
|
-
this.reset();
|
|
32981
34345
|
this.unpin();
|
|
32982
34346
|
}
|
|
32983
34347
|
unpin() {
|
|
@@ -32996,44 +34360,12 @@ var QuickFiltersComponent = class QuickFiltersComponent {
|
|
|
32996
34360
|
if (popover && trigger && element) this.flippedAbove.set(element.getBoundingClientRect().bottom <= trigger.getBoundingClientRect().top + 1);
|
|
32997
34361
|
this.resized.set(true);
|
|
32998
34362
|
}
|
|
32999
|
-
showDetails(quickFilter, event) {
|
|
33000
|
-
this.cancelHideDetails();
|
|
33001
|
-
this.details.set({
|
|
33002
|
-
quickFilter,
|
|
33003
|
-
anchor: event.currentTarget
|
|
33004
|
-
});
|
|
33005
|
-
}
|
|
33006
|
-
scheduleHideDetails() {
|
|
33007
|
-
this.cancelHideDetails();
|
|
33008
|
-
this.hideTimer = setTimeout(() => this.details.set(null), 100);
|
|
33009
|
-
}
|
|
33010
|
-
cancelHideDetails() {
|
|
33011
|
-
clearTimeout(this.hideTimer);
|
|
33012
|
-
}
|
|
33013
|
-
hideDetails() {
|
|
33014
|
-
this.cancelHideDetails();
|
|
33015
|
-
this.details.set(null);
|
|
33016
|
-
}
|
|
33017
|
-
reset() {
|
|
33018
|
-
this.cancelHideDetails();
|
|
33019
|
-
this.details.set(null);
|
|
33020
|
-
this.query.set("");
|
|
33021
|
-
}
|
|
33022
34363
|
closeOnFocusLeave(event) {
|
|
33023
34364
|
const next = event.relatedTarget;
|
|
33024
34365
|
if (!next || !(next instanceof Node) || this.host.nativeElement.contains(next)) return;
|
|
33025
34366
|
this.popover()?.close();
|
|
33026
34367
|
}
|
|
33027
|
-
picked = signal([], ...ngDevMode ? [{ debugName: "picked" }] : /* istanbul ignore next */ []);
|
|
33028
|
-
pick(values) {
|
|
33029
|
-
const quickFilter = values.at(-1);
|
|
33030
|
-
this.picked.set([]);
|
|
33031
|
-
if (quickFilter) this.apply(quickFilter);
|
|
33032
|
-
}
|
|
33033
34368
|
apply(quickFilter) {
|
|
33034
|
-
this.cancelHideDetails();
|
|
33035
|
-
this.details.set(null);
|
|
33036
|
-
this.queryParamsStore.patch({ filters: structuredClone(quickFilter.filters) });
|
|
33037
34369
|
this.popover()?.close();
|
|
33038
34370
|
this.applied.emit(quickFilter);
|
|
33039
34371
|
this.announcement.set(this.transloco.translate("quickFilters.applied", { name: quickFilter.name }));
|
|
@@ -33084,20 +34416,11 @@ var QuickFiltersComponent = class QuickFiltersComponent {
|
|
|
33084
34416
|
},
|
|
33085
34417
|
outputs: { applied: "applied" },
|
|
33086
34418
|
host: {
|
|
33087
|
-
listeners: {
|
|
33088
|
-
"focusout": "closeOnFocusLeave($event)",
|
|
33089
|
-
"keydown.escape": "hideDetails()"
|
|
33090
|
-
},
|
|
34419
|
+
listeners: { "focusout": "closeOnFocusLeave($event)" },
|
|
33091
34420
|
classAttribute: "flex rounded-(--radius-2xl)"
|
|
33092
34421
|
},
|
|
33093
34422
|
providers: [provideTranslocoScope("quick-filters")],
|
|
33094
34423
|
viewQueries: [{
|
|
33095
|
-
propertyName: "detailsCard",
|
|
33096
|
-
first: true,
|
|
33097
|
-
predicate: ["detailsCard"],
|
|
33098
|
-
descendants: true,
|
|
33099
|
-
isSignal: true
|
|
33100
|
-
}, {
|
|
33101
34424
|
propertyName: "popover",
|
|
33102
34425
|
first: true,
|
|
33103
34426
|
predicate: PopoverDirective,
|
|
@@ -33137,87 +34460,7 @@ var QuickFiltersComponent = class QuickFiltersComponent {
|
|
|
33137
34460
|
there is room under it. The list takes the height the popover is given. The popover has no padding above and under:
|
|
33138
34461
|
the handles (12px each) are that space, and hug the edges. -->
|
|
33139
34462
|
<PopoverResizeHandle edge="top" [label]="'quickFilters.resize' | transloco" (resizeStart)="onResizeStart()" />
|
|
33140
|
-
<
|
|
33141
|
-
@if (quickFilters().length > 0) {
|
|
33142
|
-
<SearchInput
|
|
33143
|
-
size="md"
|
|
33144
|
-
class="border-(--stroke-primary-light)"
|
|
33145
|
-
[(value)]="query"
|
|
33146
|
-
[placeholder]="'quickFilters.search' | transloco"
|
|
33147
|
-
[aria-label]="'quickFilters.search' | transloco"
|
|
33148
|
-
[clearLabel]="'quickFilters.clearSearch' | transloco" />
|
|
33149
|
-
}
|
|
33150
|
-
|
|
33151
|
-
@if (filtered().length) {
|
|
33152
|
-
<!-- The galactik List, as the design's List Item: a listbox of menu rows with an icon, the name and the number of
|
|
33153
|
-
filters. Choosing a row (click, Enter) applies it; the selection is not kept, the popover closes. Hovering a
|
|
33154
|
-
row lists the filters it holds in a card. -->
|
|
33155
|
-
<List
|
|
33156
|
-
variant="list-menu"
|
|
33157
|
-
size="medium"
|
|
33158
|
-
selectionMode="explicit"
|
|
33159
|
-
class="-mx-1 min-h-0 flex-1 overflow-auto px-1"
|
|
33160
|
-
[attr.aria-label]="'quickFilters.list' | transloco"
|
|
33161
|
-
[value]="picked()"
|
|
33162
|
-
(valueChange)="pick($event)">
|
|
33163
|
-
@for (quickFilter of filtered(); track quickFilter.id) {
|
|
33164
|
-
<ListItem
|
|
33165
|
-
[value]="quickFilter"
|
|
33166
|
-
[label]="quickFilter.name"
|
|
33167
|
-
(mouseenter)="showDetails(quickFilter, $event)"
|
|
33168
|
-
(mouseleave)="scheduleHideDetails()"
|
|
33169
|
-
(focusin)="showDetails(quickFilter, $event)"
|
|
33170
|
-
(focusout)="scheduleHideDetails()">
|
|
33171
|
-
<ListItemLeft>
|
|
33172
|
-
<ListItemIcon><quick-filter-icon /></ListItemIcon>
|
|
33173
|
-
<ListItemText
|
|
33174
|
-
[subtext]="(quickFilter.filters.length === 1 ? 'quickFilters.countOne' : 'quickFilters.countOther') | transloco: { count: quickFilter.filters.length }">
|
|
33175
|
-
{{ quickFilter.name }}
|
|
33176
|
-
</ListItemText>
|
|
33177
|
-
</ListItemLeft>
|
|
33178
|
-
</ListItem>
|
|
33179
|
-
}
|
|
33180
|
-
</List>
|
|
33181
|
-
|
|
33182
|
-
<!-- The hover card of the design: the filters a quick filter holds, as chips, hanging under its row. Placed with
|
|
33183
|
-
Floating UI like the popovers, in the top layer so it is above the popover it comes from. It stays while the
|
|
33184
|
-
pointer is on it, and while the focus is on the row. -->
|
|
33185
|
-
@if (details(); as shown) {
|
|
33186
|
-
<div
|
|
33187
|
-
#detailsCard
|
|
33188
|
-
role="tooltip"
|
|
33189
|
-
class="fixed m-0 flex max-w-72 flex-wrap gap-(--spacing-3xs) rounded-(--radius-lg) border border-solid border-(--stroke-neutral-white) bg-(--bg-neutral-white) p-(--spacing-md) shadow-(--shadow-2)"
|
|
33190
|
-
(mouseenter)="cancelHideDetails()"
|
|
33191
|
-
(mouseleave)="scheduleHideDetails()">
|
|
33192
|
-
@for (chip of chips(shown.quickFilter); track $index) {
|
|
33193
|
-
<span
|
|
33194
|
-
data-chip
|
|
33195
|
-
class="inline-flex items-center gap-(--spacing-3xs) rounded-(--radius-rounded) bg-(--bg-neutral-base) py-(--spacing-4xs) pr-(--spacing-2xs) pl-(--spacing-2xs) text-(length:--size-sm) font-medium text-(--font-neutral-base)">
|
|
33196
|
-
<!-- the icon the application gives the aggregation in the filters custom JSON, as on the filter button -->
|
|
33197
|
-
@if (chip.icon) {
|
|
33198
|
-
<fa-icon [faClass]="chip.icon" class="shrink-0" />
|
|
33199
|
-
}
|
|
33200
|
-
{{ chip.text }}
|
|
33201
|
-
</span>
|
|
33202
|
-
}
|
|
33203
|
-
</div>
|
|
33204
|
-
}
|
|
33205
|
-
} @else {
|
|
33206
|
-
<!-- The empty-state rule of the list features: the feature's icon on a coloured tile, a title, one hint. -->
|
|
33207
|
-
<div class="flex flex-col items-center gap-(--spacing-xs) px-(--spacing-md) py-(--spacing-lg) text-center">
|
|
33208
|
-
<span class="inline-flex size-12 items-center justify-center rounded-(--radius-md) bg-(--bg-cyan-base-alt) text-(--font-cyan-muted)" aria-hidden="true">
|
|
33209
|
-
<quick-filter-icon class="size-6 opacity-80" />
|
|
33210
|
-
</span>
|
|
33211
|
-
<span class="text-(length:--size-base) font-semibold text-(--font-neutral-base)">{{
|
|
33212
|
-
(query() ? "quickFilters.noMatch" : "quickFilters.empty") | transloco
|
|
33213
|
-
}}</span>
|
|
33214
|
-
@if (!query()) {
|
|
33215
|
-
<span class="text-(length:--size-sm) text-(--font-neutral-muted)">{{ "quickFilters.emptyHint" | transloco }}</span>
|
|
33216
|
-
}
|
|
33217
|
-
</div>
|
|
33218
|
-
}
|
|
33219
|
-
|
|
33220
|
-
</div>
|
|
34463
|
+
<quick-filters-list class="flex-1" [searchable]="true" (applied)="apply($event)" />
|
|
33221
34464
|
<PopoverResizeHandle edge="bottom" [label]="'quickFilters.resize' | transloco" (resizeStart)="onResizeStart()" />
|
|
33222
34465
|
}
|
|
33223
34466
|
</div>
|
|
@@ -33226,12 +34469,6 @@ var QuickFiltersComponent = class QuickFiltersComponent {
|
|
|
33226
34469
|
`,
|
|
33227
34470
|
isInline: true,
|
|
33228
34471
|
dependencies: [
|
|
33229
|
-
{
|
|
33230
|
-
kind: "component",
|
|
33231
|
-
type: FaIconComponent,
|
|
33232
|
-
selector: "fa-icon, FaIcon",
|
|
33233
|
-
inputs: ["faClass", "class"]
|
|
33234
|
-
},
|
|
33235
34472
|
{
|
|
33236
34473
|
kind: "directive",
|
|
33237
34474
|
type: ButtonComponent,
|
|
@@ -33244,48 +34481,6 @@ var QuickFiltersComponent = class QuickFiltersComponent {
|
|
|
33244
34481
|
"size"
|
|
33245
34482
|
]
|
|
33246
34483
|
},
|
|
33247
|
-
{
|
|
33248
|
-
kind: "directive",
|
|
33249
|
-
type: ListComponent,
|
|
33250
|
-
selector: "List,list",
|
|
33251
|
-
inputs: [
|
|
33252
|
-
"class",
|
|
33253
|
-
"variant",
|
|
33254
|
-
"size"
|
|
33255
|
-
],
|
|
33256
|
-
exportAs: ["list"]
|
|
33257
|
-
},
|
|
33258
|
-
{
|
|
33259
|
-
kind: "directive",
|
|
33260
|
-
type: ListItemComponent,
|
|
33261
|
-
selector: "ListItem,list-item,listitem",
|
|
33262
|
-
inputs: [
|
|
33263
|
-
"class",
|
|
33264
|
-
"variant",
|
|
33265
|
-
"size",
|
|
33266
|
-
"depth"
|
|
33267
|
-
],
|
|
33268
|
-
exportAs: ["listItem"]
|
|
33269
|
-
},
|
|
33270
|
-
{
|
|
33271
|
-
kind: "directive",
|
|
33272
|
-
type: ListItemIconDirective,
|
|
33273
|
-
selector: "ListItemIcon,list-item-icon,listitemicon",
|
|
33274
|
-
inputs: ["class"]
|
|
33275
|
-
},
|
|
33276
|
-
{
|
|
33277
|
-
kind: "directive",
|
|
33278
|
-
type: ListItemLeftDirective,
|
|
33279
|
-
selector: "ListItemLeft,list-item-left,listitemleft",
|
|
33280
|
-
inputs: ["class"]
|
|
33281
|
-
},
|
|
33282
|
-
{
|
|
33283
|
-
kind: "component",
|
|
33284
|
-
type: ListItemTextComponent,
|
|
33285
|
-
selector: "ListItemText,list-item-text,listitemtext",
|
|
33286
|
-
inputs: ["class", "subtext"],
|
|
33287
|
-
exportAs: ["listItemText"]
|
|
33288
|
-
},
|
|
33289
34484
|
{
|
|
33290
34485
|
kind: "directive",
|
|
33291
34486
|
type: PopoverDirective,
|
|
@@ -33314,17 +34509,23 @@ var QuickFiltersComponent = class QuickFiltersComponent {
|
|
|
33314
34509
|
],
|
|
33315
34510
|
outputs: ["resizeStart"]
|
|
33316
34511
|
},
|
|
33317
|
-
{
|
|
33318
|
-
kind: "component",
|
|
33319
|
-
type: SearchInputComponent,
|
|
33320
|
-
selector: "SearchInput:not([suggestions]), search-input:not([suggestions]), searchinput:not([suggestions])"
|
|
33321
|
-
},
|
|
33322
34512
|
{
|
|
33323
34513
|
kind: "component",
|
|
33324
34514
|
type: QuickFilterIcon,
|
|
33325
34515
|
selector: "quick-filter, quick-filter-icon, QuickFilter, QuickFilterIcon",
|
|
33326
34516
|
inputs: ["class"]
|
|
33327
34517
|
},
|
|
34518
|
+
{
|
|
34519
|
+
kind: "component",
|
|
34520
|
+
type: QuickFiltersListComponent,
|
|
34521
|
+
selector: "QuickFiltersList, quick-filters-list",
|
|
34522
|
+
inputs: [
|
|
34523
|
+
"searchable",
|
|
34524
|
+
"deletable",
|
|
34525
|
+
"emptyHint"
|
|
34526
|
+
],
|
|
34527
|
+
outputs: ["applied", "deleted"]
|
|
34528
|
+
},
|
|
33328
34529
|
{
|
|
33329
34530
|
kind: "pipe",
|
|
33330
34531
|
type: TranslocoPipe,
|
|
@@ -33344,23 +34545,16 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
33344
34545
|
selector: "QuickFilters, quick-filters",
|
|
33345
34546
|
imports: [
|
|
33346
34547
|
TranslocoPipe,
|
|
33347
|
-
FaIconComponent,
|
|
33348
34548
|
ButtonComponent,
|
|
33349
|
-
ListComponent,
|
|
33350
|
-
ListItemComponent,
|
|
33351
|
-
ListItemIconDirective,
|
|
33352
|
-
ListItemLeftDirective,
|
|
33353
|
-
ListItemTextComponent,
|
|
33354
34549
|
PopoverDirective,
|
|
33355
34550
|
PopoverResizeHandleComponent,
|
|
33356
|
-
|
|
33357
|
-
|
|
34551
|
+
QuickFilterIcon,
|
|
34552
|
+
QuickFiltersListComponent
|
|
33358
34553
|
],
|
|
33359
34554
|
providers: [provideTranslocoScope("quick-filters")],
|
|
33360
34555
|
host: {
|
|
33361
34556
|
class: "flex rounded-(--radius-2xl)",
|
|
33362
|
-
"(focusout)": "closeOnFocusLeave($event)"
|
|
33363
|
-
"(keydown.escape)": "hideDetails()"
|
|
34557
|
+
"(focusout)": "closeOnFocusLeave($event)"
|
|
33364
34558
|
},
|
|
33365
34559
|
template: `
|
|
33366
34560
|
<button
|
|
@@ -33394,87 +34588,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
33394
34588
|
there is room under it. The list takes the height the popover is given. The popover has no padding above and under:
|
|
33395
34589
|
the handles (12px each) are that space, and hug the edges. -->
|
|
33396
34590
|
<PopoverResizeHandle edge="top" [label]="'quickFilters.resize' | transloco" (resizeStart)="onResizeStart()" />
|
|
33397
|
-
<
|
|
33398
|
-
@if (quickFilters().length > 0) {
|
|
33399
|
-
<SearchInput
|
|
33400
|
-
size="md"
|
|
33401
|
-
class="border-(--stroke-primary-light)"
|
|
33402
|
-
[(value)]="query"
|
|
33403
|
-
[placeholder]="'quickFilters.search' | transloco"
|
|
33404
|
-
[aria-label]="'quickFilters.search' | transloco"
|
|
33405
|
-
[clearLabel]="'quickFilters.clearSearch' | transloco" />
|
|
33406
|
-
}
|
|
33407
|
-
|
|
33408
|
-
@if (filtered().length) {
|
|
33409
|
-
<!-- The galactik List, as the design's List Item: a listbox of menu rows with an icon, the name and the number of
|
|
33410
|
-
filters. Choosing a row (click, Enter) applies it; the selection is not kept, the popover closes. Hovering a
|
|
33411
|
-
row lists the filters it holds in a card. -->
|
|
33412
|
-
<List
|
|
33413
|
-
variant="list-menu"
|
|
33414
|
-
size="medium"
|
|
33415
|
-
selectionMode="explicit"
|
|
33416
|
-
class="-mx-1 min-h-0 flex-1 overflow-auto px-1"
|
|
33417
|
-
[attr.aria-label]="'quickFilters.list' | transloco"
|
|
33418
|
-
[value]="picked()"
|
|
33419
|
-
(valueChange)="pick($event)">
|
|
33420
|
-
@for (quickFilter of filtered(); track quickFilter.id) {
|
|
33421
|
-
<ListItem
|
|
33422
|
-
[value]="quickFilter"
|
|
33423
|
-
[label]="quickFilter.name"
|
|
33424
|
-
(mouseenter)="showDetails(quickFilter, $event)"
|
|
33425
|
-
(mouseleave)="scheduleHideDetails()"
|
|
33426
|
-
(focusin)="showDetails(quickFilter, $event)"
|
|
33427
|
-
(focusout)="scheduleHideDetails()">
|
|
33428
|
-
<ListItemLeft>
|
|
33429
|
-
<ListItemIcon><quick-filter-icon /></ListItemIcon>
|
|
33430
|
-
<ListItemText
|
|
33431
|
-
[subtext]="(quickFilter.filters.length === 1 ? 'quickFilters.countOne' : 'quickFilters.countOther') | transloco: { count: quickFilter.filters.length }">
|
|
33432
|
-
{{ quickFilter.name }}
|
|
33433
|
-
</ListItemText>
|
|
33434
|
-
</ListItemLeft>
|
|
33435
|
-
</ListItem>
|
|
33436
|
-
}
|
|
33437
|
-
</List>
|
|
33438
|
-
|
|
33439
|
-
<!-- The hover card of the design: the filters a quick filter holds, as chips, hanging under its row. Placed with
|
|
33440
|
-
Floating UI like the popovers, in the top layer so it is above the popover it comes from. It stays while the
|
|
33441
|
-
pointer is on it, and while the focus is on the row. -->
|
|
33442
|
-
@if (details(); as shown) {
|
|
33443
|
-
<div
|
|
33444
|
-
#detailsCard
|
|
33445
|
-
role="tooltip"
|
|
33446
|
-
class="fixed m-0 flex max-w-72 flex-wrap gap-(--spacing-3xs) rounded-(--radius-lg) border border-solid border-(--stroke-neutral-white) bg-(--bg-neutral-white) p-(--spacing-md) shadow-(--shadow-2)"
|
|
33447
|
-
(mouseenter)="cancelHideDetails()"
|
|
33448
|
-
(mouseleave)="scheduleHideDetails()">
|
|
33449
|
-
@for (chip of chips(shown.quickFilter); track $index) {
|
|
33450
|
-
<span
|
|
33451
|
-
data-chip
|
|
33452
|
-
class="inline-flex items-center gap-(--spacing-3xs) rounded-(--radius-rounded) bg-(--bg-neutral-base) py-(--spacing-4xs) pr-(--spacing-2xs) pl-(--spacing-2xs) text-(length:--size-sm) font-medium text-(--font-neutral-base)">
|
|
33453
|
-
<!-- the icon the application gives the aggregation in the filters custom JSON, as on the filter button -->
|
|
33454
|
-
@if (chip.icon) {
|
|
33455
|
-
<fa-icon [faClass]="chip.icon" class="shrink-0" />
|
|
33456
|
-
}
|
|
33457
|
-
{{ chip.text }}
|
|
33458
|
-
</span>
|
|
33459
|
-
}
|
|
33460
|
-
</div>
|
|
33461
|
-
}
|
|
33462
|
-
} @else {
|
|
33463
|
-
<!-- The empty-state rule of the list features: the feature's icon on a coloured tile, a title, one hint. -->
|
|
33464
|
-
<div class="flex flex-col items-center gap-(--spacing-xs) px-(--spacing-md) py-(--spacing-lg) text-center">
|
|
33465
|
-
<span class="inline-flex size-12 items-center justify-center rounded-(--radius-md) bg-(--bg-cyan-base-alt) text-(--font-cyan-muted)" aria-hidden="true">
|
|
33466
|
-
<quick-filter-icon class="size-6 opacity-80" />
|
|
33467
|
-
</span>
|
|
33468
|
-
<span class="text-(length:--size-base) font-semibold text-(--font-neutral-base)">{{
|
|
33469
|
-
(query() ? "quickFilters.noMatch" : "quickFilters.empty") | transloco
|
|
33470
|
-
}}</span>
|
|
33471
|
-
@if (!query()) {
|
|
33472
|
-
<span class="text-(length:--size-sm) text-(--font-neutral-muted)">{{ "quickFilters.emptyHint" | transloco }}</span>
|
|
33473
|
-
}
|
|
33474
|
-
</div>
|
|
33475
|
-
}
|
|
33476
|
-
|
|
33477
|
-
</div>
|
|
34591
|
+
<quick-filters-list class="flex-1" [searchable]="true" (applied)="apply($event)" />
|
|
33478
34592
|
<PopoverResizeHandle edge="bottom" [label]="'quickFilters.resize' | transloco" (resizeStart)="onResizeStart()" />
|
|
33479
34593
|
}
|
|
33480
34594
|
</div>
|
|
@@ -33521,10 +34635,6 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
33521
34635
|
type: i0.Output,
|
|
33522
34636
|
args: ["applied"]
|
|
33523
34637
|
}],
|
|
33524
|
-
detailsCard: [{
|
|
33525
|
-
type: i0.ViewChild,
|
|
33526
|
-
args: ["detailsCard", { isSignal: true }]
|
|
33527
|
-
}],
|
|
33528
34638
|
popover: [{
|
|
33529
34639
|
type: i0.ViewChild,
|
|
33530
34640
|
args: [i0.forwardRef(() => PopoverDirective), { isSignal: true }]
|
|
@@ -33965,6 +35075,115 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
33965
35075
|
}]
|
|
33966
35076
|
}
|
|
33967
35077
|
});
|
|
35078
|
+
var PopupContentOutlet = class PopupContentOutlet {
|
|
35079
|
+
container = inject(ViewContainerRef);
|
|
35080
|
+
injector = inject(Injector);
|
|
35081
|
+
popupContentOutlet = input.required(...ngDevMode ? [{ debugName: "popupContentOutlet" }] : /* istanbul ignore next */ []);
|
|
35082
|
+
popupContentContext = input.required(...ngDevMode ? [{ debugName: "popupContentContext" }] : /* istanbul ignore next */ []);
|
|
35083
|
+
constructor() {
|
|
35084
|
+
effect(() => {
|
|
35085
|
+
const template = this.popupContentOutlet();
|
|
35086
|
+
const context = this.popupContentContext();
|
|
35087
|
+
untracked(() => {
|
|
35088
|
+
this.container.clear();
|
|
35089
|
+
this.container.createEmbeddedView(template, context, { injector: this.injector });
|
|
35090
|
+
});
|
|
35091
|
+
});
|
|
35092
|
+
}
|
|
35093
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
35094
|
+
minVersion: "12.0.0",
|
|
35095
|
+
version: "22.2.1",
|
|
35096
|
+
ngImport: i0,
|
|
35097
|
+
type: PopupContentOutlet,
|
|
35098
|
+
deps: [],
|
|
35099
|
+
target: i0.ɵɵFactoryTarget.Directive
|
|
35100
|
+
});
|
|
35101
|
+
static ɵdir = i0.ɵɵngDeclareDirective({
|
|
35102
|
+
minVersion: "17.1.0",
|
|
35103
|
+
version: "22.2.1",
|
|
35104
|
+
type: PopupContentOutlet,
|
|
35105
|
+
isStandalone: true,
|
|
35106
|
+
selector: "[popupContentOutlet]",
|
|
35107
|
+
inputs: {
|
|
35108
|
+
popupContentOutlet: {
|
|
35109
|
+
classPropertyName: "popupContentOutlet",
|
|
35110
|
+
publicName: "popupContentOutlet",
|
|
35111
|
+
isSignal: true,
|
|
35112
|
+
isRequired: true,
|
|
35113
|
+
transformFunction: null
|
|
35114
|
+
},
|
|
35115
|
+
popupContentContext: {
|
|
35116
|
+
classPropertyName: "popupContentContext",
|
|
35117
|
+
publicName: "popupContentContext",
|
|
35118
|
+
isSignal: true,
|
|
35119
|
+
isRequired: true,
|
|
35120
|
+
transformFunction: null
|
|
35121
|
+
}
|
|
35122
|
+
},
|
|
35123
|
+
ngImport: i0
|
|
35124
|
+
});
|
|
35125
|
+
};
|
|
35126
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
35127
|
+
minVersion: "12.0.0",
|
|
35128
|
+
version: "22.2.1",
|
|
35129
|
+
ngImport: i0,
|
|
35130
|
+
type: PopupContentOutlet,
|
|
35131
|
+
decorators: [{
|
|
35132
|
+
type: Directive,
|
|
35133
|
+
args: [{ selector: "[popupContentOutlet]" }]
|
|
35134
|
+
}],
|
|
35135
|
+
ctorParameters: () => [],
|
|
35136
|
+
propDecorators: {
|
|
35137
|
+
popupContentOutlet: [{
|
|
35138
|
+
type: i0.Input,
|
|
35139
|
+
args: [{
|
|
35140
|
+
isSignal: true,
|
|
35141
|
+
alias: "popupContentOutlet",
|
|
35142
|
+
required: true
|
|
35143
|
+
}]
|
|
35144
|
+
}],
|
|
35145
|
+
popupContentContext: [{
|
|
35146
|
+
type: i0.Input,
|
|
35147
|
+
args: [{
|
|
35148
|
+
isSignal: true,
|
|
35149
|
+
alias: "popupContentContext",
|
|
35150
|
+
required: true
|
|
35151
|
+
}]
|
|
35152
|
+
}]
|
|
35153
|
+
}
|
|
35154
|
+
});
|
|
35155
|
+
var SearchBoxPopupDirective = class SearchBoxPopupDirective {
|
|
35156
|
+
template = inject(TemplateRef);
|
|
35157
|
+
static ngTemplateContextGuard(_dir, _ctx) {
|
|
35158
|
+
return true;
|
|
35159
|
+
}
|
|
35160
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
35161
|
+
minVersion: "12.0.0",
|
|
35162
|
+
version: "22.2.1",
|
|
35163
|
+
ngImport: i0,
|
|
35164
|
+
type: SearchBoxPopupDirective,
|
|
35165
|
+
deps: [],
|
|
35166
|
+
target: i0.ɵɵFactoryTarget.Directive
|
|
35167
|
+
});
|
|
35168
|
+
static ɵdir = i0.ɵɵngDeclareDirective({
|
|
35169
|
+
minVersion: "14.0.0",
|
|
35170
|
+
version: "22.2.1",
|
|
35171
|
+
type: SearchBoxPopupDirective,
|
|
35172
|
+
isStandalone: true,
|
|
35173
|
+
selector: "ng-template[searchBoxPopup]",
|
|
35174
|
+
ngImport: i0
|
|
35175
|
+
});
|
|
35176
|
+
};
|
|
35177
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
35178
|
+
minVersion: "12.0.0",
|
|
35179
|
+
version: "22.2.1",
|
|
35180
|
+
ngImport: i0,
|
|
35181
|
+
type: SearchBoxPopupDirective,
|
|
35182
|
+
decorators: [{
|
|
35183
|
+
type: Directive,
|
|
35184
|
+
args: [{ selector: "ng-template[searchBoxPopup]" }]
|
|
35185
|
+
}]
|
|
35186
|
+
});
|
|
33968
35187
|
let nextHintId = 0;
|
|
33969
35188
|
const SEARCH_BOX_CATEGORIES_SORT = new InjectionToken("Order by preference for the categories of the suggestions", { factory: () => [
|
|
33970
35189
|
"full-text",
|
|
@@ -33998,10 +35217,20 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
33998
35217
|
...ngDevMode ? { debugName: "popupOffset" } : /* istanbul ignore next */ {},
|
|
33999
35218
|
transform: numberAttribute
|
|
34000
35219
|
});
|
|
35220
|
+
popupFitScreen = input(void 0, {
|
|
35221
|
+
...ngDevMode ? { debugName: "popupFitScreen" } : /* istanbul ignore next */ {},
|
|
35222
|
+
transform: (value) => value === void 0 ? void 0 : booleanAttribute(value)
|
|
35223
|
+
});
|
|
35224
|
+
popupFlip = input(void 0, {
|
|
35225
|
+
...ngDevMode ? { debugName: "popupFlip" } : /* istanbul ignore next */ {},
|
|
35226
|
+
transform: (value) => value === void 0 ? void 0 : booleanAttribute(value)
|
|
35227
|
+
});
|
|
34001
35228
|
placeholderOverride = input(void 0, {
|
|
34002
35229
|
...ngDevMode ? { debugName: "placeholderOverride" } : /* istanbul ignore next */ {},
|
|
34003
35230
|
alias: "placeholder"
|
|
34004
35231
|
});
|
|
35232
|
+
suggestionsProvider = input(void 0, ...ngDevMode ? [{ debugName: "suggestionsProvider" }] : /* istanbul ignore next */ []);
|
|
35233
|
+
customPopup = contentChild(SearchBoxPopupDirective, ...ngDevMode ? [{ debugName: "customPopup" }] : /* istanbul ignore next */ []);
|
|
34005
35234
|
searched = output();
|
|
34006
35235
|
debounced = output();
|
|
34007
35236
|
saved = output();
|
|
@@ -34043,9 +35272,12 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34043
35272
|
savedSearches = computed(() => this.userSettingsStore.savedSearches() ?? [], ...ngDevMode ? [{ debugName: "savedSearches" }] : /* istanbul ignore next */ []);
|
|
34044
35273
|
savedTexts = computed(() => new Set(this.savedSearches().map((s) => s.display || s.label)), ...ngDevMode ? [{ debugName: "savedTexts" }] : /* istanbul ignore next */ []);
|
|
34045
35274
|
inline = computed(() => this.popup() === "inline", ...ngDevMode ? [{ debugName: "inline" }] : /* istanbul ignore next */ []);
|
|
35275
|
+
fitScreen = computed(() => this.popupFitScreen() ?? (this.inline() && this.size() === "hero"), ...ngDevMode ? [{ debugName: "fitScreen" }] : /* istanbul ignore next */ []);
|
|
35276
|
+
flip = computed(() => this.popupFlip() ?? this.size() !== "hero", ...ngDevMode ? [{ debugName: "flip" }] : /* istanbul ignore next */ []);
|
|
35277
|
+
floating = computed(() => !this.inline() || this.fitScreen(), ...ngDevMode ? [{ debugName: "floating" }] : /* istanbul ignore next */ []);
|
|
34046
35278
|
barClass = computed(() => this.size() === "hero" ? "w-full" : "w-full gap-(--spacing-3xs)! pe-(--spacing-3xs)", ...ngDevMode ? [{ debugName: "barClass" }] : /* istanbul ignore next */ []);
|
|
34047
|
-
popupClass = computed(() => cn("flex flex-col transition-[opacity,translate] duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] starting:-translate-y-1.5 starting:opacity-0 motion-reduce:transition-none", this.inline() ? "absolute inset-x-0 top-full z-20 mx-auto mt-(--spacing-2xs) w-[calc(100%-74px)] gap-(--spacing-3xs)" : "fixed z-20 gap-(--spacing-2xs) overflow-hidden rounded-(--radius-xl) border border-solid border-(--stroke-neutral-white) bg-(--bg-neutral-white) p-(--spacing-xs) shadow-(--shadow-2)"), ...ngDevMode ? [{ debugName: "popupClass" }] : /* istanbul ignore next */ []);
|
|
34048
|
-
listClass = computed(() => cn("h-68 scroll-pt-8 overflow-auto overscroll-contain text-(--font-primary-base) focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-2 focus-visible:outline-(--stroke-focus)", this.inline() ? "bg-transparent" : "bg-(--bg-neutral-white)"), ...ngDevMode ? [{ debugName: "listClass" }] : /* istanbul ignore next */ []);
|
|
35279
|
+
popupClass = computed(() => cn("flex flex-col transition-[opacity,translate] duration-200 ease-[cubic-bezier(0.32,0.72,0,1)] starting:-translate-y-1.5 starting:opacity-0 motion-reduce:transition-none", this.inline() ? this.fitScreen() ? "fixed z-20 max-h-(--search-box-max-h) w-[calc(var(--search-box-w,100vw)-74px)] gap-(--spacing-3xs)" : "absolute inset-x-0 top-full z-20 mx-auto mt-(--spacing-2xs) w-[calc(100%-74px)] gap-(--spacing-3xs)" : "fixed z-20 gap-(--spacing-2xs) overflow-hidden rounded-(--radius-xl) border border-solid border-(--stroke-neutral-white) bg-(--bg-neutral-white) p-(--spacing-xs) shadow-(--shadow-2)", !this.inline() && this.fitScreen() && "max-h-(--search-box-max-h)"), ...ngDevMode ? [{ debugName: "popupClass" }] : /* istanbul ignore next */ []);
|
|
35280
|
+
listClass = computed(() => cn("h-68 scroll-pt-8 overflow-auto overscroll-contain text-(--font-primary-base) focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-2 focus-visible:outline-(--stroke-focus)", this.fitScreen() && "min-h-0 shrink", this.inline() ? "bg-transparent" : "bg-(--bg-neutral-white)"), ...ngDevMode ? [{ debugName: "listClass" }] : /* istanbul ignore next */ []);
|
|
34049
35281
|
rowClass = computed(() => cn("group/row data-list-item flex min-h-9 w-full cursor-pointer items-center gap-(--spacing-2xs) rounded-(--radius-sm) px-(--spacing-2xs) py-(--spacing-3xs) text-left text-(length:--size-base) text-(--font-primary-muted) transition-[background-color] duration-100 ease-[ease] hover:bg-(--bg-primary-lighter) hover:outline-none focus-visible:bg-(--bg-primary-lighter) aria-selected:text-(--font-primary-dark) data-[active=true]:bg-(--bg-primary-lighter) motion-reduce:transition-none"), ...ngDevMode ? [{ debugName: "rowClass" }] : /* istanbul ignore next */ []);
|
|
34050
35282
|
titleClass = computed(() => cn("sticky top-0 px-(--spacing-2xs) pt-(--spacing-xs) pb-(--spacing-3xs) text-(length:--size-xs) font-semibold tracking-wider text-(--font-primary-muted) uppercase", this.inline() ? "bg-(--layer-page-layer,var(--bg-neutral-base))" : "bg-(--bg-neutral-white)"), ...ngDevMode ? [{ debugName: "titleClass" }] : /* istanbul ignore next */ []);
|
|
34051
35283
|
hasFilters = computed(() => {
|
|
@@ -34064,9 +35296,10 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34064
35296
|
params: () => ({
|
|
34065
35297
|
text: this.text(),
|
|
34066
35298
|
wasSearchClicked: this.wasSearchClicked(),
|
|
34067
|
-
autocomplete: this.autocomplete()
|
|
35299
|
+
autocomplete: this.autocomplete(),
|
|
35300
|
+
provider: this.suggestionsProvider()
|
|
34068
35301
|
}),
|
|
34069
|
-
loader: async ({ params: { text, autocomplete } }) => this.fetchSuggestions({
|
|
35302
|
+
loader: async ({ params: { text, autocomplete, provider } }) => provider ? this.fetchFromProvider(provider, text) : this.fetchSuggestions({
|
|
34070
35303
|
text,
|
|
34071
35304
|
autocomplete
|
|
34072
35305
|
})
|
|
@@ -34081,14 +35314,33 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34081
35314
|
}
|
|
34082
35315
|
return isLoading ? this.previousSuggestions : [];
|
|
34083
35316
|
}, ...ngDevMode ? [{ debugName: "suggestions" }] : /* istanbul ignore next */ []);
|
|
35317
|
+
popupTemplateContext = { $implicit: {
|
|
35318
|
+
text: this.text,
|
|
35319
|
+
typed: this.typed,
|
|
35320
|
+
suggestions: this.suggestions,
|
|
35321
|
+
loading: this.suggestionsResource.isLoading,
|
|
35322
|
+
savedSearches: this.savedSearches,
|
|
35323
|
+
navigatedSinceTyping: this.navigatedSinceTyping,
|
|
35324
|
+
commit: (text) => this.commitText(text),
|
|
35325
|
+
preview: (text) => this.previewSuggestion(text),
|
|
35326
|
+
openSaved: (saved) => this.openSaved(saved),
|
|
35327
|
+
isSaved: (text) => this.savedTexts().has(text),
|
|
35328
|
+
toggleSaved: (text) => this.toggleSavedText(text),
|
|
35329
|
+
collapse: () => this.collapse(),
|
|
35330
|
+
focusField: () => this.focusField()
|
|
35331
|
+
} };
|
|
34084
35332
|
inputListenersAttached = false;
|
|
34085
35333
|
constructor() {
|
|
34086
35334
|
this.destroyRef.onDestroy(() => this.field().reset());
|
|
34087
35335
|
injectFloatingPanel({
|
|
34088
|
-
panel: () => this.
|
|
35336
|
+
panel: () => this.floating() ? this.popupCard()?.nativeElement : null,
|
|
34089
35337
|
anchor: () => this.barElement()?.nativeElement,
|
|
34090
|
-
|
|
34091
|
-
|
|
35338
|
+
placement: () => this.inline() ? "bottom" : "bottom-start",
|
|
35339
|
+
offset: () => this.inline() ? 8 : this.popupOffset(),
|
|
35340
|
+
flip: () => this.flip(),
|
|
35341
|
+
matchAnchorWidth: () => !this.inline(),
|
|
35342
|
+
anchorWidthVariable: "--search-box-w",
|
|
35343
|
+
maxHeightVariable: "--search-box-max-h"
|
|
34092
35344
|
});
|
|
34093
35345
|
this.eventManager.addEventListener(this.host.nativeElement, "click", () => this.wasSearchClicked.set(true));
|
|
34094
35346
|
let firstText = true;
|
|
@@ -34135,7 +35387,15 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34135
35387
|
const onInput = () => this.navigatedSinceTyping.set(false);
|
|
34136
35388
|
element.addEventListener("keydown", onKeydown);
|
|
34137
35389
|
element.addEventListener("input", onInput);
|
|
35390
|
+
const activeObserver = new MutationObserver(() => {
|
|
35391
|
+
if (this.customPopup()) this.revealActiveOption();
|
|
35392
|
+
});
|
|
35393
|
+
activeObserver.observe(element, {
|
|
35394
|
+
attributes: true,
|
|
35395
|
+
attributeFilter: ["aria-activedescendant"]
|
|
35396
|
+
});
|
|
34138
35397
|
this.destroyRef.onDestroy(() => {
|
|
35398
|
+
activeObserver.disconnect();
|
|
34139
35399
|
element.removeEventListener("keydown", onKeydown);
|
|
34140
35400
|
element.removeEventListener("input", onInput);
|
|
34141
35401
|
});
|
|
@@ -34166,6 +35426,10 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34166
35426
|
if (this.host.nativeElement.contains(next)) return;
|
|
34167
35427
|
this.collapse();
|
|
34168
35428
|
}
|
|
35429
|
+
revealActiveOption() {
|
|
35430
|
+
const id = this.inputComponent()?.searchInput().nativeElement.getAttribute("aria-activedescendant");
|
|
35431
|
+
if (id) document.getElementById(id)?.scrollIntoView({ block: "nearest" });
|
|
35432
|
+
}
|
|
34169
35433
|
collapse() {
|
|
34170
35434
|
this.combobox()?.expanded.set(false);
|
|
34171
35435
|
}
|
|
@@ -34194,9 +35458,12 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34194
35458
|
commitSuggestion(suggestion, event) {
|
|
34195
35459
|
if (!suggestion.display) return;
|
|
34196
35460
|
const text = suggestion.display;
|
|
35461
|
+
this.commitText(text, event.currentTarget?.getAttribute("data-text") || text);
|
|
35462
|
+
}
|
|
35463
|
+
commitText(text, searched = text) {
|
|
34197
35464
|
this.searchInputText.set(text);
|
|
34198
35465
|
this.collapse();
|
|
34199
|
-
this.runSearch(
|
|
35466
|
+
this.runSearch(searched);
|
|
34200
35467
|
}
|
|
34201
35468
|
openSaved(saved) {
|
|
34202
35469
|
const queryParams = savedSearchQueryParams(saved);
|
|
@@ -34328,6 +35595,14 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34328
35595
|
}
|
|
34329
35596
|
return category;
|
|
34330
35597
|
}
|
|
35598
|
+
async fetchFromProvider(provider, text) {
|
|
35599
|
+
try {
|
|
35600
|
+
return await provider(text);
|
|
35601
|
+
} catch (err) {
|
|
35602
|
+
error("Error getting suggestions from the suggestions provider", err);
|
|
35603
|
+
return [];
|
|
35604
|
+
}
|
|
35605
|
+
}
|
|
34331
35606
|
fetchSuggestions = async ({ text, autocomplete = 3 }) => {
|
|
34332
35607
|
const fromUserSettings = this.autocompleteService.getFromUserSettingsForText(text, autocomplete);
|
|
34333
35608
|
if (!text) return fromUserSettings;
|
|
@@ -34405,6 +35680,20 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34405
35680
|
isRequired: false,
|
|
34406
35681
|
transformFunction: null
|
|
34407
35682
|
},
|
|
35683
|
+
popupFitScreen: {
|
|
35684
|
+
classPropertyName: "popupFitScreen",
|
|
35685
|
+
publicName: "popupFitScreen",
|
|
35686
|
+
isSignal: true,
|
|
35687
|
+
isRequired: false,
|
|
35688
|
+
transformFunction: null
|
|
35689
|
+
},
|
|
35690
|
+
popupFlip: {
|
|
35691
|
+
classPropertyName: "popupFlip",
|
|
35692
|
+
publicName: "popupFlip",
|
|
35693
|
+
isSignal: true,
|
|
35694
|
+
isRequired: false,
|
|
35695
|
+
transformFunction: null
|
|
35696
|
+
},
|
|
34408
35697
|
placeholderOverride: {
|
|
34409
35698
|
classPropertyName: "placeholderOverride",
|
|
34410
35699
|
publicName: "placeholder",
|
|
@@ -34412,6 +35701,13 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34412
35701
|
isRequired: false,
|
|
34413
35702
|
transformFunction: null
|
|
34414
35703
|
},
|
|
35704
|
+
suggestionsProvider: {
|
|
35705
|
+
classPropertyName: "suggestionsProvider",
|
|
35706
|
+
publicName: "suggestionsProvider",
|
|
35707
|
+
isSignal: true,
|
|
35708
|
+
isRequired: false,
|
|
35709
|
+
transformFunction: null
|
|
35710
|
+
},
|
|
34415
35711
|
searchInputText: {
|
|
34416
35712
|
classPropertyName: "searchInputText",
|
|
34417
35713
|
publicName: "searchInputText",
|
|
@@ -34436,6 +35732,13 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34436
35732
|
classAttribute: "relative block"
|
|
34437
35733
|
},
|
|
34438
35734
|
providers: [provideTranslocoScope("search-box"), provideTranslocoScope("searches")],
|
|
35735
|
+
queries: [{
|
|
35736
|
+
propertyName: "customPopup",
|
|
35737
|
+
first: true,
|
|
35738
|
+
predicate: SearchBoxPopupDirective,
|
|
35739
|
+
descendants: true,
|
|
35740
|
+
isSignal: true
|
|
35741
|
+
}],
|
|
34439
35742
|
viewQueries: [
|
|
34440
35743
|
{
|
|
34441
35744
|
propertyName: "inputComponent",
|
|
@@ -34517,6 +35820,10 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34517
35820
|
page, where it takes the place of the filters) or a floating card (the results header). It fades and slides in
|
|
34518
35821
|
once, when the popup opens (starting: gives the transition its first frame). -->
|
|
34519
35822
|
<div #popupCard [class]="popupClass()" (mousedown)="$event.preventDefault()">
|
|
35823
|
+
@if (customPopup(); as custom) {
|
|
35824
|
+
<!-- The application's own content (searchBoxPopup): the card, its placement and its opening stay the search box's. -->
|
|
35825
|
+
<ng-container [popupContentOutlet]="custom.template" [popupContentContext]="popupTemplateContext" />
|
|
35826
|
+
} @else {
|
|
34520
35827
|
<!-- The tabs hold no panel of their own: only one list exists at a time, since two listboxes would both
|
|
34521
35828
|
register with the combobox. The div below is the one tabpanel, named by the selected tab. -->
|
|
34522
35829
|
<Tabs
|
|
@@ -34540,7 +35847,9 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34540
35847
|
</TabList>
|
|
34541
35848
|
</Tabs>
|
|
34542
35849
|
|
|
34543
|
-
|
|
35850
|
+
<!-- A flex column with min-h-0, so that the list inside can shrink and scroll when the popup is capped (popupFitScreen):
|
|
35851
|
+
a plain block would keep the height of its list as its minimum, and the card would only cut the bottom off. -->
|
|
35852
|
+
<div role="tabpanel" class="flex min-h-0 flex-col" [attr.aria-label]="(tab() === 'saved' ? 'searchBox.tabs.saved' : 'searchBox.tabs.suggestions') | transloco">
|
|
34544
35853
|
@if (tab() === "saved") {
|
|
34545
35854
|
<ul
|
|
34546
35855
|
#savedList
|
|
@@ -34571,7 +35880,14 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34571
35880
|
(click)="removeSaved(saved, $event)">
|
|
34572
35881
|
<star-icon solid class="size-(--icon-sm)" />
|
|
34573
35882
|
</span>
|
|
34574
|
-
<p
|
|
35883
|
+
<p
|
|
35884
|
+
class="min-w-0 flex-1 truncate text-(--font-primary-base)"
|
|
35885
|
+
truncated
|
|
35886
|
+
#cutSaved="truncated"
|
|
35887
|
+
[tooltip]="saved.display || saved.label || ''"
|
|
35888
|
+
[tooltipDisabled]="!cutSaved.isTruncated()">
|
|
35889
|
+
{{ saved.display || saved.label }}
|
|
35890
|
+
</p>
|
|
34575
35891
|
</li>
|
|
34576
35892
|
} @empty {
|
|
34577
35893
|
<!-- The empty-state rule of the list features: the feature's icon on a coloured tile, a title, one hint. -->
|
|
@@ -34642,7 +35958,12 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34642
35958
|
</span>
|
|
34643
35959
|
</span>
|
|
34644
35960
|
|
|
34645
|
-
<p
|
|
35961
|
+
<p
|
|
35962
|
+
class="min-w-0 flex-1 truncate text-(--font-primary-base)"
|
|
35963
|
+
truncated
|
|
35964
|
+
#cutSuggest="truncated"
|
|
35965
|
+
[tooltip]="suggest.display ?? ''"
|
|
35966
|
+
[tooltipDisabled]="!cutSuggest.isTruncated()">
|
|
34646
35967
|
@for (chunk of suggest.display! | highlightWord: text() : 10; track $index) {
|
|
34647
35968
|
<span [class]="{ 'font-semibold': chunk.match }">{{ chunk.text }}</span>
|
|
34648
35969
|
}
|
|
@@ -34663,6 +35984,7 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34663
35984
|
</ul>
|
|
34664
35985
|
}
|
|
34665
35986
|
</div>
|
|
35987
|
+
}
|
|
34666
35988
|
</div>
|
|
34667
35989
|
</ng-template>
|
|
34668
35990
|
}
|
|
@@ -34685,6 +36007,12 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34685
36007
|
],
|
|
34686
36008
|
exportAs: ["ngComponentOutlet"]
|
|
34687
36009
|
},
|
|
36010
|
+
{
|
|
36011
|
+
kind: "directive",
|
|
36012
|
+
type: PopupContentOutlet,
|
|
36013
|
+
selector: "[popupContentOutlet]",
|
|
36014
|
+
inputs: ["popupContentOutlet", "popupContentContext"]
|
|
36015
|
+
},
|
|
34688
36016
|
{
|
|
34689
36017
|
kind: "directive",
|
|
34690
36018
|
type: FormField,
|
|
@@ -34805,6 +36133,13 @@ var SearchBoxComponent = class SearchBoxComponent {
|
|
|
34805
36133
|
],
|
|
34806
36134
|
exportAs: ["tooltip"]
|
|
34807
36135
|
},
|
|
36136
|
+
{
|
|
36137
|
+
kind: "directive",
|
|
36138
|
+
type: TruncatedDirective,
|
|
36139
|
+
selector: "[truncated]",
|
|
36140
|
+
inputs: ["truncated"],
|
|
36141
|
+
exportAs: ["truncated"]
|
|
36142
|
+
},
|
|
34808
36143
|
{
|
|
34809
36144
|
kind: "component",
|
|
34810
36145
|
type: SavedSearchPopover,
|
|
@@ -34836,6 +36171,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
34836
36171
|
selector: "SearchBox, search-box",
|
|
34837
36172
|
imports: [
|
|
34838
36173
|
NgComponentOutlet,
|
|
36174
|
+
PopupContentOutlet,
|
|
34839
36175
|
FormField,
|
|
34840
36176
|
FormRoot,
|
|
34841
36177
|
ComboboxPopup,
|
|
@@ -34850,6 +36186,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
34850
36186
|
TabListComponent,
|
|
34851
36187
|
TabComponent,
|
|
34852
36188
|
TooltipDirective,
|
|
36189
|
+
TruncatedDirective,
|
|
34853
36190
|
SavedSearchPopover
|
|
34854
36191
|
],
|
|
34855
36192
|
providers: [provideTranslocoScope("search-box"), provideTranslocoScope("searches")],
|
|
@@ -34901,6 +36238,10 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
34901
36238
|
page, where it takes the place of the filters) or a floating card (the results header). It fades and slides in
|
|
34902
36239
|
once, when the popup opens (starting: gives the transition its first frame). -->
|
|
34903
36240
|
<div #popupCard [class]="popupClass()" (mousedown)="$event.preventDefault()">
|
|
36241
|
+
@if (customPopup(); as custom) {
|
|
36242
|
+
<!-- The application's own content (searchBoxPopup): the card, its placement and its opening stay the search box's. -->
|
|
36243
|
+
<ng-container [popupContentOutlet]="custom.template" [popupContentContext]="popupTemplateContext" />
|
|
36244
|
+
} @else {
|
|
34904
36245
|
<!-- The tabs hold no panel of their own: only one list exists at a time, since two listboxes would both
|
|
34905
36246
|
register with the combobox. The div below is the one tabpanel, named by the selected tab. -->
|
|
34906
36247
|
<Tabs
|
|
@@ -34924,7 +36265,9 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
34924
36265
|
</TabList>
|
|
34925
36266
|
</Tabs>
|
|
34926
36267
|
|
|
34927
|
-
|
|
36268
|
+
<!-- A flex column with min-h-0, so that the list inside can shrink and scroll when the popup is capped (popupFitScreen):
|
|
36269
|
+
a plain block would keep the height of its list as its minimum, and the card would only cut the bottom off. -->
|
|
36270
|
+
<div role="tabpanel" class="flex min-h-0 flex-col" [attr.aria-label]="(tab() === 'saved' ? 'searchBox.tabs.saved' : 'searchBox.tabs.suggestions') | transloco">
|
|
34928
36271
|
@if (tab() === "saved") {
|
|
34929
36272
|
<ul
|
|
34930
36273
|
#savedList
|
|
@@ -34955,7 +36298,14 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
34955
36298
|
(click)="removeSaved(saved, $event)">
|
|
34956
36299
|
<star-icon solid class="size-(--icon-sm)" />
|
|
34957
36300
|
</span>
|
|
34958
|
-
<p
|
|
36301
|
+
<p
|
|
36302
|
+
class="min-w-0 flex-1 truncate text-(--font-primary-base)"
|
|
36303
|
+
truncated
|
|
36304
|
+
#cutSaved="truncated"
|
|
36305
|
+
[tooltip]="saved.display || saved.label || ''"
|
|
36306
|
+
[tooltipDisabled]="!cutSaved.isTruncated()">
|
|
36307
|
+
{{ saved.display || saved.label }}
|
|
36308
|
+
</p>
|
|
34959
36309
|
</li>
|
|
34960
36310
|
} @empty {
|
|
34961
36311
|
<!-- The empty-state rule of the list features: the feature's icon on a coloured tile, a title, one hint. -->
|
|
@@ -35026,7 +36376,12 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
35026
36376
|
</span>
|
|
35027
36377
|
</span>
|
|
35028
36378
|
|
|
35029
|
-
<p
|
|
36379
|
+
<p
|
|
36380
|
+
class="min-w-0 flex-1 truncate text-(--font-primary-base)"
|
|
36381
|
+
truncated
|
|
36382
|
+
#cutSuggest="truncated"
|
|
36383
|
+
[tooltip]="suggest.display ?? ''"
|
|
36384
|
+
[tooltipDisabled]="!cutSuggest.isTruncated()">
|
|
35030
36385
|
@for (chunk of suggest.display! | highlightWord: text() : 10; track $index) {
|
|
35031
36386
|
<span [class]="{ 'font-semibold': chunk.match }">{{ chunk.text }}</span>
|
|
35032
36387
|
}
|
|
@@ -35047,6 +36402,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
35047
36402
|
</ul>
|
|
35048
36403
|
}
|
|
35049
36404
|
</div>
|
|
36405
|
+
}
|
|
35050
36406
|
</div>
|
|
35051
36407
|
</ng-template>
|
|
35052
36408
|
}
|
|
@@ -35097,6 +36453,22 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
35097
36453
|
required: false
|
|
35098
36454
|
}]
|
|
35099
36455
|
}],
|
|
36456
|
+
popupFitScreen: [{
|
|
36457
|
+
type: i0.Input,
|
|
36458
|
+
args: [{
|
|
36459
|
+
isSignal: true,
|
|
36460
|
+
alias: "popupFitScreen",
|
|
36461
|
+
required: false
|
|
36462
|
+
}]
|
|
36463
|
+
}],
|
|
36464
|
+
popupFlip: [{
|
|
36465
|
+
type: i0.Input,
|
|
36466
|
+
args: [{
|
|
36467
|
+
isSignal: true,
|
|
36468
|
+
alias: "popupFlip",
|
|
36469
|
+
required: false
|
|
36470
|
+
}]
|
|
36471
|
+
}],
|
|
35100
36472
|
placeholderOverride: [{
|
|
35101
36473
|
type: i0.Input,
|
|
35102
36474
|
args: [{
|
|
@@ -35105,6 +36477,18 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
35105
36477
|
required: false
|
|
35106
36478
|
}]
|
|
35107
36479
|
}],
|
|
36480
|
+
suggestionsProvider: [{
|
|
36481
|
+
type: i0.Input,
|
|
36482
|
+
args: [{
|
|
36483
|
+
isSignal: true,
|
|
36484
|
+
alias: "suggestionsProvider",
|
|
36485
|
+
required: false
|
|
36486
|
+
}]
|
|
36487
|
+
}],
|
|
36488
|
+
customPopup: [{
|
|
36489
|
+
type: i0.ContentChild,
|
|
36490
|
+
args: [i0.forwardRef(() => SearchBoxPopupDirective), { isSignal: true }]
|
|
36491
|
+
}],
|
|
35108
36492
|
searched: [{
|
|
35109
36493
|
type: i0.Output,
|
|
35110
36494
|
args: ["searched"]
|
|
@@ -37783,6 +39167,6 @@ function provideSessionExpiry(options = {}) {
|
|
|
37783
39167
|
const queryNameResolver = () => {
|
|
37784
39168
|
return inject(AppStore).getDefaultQuery()?.name || "";
|
|
37785
39169
|
};
|
|
37786
|
-
export { AGGREGATIONS_NAMES, AGGREGATIONS_NAMES_PRESET_DEFAULT, APP_FEATURES, ATOMIC_CLIENT, ATOMIC_SCOPED_PROVIDERS, AddToCollection, AdvancedFiltersBodyComponent, AdvancedFiltersComponent, AdvancedSearch, AdvancedSearchTabs, AggregationComponent, AggregationDateComponent, AggregationDateRange, AggregationItemDirective, AggregationListComponent, AggregationPanelComponent, AggregationTreeComponent, AggregationsService, AggregationsStore, Alert, AlertDialog, AlertForm, AlertFormComponent, AlertsComponent, AppService, AppStore, ApplicationService, ApplicationStore, AppliedFiltersComponent, ArticleEntities, ArticleEntitiesTabPanel, ArticleExtracts, ArticleExtractsTabPanel, ArticleHighlights, ArticleLabels, ArticleLabelsTabPanel, ArticleMatchLocations, ArticleMatchLocationsTabPanel, ArticleSimilarDocuments, ArticleSimilarDocumentsTabPanel, 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, ConverterSelectComponent, DeleteCollection, DeleteCollectionDialog, DidYouMeanComponent, DocumentLocatorComponent, EditLabels, ErrorComponent, Export, ExportDialog, ExportService, FILTERS_BREAKPOINT, FILTER_DATE_ALLOW_CUSTOM_RANGE, FORM_ALERT_CLASS, FORM_BARE_CLASS, FORM_ERROR_CLASS, FORM_FIELD_CLASS, FORM_FOOTER_CLASS, FORM_LABEL_CLASS, FORM_SURFACE_CLASS, FaIconComponent, FeatureFlags, FeatureFlagsDialogComponent, Feedback, FeedbackDialogComponent, FileSizePipe, FilterButtonComponent, FiltersBarComponent, FloatingToolbarComponent, ForgotPasswordComponent, HIGHLIGHTS, HeatmapComponent, HighlightWordPipe, HistoryPanelComponent, HistoryTitleMarqueeDirective, InfinityScrollDirective, InlineWorker, JsonMethodPluginService, KeyboardShortcutsDialog, KeyboardShortcutsDialogComponent, KeyboardShortcutsListComponent, KeyboardShortcutsService, LabelService, LabelsEditDialog, ListPanelItemDirective, LoadingComponent, MarkdownPipe, MarkdownRendererService, MetadataComponent, MissingTermsComponent, MoreButtonComponent, MoreComponent, MultiSelectLabelsComponent, MultiSelectionToolbarComponent, NON_SEARCHABLE_COLUMNS, NON_SEARCHABLE_DEFAULTS, NavbarTabsComponent, NavigationService, NoResultComponent, OpenArticleOnCtrlEnterDirective, OperatorPipe, OverflowItemDirective, OverflowManagerDirective, OverflowStopDirective, OverrideUser, OverrideUserDialogComponent, PREVIEW_ASSISTANT_COMPONENT, PREVIEW_CONFIG, PagerComponent, PreviewActionsComponent, PreviewContentComponent, PreviewHeaderComponent, PreviewNavigator, PreviewService, PrincipalService, PrincipalStore, QueryParamsStore, QueryService, QuickFiltersComponent, RECENT_SEARCHES_CONFIG, RECENT_SEARCHES_OPTIONS, ROUTE_COMPONENTS, RecentSearchesComponent, ResetUserSettings, ResetUserSettingsDialogComponent, SAVED_SEARCHES_CONFIG, SAVED_SEARCHES_OPTIONS, SEARCH_BOX_CATEGORIES_SORT, SEARCH_BOX_CATEGORY_ICONS, STARTUP_DATA_SOURCE, SavedSearch, SavedSearchDialog, SavedSearchPopover, SavedSearchesComponent, SavedSearchesService, SearchBoxComponent, SearchFeedbackComponent, SearchInputFooter, SearchService, SelectArticleDirective, SelectionService, SelectionStore, Settings, SettingsAccountPanelComponent, SettingsAlertsPanelComponent, SettingsAppearancePanelComponent, SettingsDialogComponent, SettingsShortcutsPanelComponent, ShortcutDirective, ShowBookmarkDirective, SignInComponent, SignedOutComponent, SortSelectorComponent, SourceComponent, SourceIconPipe, SponsoredResultsComponent, SyslangPipe, THEMES, TagCloudComponent, TextChunkService, ThemeProviderDirective, ThemeSelectorComponent, ThemeStore, ThemeToggleComponent, TranslocoDateImpurePipe, UserOverrideService, UserProfileFormComponent, UserProfileService, UserSettingsPersistence, UserSettingsStore, WEIGHT_STEPS, WordCloudComponent, ZoomControlsComponent, __resetSignInMutexForTests, activateShortcutTarget, applyThemeToNativeElement, atomicClientProvider, auditInterceptorFn, authInterceptorFn, bodyInterceptorFn, bootstrapApp, buildHeatmap, buildQuery, buildTagCloud, candidatesFor, canonicalize, debouncedSignal, errorInterceptorFn, fieldErrorKey, fieldShowsError, filterKey, filterLeaves, filterValues, findAggregation, flagsFromWeekdays, focusWhenReady, galactikPrimitive, galactikSemantic, getCurrentPath, getCurrentQueryName, getQueryNameFromRoute, groupSearchesByDate, injectAdvancedFilters, injectAtomicClient, injectRouteNavigation, injectUserSettingsPatch, isAuthRoute, isSignInInFlight, itemLabel, layoutWordCloud, legacyFilterToFilter, makeWeightScale, multiSelectionToolbarVariants, paletteColor, processCssVars, provideAtomicClient, provideAtomicNotifications, provideAtomicScope, provideDocumentLang, provideKeyboardShortcuts, provideSessionExpiry, provideStartupAggregation, quantileScale, queryNameResolver, rebuildWithout, searchItemKey, seriesPalette, signIn, themeColorNameToCssVariable, themeColorsToCssVariables, timelinePalette, toAriaKeyshortcuts, toDisplayParts, toPercentages, toSeries, toastInterceptorFn, weekdaysFromFlags, withAggregationsFeatures, withAlertsFeatures, withAlpha, withAppFeatures, withApplicationFeatures, withAssistantFeatures, withBasketsFeatures, withBookmarkFeatures, withBootstrapApp, withExtractsFeatures, withFavoriteSearchesFeatures, withFetch, withMultiSelectionFeatures, withQueryParamsFeatures, withQuickFiltersFeatures, withRecentSearchesFeatures, withSavedSearchesFeatures, withSelectionFeatures, withThemeBodyHook, withThemes, withThemesFeatures, withUserSettingsFeatures };
|
|
39170
|
+
export { AGGREGATIONS_NAMES, AGGREGATIONS_NAMES_PRESET_DEFAULT, APP_FEATURES, ATOMIC_CLIENT, ATOMIC_SCOPED_PROVIDERS, AddToCollection, AdvancedFiltersBodyComponent, AdvancedFiltersComponent, AdvancedSearch, AdvancedSearchTabs, AggregationComponent, AggregationDateComponent, AggregationDateRange, AggregationItemDirective, AggregationListComponent, AggregationPanelComponent, AggregationTreeComponent, AggregationsService, AggregationsStore, Alert, AlertDialog, AlertForm, AlertFormComponent, AlertsComponent, AppService, AppStore, ApplicationService, ApplicationStore, AppliedFiltersComponent, ArticleEntities, ArticleEntitiesTabPanel, ArticleExtracts, ArticleExtractsTabPanel, ArticleHighlights, ArticleLabels, ArticleLabelsTabPanel, ArticleMatchLocations, ArticleMatchLocationsTabPanel, ArticleSimilarDocuments, ArticleSimilarDocumentsTabPanel, 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, ConverterSelectComponent, DeleteCollection, DeleteCollectionDialog, DidYouMeanComponent, DocumentLocatorComponent, EditLabels, ErrorComponent, Export, ExportDialog, ExportService, FILTERS_BREAKPOINT, FILTER_DATE_ALLOW_CUSTOM_RANGE, FORM_ALERT_CLASS, FORM_BARE_CLASS, FORM_ERROR_CLASS, FORM_FIELD_CLASS, FORM_FOOTER_CLASS, FORM_LABEL_CLASS, FORM_SURFACE_CLASS, FaIconComponent, FeatureFlags, FeatureFlagsDialogComponent, Feedback, FeedbackDialogComponent, FileSizePipe, FilterButtonComponent, FiltersBarComponent, FiltersPanelComponent, FloatingToolbarComponent, ForgotPasswordComponent, HIGHLIGHTS, HeatmapComponent, HighlightWordPipe, HistoryPanelComponent, HistoryTitleMarqueeDirective, InfinityScrollDirective, InlineWorker, JsonMethodPluginService, KeyboardShortcutsDialog, KeyboardShortcutsDialogComponent, KeyboardShortcutsListComponent, KeyboardShortcutsService, LabelService, LabelsEditDialog, ListPanelItemDirective, LoadingComponent, LocalizedNumberPipe, MarkdownPipe, MarkdownRendererService, MetadataComponent, MissingTermsComponent, MoreButtonComponent, MoreComponent, MultiSelectLabelsComponent, MultiSelectionToolbarComponent, NON_SEARCHABLE_COLUMNS, NON_SEARCHABLE_DEFAULTS, NavbarTabsComponent, NavigationService, NoResultComponent, OpenArticleOnCtrlEnterDirective, OperatorPipe, OverflowItemDirective, OverflowManagerDirective, OverflowStopDirective, OverrideUser, OverrideUserDialogComponent, PREVIEW_ASSISTANT_COMPONENT, PREVIEW_CONFIG, PagerComponent, PreviewActionsComponent, PreviewContentComponent, PreviewHeaderComponent, PreviewNavigator, PreviewService, PrincipalService, PrincipalStore, QueryParamsStore, QueryService, QuickFiltersComponent, QuickFiltersListComponent, RECENT_SEARCHES_CONFIG, RECENT_SEARCHES_OPTIONS, ROUTE_COMPONENTS, RecentSearchesComponent, ResetUserSettings, ResetUserSettingsDialogComponent, SAVED_SEARCHES_CONFIG, SAVED_SEARCHES_OPTIONS, SEARCH_BOX_CATEGORIES_SORT, SEARCH_BOX_CATEGORY_ICONS, STARTUP_DATA_SOURCE, SavedSearch, SavedSearchDialog, SavedSearchPopover, SavedSearchesComponent, SavedSearchesService, SearchBoxComponent, SearchBoxPopupDirective, SearchFeedbackComponent, SearchInputFooter, SearchService, SelectArticleDirective, SelectionService, SelectionStore, Settings, SettingsAccountPanelComponent, SettingsAlertsPanelComponent, SettingsAppearancePanelComponent, SettingsDialogComponent, SettingsShortcutsPanelComponent, ShortcutDirective, ShowBookmarkDirective, SignInComponent, SignedOutComponent, SortSelectorComponent, SourceComponent, SourceIconPipe, SponsoredResultsComponent, SyslangPipe, THEMES, TagCloudComponent, TextChunkService, ThemeProviderDirective, ThemeSelectorComponent, ThemeStore, ThemeToggleComponent, TranslocoDateImpurePipe, UserOverrideService, UserProfileFormComponent, UserProfileService, UserSettingsPersistence, UserSettingsStore, WEIGHT_STEPS, WordCloudComponent, ZoomControlsComponent, __resetSignInMutexForTests, activateShortcutTarget, applyThemeToNativeElement, atomicClientProvider, auditInterceptorFn, authInterceptorFn, bodyInterceptorFn, bootstrapApp, buildHeatmap, buildQuery, buildTagCloud, candidatesFor, canonicalize, debouncedSignal, errorInterceptorFn, fieldErrorKey, fieldShowsError, filterKey, filterLeaves, filterValues, findAggregation, flagsFromWeekdays, focusWhenReady, galactikPrimitive, galactikSemantic, getCurrentPath, getCurrentQueryName, getQueryNameFromRoute, groupSearchesByDate, injectAdvancedFilters, injectAtomicClient, injectRouteNavigation, injectUserSettingsPatch, isAuthRoute, isSignInInFlight, itemLabel, layoutWordCloud, legacyFilterToFilter, makeWeightScale, multiSelectionToolbarVariants, paletteColor, processCssVars, provideAtomicClient, provideAtomicNotifications, provideAtomicScope, provideDocumentLang, provideKeyboardShortcuts, provideSessionExpiry, provideStartupAggregation, quantileScale, queryNameResolver, rebuildWithout, searchItemKey, seriesPalette, signIn, themeColorNameToCssVariable, themeColorsToCssVariables, timelinePalette, toAriaKeyshortcuts, toDisplayParts, toPercentages, toSeries, toastInterceptorFn, weekdaysFromFlags, withAggregationsFeatures, withAlertsFeatures, withAlpha, withAppFeatures, withApplicationFeatures, withAssistantFeatures, withBasketsFeatures, withBookmarkFeatures, withBootstrapApp, withExtractsFeatures, withFavoriteSearchesFeatures, withFetch, withMultiSelectionFeatures, withQueryParamsFeatures, withQuickFiltersFeatures, withRecentSearchesFeatures, withSavedSearchesFeatures, withSelectionFeatures, withThemeBodyHook, withThemes, withThemesFeatures, withUserSettingsFeatures };
|
|
37787
39171
|
|
|
37788
39172
|
//# sourceMappingURL=sinequa-atomic-angular.mjs.map
|