@sinequa/atomic-angular 1.8.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/sinequa-atomic-angular.mjs +136 -128
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/index.d.ts +265 -502
- package/package.json +1 -1
|
@@ -1854,42 +1854,49 @@ async function withFetch(callback, injector) {
|
|
|
1854
1854
|
}
|
|
1855
1855
|
}
|
|
1856
1856
|
|
|
1857
|
+
/**
|
|
1858
|
+
* Canonical default shape of the query params state.
|
|
1859
|
+
*
|
|
1860
|
+
* Shared between `withState()` (initial value) and `reset()` (replacement state) so the
|
|
1861
|
+
* two cannot drift apart. Keep in sync with `QueryParamsState`.
|
|
1862
|
+
*/
|
|
1863
|
+
const QUERY_PARAMS_DEFAULTS = {
|
|
1864
|
+
// QueryParams specific properties
|
|
1865
|
+
path: "",
|
|
1866
|
+
filters: [],
|
|
1867
|
+
id: "",
|
|
1868
|
+
// Query properties (all optional due to Partial<>)
|
|
1869
|
+
name: "",
|
|
1870
|
+
text: "",
|
|
1871
|
+
action: "",
|
|
1872
|
+
select: [],
|
|
1873
|
+
open: [],
|
|
1874
|
+
page: undefined,
|
|
1875
|
+
pageSize: undefined,
|
|
1876
|
+
tab: "",
|
|
1877
|
+
scope: "",
|
|
1878
|
+
sort: "",
|
|
1879
|
+
basket: "",
|
|
1880
|
+
isFirstPage: undefined,
|
|
1881
|
+
strictRefine: undefined,
|
|
1882
|
+
globalRelevance: undefined,
|
|
1883
|
+
questionLanguage: "",
|
|
1884
|
+
questionDefaultLanguage: "",
|
|
1885
|
+
spellingCorrectionMode: undefined,
|
|
1886
|
+
spellingCorrectionFilter: "",
|
|
1887
|
+
documentWeight: "",
|
|
1888
|
+
textPartWeights: "",
|
|
1889
|
+
relevanceTransforms: "",
|
|
1890
|
+
removeDuplicates: undefined,
|
|
1891
|
+
queryId: "",
|
|
1892
|
+
aggregations: undefined,
|
|
1893
|
+
orderBy: "",
|
|
1894
|
+
groupBy: "",
|
|
1895
|
+
neuralSearch: undefined
|
|
1896
|
+
};
|
|
1857
1897
|
const QueryParamsStore = signalStore({ providedIn: "root" }, withDevtools("QueryParams"), withQueryParamsFeatures());
|
|
1858
1898
|
function withQueryParamsFeatures() {
|
|
1859
|
-
return signalStoreFeature(withState({
|
|
1860
|
-
// QueryParams specific properties
|
|
1861
|
-
path: "",
|
|
1862
|
-
filters: [],
|
|
1863
|
-
id: "",
|
|
1864
|
-
// Query properties (all optional due to Partial<>)
|
|
1865
|
-
name: "",
|
|
1866
|
-
text: "",
|
|
1867
|
-
action: "",
|
|
1868
|
-
select: [],
|
|
1869
|
-
open: [],
|
|
1870
|
-
page: undefined,
|
|
1871
|
-
pageSize: undefined,
|
|
1872
|
-
tab: "",
|
|
1873
|
-
scope: "",
|
|
1874
|
-
sort: "",
|
|
1875
|
-
basket: "",
|
|
1876
|
-
isFirstPage: undefined,
|
|
1877
|
-
strictRefine: undefined,
|
|
1878
|
-
globalRelevance: undefined,
|
|
1879
|
-
questionLanguage: "",
|
|
1880
|
-
questionDefaultLanguage: "",
|
|
1881
|
-
spellingCorrectionMode: undefined,
|
|
1882
|
-
spellingCorrectionFilter: "",
|
|
1883
|
-
documentWeight: "",
|
|
1884
|
-
textPartWeights: "",
|
|
1885
|
-
relevanceTransforms: "",
|
|
1886
|
-
removeDuplicates: undefined,
|
|
1887
|
-
queryId: "",
|
|
1888
|
-
aggregations: undefined,
|
|
1889
|
-
orderBy: "",
|
|
1890
|
-
groupBy: "",
|
|
1891
|
-
neuralSearch: undefined
|
|
1892
|
-
}), withMethods((store, injector = inject(Injector), router = inject(Router), route = inject(ActivatedRoute), appStore = inject(AppStore)) => ({
|
|
1899
|
+
return signalStoreFeature(withState(QUERY_PARAMS_DEFAULTS), withMethods((store, injector = inject(Injector), router = inject(Router), route = inject(ActivatedRoute), appStore = inject(AppStore)) => ({
|
|
1893
1900
|
/**
|
|
1894
1901
|
* Sets the state from the given URL by extracting query parameters and updating the store.
|
|
1895
1902
|
*
|
|
@@ -2127,6 +2134,18 @@ function withQueryParamsFeatures() {
|
|
|
2127
2134
|
return newState;
|
|
2128
2135
|
});
|
|
2129
2136
|
},
|
|
2137
|
+
/**
|
|
2138
|
+
* Resets the query params to their default state.
|
|
2139
|
+
*
|
|
2140
|
+
* Called on every application (re-)initialization (first login, sign-in, and user
|
|
2141
|
+
* override/impersonation) so that a previous user's search text/filters/sort/etc. don't
|
|
2142
|
+
* leak into the next user's session — `QueryParamsStore` is a root-level singleton that
|
|
2143
|
+
* otherwise survives across logins. See ES-32053.
|
|
2144
|
+
*/
|
|
2145
|
+
reset(options) {
|
|
2146
|
+
patchState(store, QUERY_PARAMS_DEFAULTS);
|
|
2147
|
+
this._updateUrlQueryParams(QUERY_PARAMS_DEFAULTS, undefined, options);
|
|
2148
|
+
},
|
|
2130
2149
|
/**
|
|
2131
2150
|
* Retrieves a filter object based on the provided field or name.
|
|
2132
2151
|
*
|
|
@@ -3540,16 +3559,33 @@ class AggregationsService {
|
|
|
3540
3559
|
}
|
|
3541
3560
|
/* aggregations helpers fot the filters components */
|
|
3542
3561
|
getAuthorizedFilters(aggregations, includedFilters = [], excludedFilters = [], homepageOnly = false) {
|
|
3562
|
+
// An explicit list is a deliberate developer choice: it bypasses the `hidden`
|
|
3563
|
+
// admin rule. The rule only applies to the default, config-driven path.
|
|
3564
|
+
const explicit = aggregations !== undefined;
|
|
3543
3565
|
const agg = aggregations || this.appStore.getAuthorizedFilters();
|
|
3544
3566
|
const authorizedFilters = agg
|
|
3545
3567
|
.filter((f) => this.getFilterCriteria()(f)) // filter only the filters present
|
|
3546
3568
|
.filter((f) => !homepageOnly || this.getHomepageFilterCriteria()(f)) // when requested, keep only filters flagged `homepage: true`
|
|
3547
3569
|
.filter((f) => !this.getLeftOnlyFilterCriteria()(f)) // exclude left-only filters (rendered in the left drawer, not the bar)
|
|
3570
|
+
.filter((f) => explicit || !this.getHiddenFilterCriteria()(f)) // exclude admin-hidden filters (default path only)
|
|
3548
3571
|
.filter((f) => !excludedFilters.includes(f.name))
|
|
3549
3572
|
.filter((f) => !includedFilters.length || includedFilters.includes(f.name))
|
|
3550
3573
|
.map((f) => ({ field: f.column, column: f.column, name: f.name })); // field is needed for filters constructions
|
|
3551
3574
|
return authorizedFilters;
|
|
3552
3575
|
}
|
|
3576
|
+
/**
|
|
3577
|
+
* Removes the aggregations flagged `hidden: true` in the `filters` custom JSON
|
|
3578
|
+
* configuration (the administrator does not want them displayed).
|
|
3579
|
+
*
|
|
3580
|
+
* Exposed for consumers that build their own aggregation list without going through
|
|
3581
|
+
* {@link getAuthorizedFilters} (e.g. the aside filters drawer).
|
|
3582
|
+
*
|
|
3583
|
+
* @param aggregations - The aggregations to filter.
|
|
3584
|
+
* @returns The aggregations that are not flagged hidden.
|
|
3585
|
+
*/
|
|
3586
|
+
rejectHidden(aggregations) {
|
|
3587
|
+
return aggregations.filter((agg) => !this.getHiddenFilterCriteria()(agg));
|
|
3588
|
+
}
|
|
3553
3589
|
/**
|
|
3554
3590
|
* Determines whether a custom JSON filter refers to the given aggregation.
|
|
3555
3591
|
*
|
|
@@ -3593,6 +3629,10 @@ class AggregationsService {
|
|
|
3593
3629
|
// `position: 'both'` (and an undefined position) stay in the bar.
|
|
3594
3630
|
return (agg) => this.appStore.filters().some((filter) => filter.position === "left" && this.matchesAggregation(filter, agg));
|
|
3595
3631
|
};
|
|
3632
|
+
getHiddenFilterCriteria = () => {
|
|
3633
|
+
// an aggregation is hidden when a matching filter in the custom JSON has `hidden: true`
|
|
3634
|
+
return (agg) => this.appStore.filters().some((filter) => filter.hidden && this.matchesAggregation(filter, agg));
|
|
3635
|
+
};
|
|
3596
3636
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3597
3637
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationsService, providedIn: "root" });
|
|
3598
3638
|
}
|
|
@@ -3721,6 +3761,7 @@ class ApplicationService {
|
|
|
3721
3761
|
userSettingsStore = inject(UserSettingsStore);
|
|
3722
3762
|
principalStore = inject(PrincipalStore);
|
|
3723
3763
|
appStore = inject(AppStore);
|
|
3764
|
+
queryParamsStore = inject(QueryParamsStore);
|
|
3724
3765
|
router = inject(Router);
|
|
3725
3766
|
titleService = inject(Title);
|
|
3726
3767
|
components = inject(ROUTE_COMPONENTS);
|
|
@@ -3771,6 +3812,8 @@ class ApplicationService {
|
|
|
3771
3812
|
* Initializes the application stores.
|
|
3772
3813
|
* - Fetches the application configuration.
|
|
3773
3814
|
* - Sets the fetched application configuration in the app store.
|
|
3815
|
+
* - Resets the query params store (runs on first login as well as on every subsequent
|
|
3816
|
+
* re-initialization, e.g. user override, so no previous user's search state leaks in).
|
|
3774
3817
|
* - Loads the user settings and logs the state of the user settings store.
|
|
3775
3818
|
*/
|
|
3776
3819
|
async initializeStores() {
|
|
@@ -3781,6 +3824,9 @@ class ApplicationService {
|
|
|
3781
3824
|
// Load the principal (user information)
|
|
3782
3825
|
await this.principalStore.initialize();
|
|
3783
3826
|
info("principalStore", getState(this.principalStore));
|
|
3827
|
+
// Reset the query params (search text, filters, sort, ...) so a previous user's
|
|
3828
|
+
// in-memory search state doesn't leak into the next user's session. ES-32053.
|
|
3829
|
+
this.queryParamsStore.reset();
|
|
3784
3830
|
// Load the user settings
|
|
3785
3831
|
await this.userSettingsStore.initialize();
|
|
3786
3832
|
info("userSettingsStore", getState(this.userSettingsStore));
|
|
@@ -14693,7 +14739,7 @@ class AggregationTreeComponent {
|
|
|
14693
14739
|
return false;
|
|
14694
14740
|
}
|
|
14695
14741
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationTreeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14696
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AggregationTreeComponent, isStandalone: true, selector: "AggregationTree, aggregation-tree, aggregationtree", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, showFiltersCount: { classPropertyName: "showFiltersCount", publicName: "showFiltersCount", isSignal: true, isRequired: false, transformFunction: null }, showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, expandedLevel: { classPropertyName: "expandedLevel", publicName: "expandedLevel", isSignal: true, isRequired: false, transformFunction: null }, syncUrl: { classPropertyName: "syncUrl", publicName: "syncUrl", isSignal: true, isRequired: false, transformFunction: null }, controlledQuery: { classPropertyName: "controlledQuery", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, controlledAggregation: { classPropertyName: "controlledAggregation", publicName: "aggregation", isSignal: true, isRequired: false, transformFunction: null }, controlledSelection: { classPropertyName: "controlledSelection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, itemTemplateInput: { classPropertyName: "itemTemplateInput", publicName: "itemTemplate", isSignal: true, isRequired: false, transformFunction: null }, searchText: { classPropertyName: "searchText", publicName: "searchText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", onApply: "onApply", onClear: "onClear", selectionChange: "selectionChange", filtersChange: "filtersChange", searchText: "searchTextChange" }, host: { properties: { "class": "cn(\"block h-[inherit] max-h-[inherit] w-[inherit]\", class())" } }, queries: [{ propertyName: "itemDirective", first: true, predicate: AggregationItemDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "virtualItems", predicate: ["virtualItem"], descendants: true, isSignal: true }, { propertyName: "scrollElement", first: true, predicate: ["scrollElement"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (!aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <triangle-alert-icon class=\"mr-1\" />\r\n The aggregationTree component does not support list aggregations. Please use\r\n the <Aggregation /> component instead.\r\n </div>\r\n}\r\n<aggregation-panel\r\n [id]=\"id()\"\r\n [collapsible]=\"collapsible()\"\r\n [collapsed]=\"collapsed()\"\r\n [isEmpty]=\"isEmpty()\"\r\n [isDate]=\"isDate()\"\r\n [aggregation]=\"aggregation()\"\r\n [showFiltersCount]=\"showFiltersCount()\"\r\n [filtersCount]=\"filtersCount()\"\r\n [hasFilters]=\"hasFilters()\"\r\n [selection]=\"selection()\"\r\n [isAllSelected]=\"isAllSelected()\"\r\n [(searchText)]=\"searchText\"\r\n [itemsLength]=\"items().length\"\r\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\r\n [searchedItemsLength]=\"searchedItems().length\"\r\n (cleared)=\"clear()\"\r\n (applied)=\"apply()\"\r\n (allSelected)=\"selectAll()\"\r\n (allUnselected)=\"unselectAll()\"\r\n (loadedMore)=\"loadMore()\">\r\n <div\r\n #scrollElement\r\n class=\"max-h-(--scroll-height,20rem) w-full scrollbar-thin overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"tree\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n <div\r\n class=\"absolute top-0 left-0 w-full\"\r\n [style.transform]=\"\r\n 'translateY(' +\r\n (virtualizer.getVirtualItems()[0]\r\n ? virtualizer.getVirtualItems()[0].start\r\n : 0) +\r\n 'px)'\r\n \">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div #virtualItem [attr.data-index]=\"vItem.index\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"treeItemTpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: item,\r\n path: [],\r\n field: aggregation()?.column,\r\n tpl: treeItemTpl,\r\n parent: null\r\n }\">\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</aggregation-panel>\r\n\r\n<ng-template\r\n #treeItemTpl\r\n let-node=\"node\"\r\n let-path=\"path\"\r\n let-field=\"field\"\r\n let-tpl=\"tpl\"\r\n let-parent=\"parent\">\r\n <div\r\n role=\"treeitem\"\r\n [style.--level]=\"treeItemLevel(node)\"\r\n [attr.aria-selected]=\"node.$selected ?? false\"\r\n [attr.aria-expanded]=\"\r\n node.hasChildren !== false ? (node.$opened ?? false) : null\r\n \"\r\n [attr.aria-disabled]=\"node.count === 0 ? 'true' : null\"\r\n [attr.disabled]=\"node.count === 0 ? 'disabled' : null\">\r\n <a\r\n [attr.aria-label]=\"treeItemName(node) | syslang\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n node.count === 0 && 'disabled pointer-events-none',\r\n (node.$selected || node.$selectedVisually) && ''\r\n )\r\n \"\r\n (click)=\"selectTreeItem(node, parent, $event, true)\">\r\n <button\r\n variant=\"none\"\r\n [iconOnly]=\"true\"\r\n (click)=\"toggleTreeNode($event, node)\"\r\n class=\"transition-transform ease-in hover:scale-125\"\r\n aria-label=\"Open\">\r\n <ChevronRight\r\n [class]=\"\r\n cn(\r\n 'size-4 translate-x-1',\r\n node.$opened && 'rotate-90',\r\n !node.hasChildren && 'hidden'\r\n )\r\n \"\r\n width=\"16\"\r\n height=\"16\" />\r\n </button>\r\n @if (showCheckbox()) {\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ node.value }}\"\r\n [attr.disabled]=\"node.count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"node.count === 0\"\r\n (keydown.enter)=\"selectTreeItem(node, parent, $event)\"\r\n [checked]=\"node.$selected || node.$selectedVisually\" />\r\n }\r\n @if (resolvedItemTemplate(); as tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: node,\r\n field: field,\r\n selected: node.$selected || node.$selectedVisually || false,\r\n count: node.count,\r\n name: (treeItemName(node) | syslang) ?? '',\r\n searchText: searchText(),\r\n level: treeItemLevel(node),\r\n expanded: node.$opened ?? false,\r\n hasChildren: node.hasChildren ?? false\r\n }\" />\r\n } @else {\r\n @let icon = node.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"self-center justify-self-center\" />\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isTreeItemFiltered(node, field)\r\n ? 'filters.removeFilter'\r\n : 'filters.addFilter'\r\n ) | transloco) +\r\n ': ' +\r\n (treeItemName(node) | syslang)\r\n : (treeItemName(node) | syslang)\r\n \"\r\n (click)=\"onTreeItemTextClick(node, parent, $event)\">\r\n @for (\r\n chunk of (treeItemName(node) | syslang) ?? \"\"\r\n | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && node.count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n node.count\r\n }}</span>\r\n }\r\n }\r\n </a>\r\n @if (node.hasChildren && node.$opened) {\r\n <div role=\"group\">\r\n @for (child of node.items; track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: child,\r\n path: treeChildrenPath(node, path),\r\n field: field,\r\n tpl: tpl,\r\n parent: node\r\n }\">\r\n </ng-container>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: ["div[role=treeitem]:has(+div[role=treeitem]){margin-bottom:var(--agg-item-gap, 0)}div[role=treeitem]{display:block;-webkit-user-select:none;user-select:none}div[role=treeitem] a{padding-left:calc(var(--agg-tree-indent, .5rem) * var(--level, 0));line-height:var(--agg-item-height, inherit)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon, chevron-right-icon, chevronrighticon", inputs: ["class"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass", "class"] }, { kind: "component", type: TriangleAlertIcon, selector: "triangle-alert-icon, TriangleAlertIcon", inputs: ["class"] }, { kind: "component", type: AggregationPanelComponent, selector: "AggregationPanel, aggregation-panel", inputs: ["id", "collapsible", "collapsed", "isDate", "isEmpty", "aggregation", "showFiltersCount", "filtersCount", "hasFilters", "selection", "isAllSelected", "searchText", "itemsLength", "hasMore", "searchedItemsLength"], outputs: ["searchTextChange", "cleared", "applied", "allSelected", "allUnselected", "loadedMore"] }, { kind: "pipe", type: SyslangPipe, name: "syslang" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: HighlightWordPipe, name: "highlightWord" }] });
|
|
14742
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AggregationTreeComponent, isStandalone: true, selector: "AggregationTree, aggregation-tree, aggregationtree", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, column: { classPropertyName: "column", publicName: "column", isSignal: true, isRequired: true, transformFunction: null }, collapsible: { classPropertyName: "collapsible", publicName: "collapsible", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, showFiltersCount: { classPropertyName: "showFiltersCount", publicName: "showFiltersCount", isSignal: true, isRequired: false, transformFunction: null }, showCheckbox: { classPropertyName: "showCheckbox", publicName: "showCheckbox", isSignal: true, isRequired: false, transformFunction: null }, expandedLevel: { classPropertyName: "expandedLevel", publicName: "expandedLevel", isSignal: true, isRequired: false, transformFunction: null }, syncUrl: { classPropertyName: "syncUrl", publicName: "syncUrl", isSignal: true, isRequired: false, transformFunction: null }, controlledQuery: { classPropertyName: "controlledQuery", publicName: "query", isSignal: true, isRequired: false, transformFunction: null }, controlledAggregation: { classPropertyName: "controlledAggregation", publicName: "aggregation", isSignal: true, isRequired: false, transformFunction: null }, controlledSelection: { classPropertyName: "controlledSelection", publicName: "selection", isSignal: true, isRequired: false, transformFunction: null }, itemTemplateInput: { classPropertyName: "itemTemplateInput", publicName: "itemTemplate", isSignal: true, isRequired: false, transformFunction: null }, searchText: { classPropertyName: "searchText", publicName: "searchText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSelect: "onSelect", onApply: "onApply", onClear: "onClear", selectionChange: "selectionChange", filtersChange: "filtersChange", searchText: "searchTextChange" }, host: { properties: { "class": "cn(\"block h-[inherit] max-h-[inherit] w-[inherit]\", class())" } }, queries: [{ propertyName: "itemDirective", first: true, predicate: AggregationItemDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "virtualItems", predicate: ["virtualItem"], descendants: true, isSignal: true }, { propertyName: "scrollElement", first: true, predicate: ["scrollElement"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (!aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <triangle-alert-icon class=\"mr-1\" />\r\n The aggregationTree component does not support list aggregations. Please use\r\n the <Aggregation /> component instead.\r\n </div>\r\n}\r\n<aggregation-panel\r\n [id]=\"id()\"\r\n [collapsible]=\"collapsible()\"\r\n [collapsed]=\"collapsed()\"\r\n [isEmpty]=\"isEmpty()\"\r\n [isDate]=\"isDate()\"\r\n [aggregation]=\"aggregation()\"\r\n [showFiltersCount]=\"showFiltersCount()\"\r\n [filtersCount]=\"filtersCount()\"\r\n [hasFilters]=\"hasFilters()\"\r\n [selection]=\"selection()\"\r\n [isAllSelected]=\"isAllSelected()\"\r\n [(searchText)]=\"searchText\"\r\n [itemsLength]=\"items().length\"\r\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\r\n [searchedItemsLength]=\"searchedItems().length\"\r\n (cleared)=\"clear()\"\r\n (applied)=\"apply()\"\r\n (allSelected)=\"selectAll()\"\r\n (allUnselected)=\"unselectAll()\"\r\n (loadedMore)=\"loadMore()\">\r\n <div\r\n #scrollElement\r\n class=\"max-h-(--scroll-height,20rem) w-full scrollbar-thin overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"tree\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n <div\r\n class=\"absolute top-0 left-0 w-full\"\r\n [style.transform]=\"\r\n 'translateY(' +\r\n (virtualizer.getVirtualItems()[0]\r\n ? virtualizer.getVirtualItems()[0].start\r\n : 0) +\r\n 'px)'\r\n \">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div #virtualItem [attr.data-index]=\"vItem.index\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"treeItemTpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: item,\r\n path: [],\r\n field: aggregation()?.column,\r\n tpl: treeItemTpl,\r\n parent: null\r\n }\">\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</aggregation-panel>\r\n\r\n<ng-template\r\n #treeItemTpl\r\n let-node=\"node\"\r\n let-path=\"path\"\r\n let-field=\"field\"\r\n let-tpl=\"tpl\"\r\n let-parent=\"parent\">\r\n <div\r\n role=\"treeitem\"\r\n [style.--level]=\"treeItemLevel(node)\"\r\n [attr.aria-selected]=\"node.$selected ?? false\"\r\n [attr.aria-expanded]=\"\r\n node.hasChildren !== false ? (node.$opened ?? false) : null\r\n \"\r\n [attr.aria-disabled]=\"node.count === 0 ? 'true' : null\"\r\n [attr.disabled]=\"node.count === 0 ? 'disabled' : null\">\r\n <a\r\n [attr.aria-label]=\"treeItemName(node) | syslang\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n node.count === 0 && 'disabled pointer-events-none',\r\n (node.$selected || node.$selectedVisually) && ''\r\n )\r\n \"\r\n (click)=\"selectTreeItem(node, parent, $event, true)\">\r\n <button\r\n variant=\"none\"\r\n [iconOnly]=\"true\"\r\n (click)=\"toggleTreeNode($event, node)\"\r\n class=\"transition-transform ease-in hover:scale-125\"\r\n aria-label=\"Open\">\r\n <ChevronRight\r\n [class]=\"\r\n cn(\r\n 'size-4 translate-x-1',\r\n node.$opened && 'rotate-90',\r\n !node.hasChildren && 'hidden'\r\n )\r\n \"\r\n width=\"16\"\r\n height=\"16\" />\r\n </button>\r\n @if (showCheckbox()) {\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ node.value }}\"\r\n [attr.disabled]=\"node.count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"node.count === 0\"\r\n (keydown.enter)=\"selectTreeItem(node, parent, $event, true)\"\r\n [checked]=\"node.$selected || node.$selectedVisually\" />\r\n }\r\n @if (resolvedItemTemplate(); as tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: node,\r\n field: field,\r\n selected: node.$selected || node.$selectedVisually || false,\r\n count: node.count,\r\n name: (treeItemName(node) | syslang) ?? '',\r\n searchText: searchText(),\r\n level: treeItemLevel(node),\r\n expanded: node.$opened ?? false,\r\n hasChildren: node.hasChildren ?? false\r\n }\" />\r\n } @else {\r\n @let icon = node.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"self-center justify-self-center\" />\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isTreeItemFiltered(node, field)\r\n ? 'filters.removeFilter'\r\n : 'filters.addFilter'\r\n ) | transloco) +\r\n ': ' +\r\n (treeItemName(node) | syslang)\r\n : (treeItemName(node) | syslang)\r\n \"\r\n (click)=\"onTreeItemTextClick(node, parent, $event)\">\r\n @for (\r\n chunk of (treeItemName(node) | syslang) ?? \"\"\r\n | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && node.count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n node.count\r\n }}</span>\r\n }\r\n }\r\n </a>\r\n @if (node.hasChildren && node.$opened) {\r\n <div role=\"group\">\r\n @for (child of node.items; track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: child,\r\n path: treeChildrenPath(node, path),\r\n field: field,\r\n tpl: tpl,\r\n parent: node\r\n }\">\r\n </ng-container>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: ["div[role=treeitem]:has(+div[role=treeitem]){margin-bottom:var(--agg-item-gap, 0)}div[role=treeitem]{display:block;-webkit-user-select:none;user-select:none}div[role=treeitem] a{padding-left:calc(var(--agg-tree-indent, .5rem) * var(--level, 0));line-height:var(--agg-item-height, inherit)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: ButtonComponent, selector: "button", inputs: ["class", "variant", "decoration", "scheme", "iconOnly", "size", "solid"] }, { kind: "component", type: ChevronRightIcon, selector: "chevron-right, ChevronRight, chevronright, ChevronRightIcon, chevron-right-icon, chevronrighticon", inputs: ["class"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon, FaIcon", inputs: ["faClass", "class"] }, { kind: "component", type: TriangleAlertIcon, selector: "triangle-alert-icon, TriangleAlertIcon", inputs: ["class"] }, { kind: "component", type: AggregationPanelComponent, selector: "AggregationPanel, aggregation-panel", inputs: ["id", "collapsible", "collapsed", "isDate", "isEmpty", "aggregation", "showFiltersCount", "filtersCount", "hasFilters", "selection", "isAllSelected", "searchText", "itemsLength", "hasMore", "searchedItemsLength"], outputs: ["searchTextChange", "cleared", "applied", "allSelected", "allUnselected", "loadedMore"] }, { kind: "pipe", type: SyslangPipe, name: "syslang" }, { kind: "pipe", type: TranslocoPipe, name: "transloco" }, { kind: "pipe", type: HighlightWordPipe, name: "highlightWord" }] });
|
|
14697
14743
|
}
|
|
14698
14744
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AggregationTreeComponent, decorators: [{
|
|
14699
14745
|
type: Component,
|
|
@@ -14711,7 +14757,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
14711
14757
|
AggregationPanelComponent,
|
|
14712
14758
|
], standalone: true, host: {
|
|
14713
14759
|
"[class]": 'cn("block h-[inherit] max-h-[inherit] w-[inherit]", class())',
|
|
14714
|
-
}, template: "@if (!aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <triangle-alert-icon class=\"mr-1\" />\r\n The aggregationTree component does not support list aggregations. Please use\r\n the <Aggregation /> component instead.\r\n </div>\r\n}\r\n<aggregation-panel\r\n [id]=\"id()\"\r\n [collapsible]=\"collapsible()\"\r\n [collapsed]=\"collapsed()\"\r\n [isEmpty]=\"isEmpty()\"\r\n [isDate]=\"isDate()\"\r\n [aggregation]=\"aggregation()\"\r\n [showFiltersCount]=\"showFiltersCount()\"\r\n [filtersCount]=\"filtersCount()\"\r\n [hasFilters]=\"hasFilters()\"\r\n [selection]=\"selection()\"\r\n [isAllSelected]=\"isAllSelected()\"\r\n [(searchText)]=\"searchText\"\r\n [itemsLength]=\"items().length\"\r\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\r\n [searchedItemsLength]=\"searchedItems().length\"\r\n (cleared)=\"clear()\"\r\n (applied)=\"apply()\"\r\n (allSelected)=\"selectAll()\"\r\n (allUnselected)=\"unselectAll()\"\r\n (loadedMore)=\"loadMore()\">\r\n <div\r\n #scrollElement\r\n class=\"max-h-(--scroll-height,20rem) w-full scrollbar-thin overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"tree\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n <div\r\n class=\"absolute top-0 left-0 w-full\"\r\n [style.transform]=\"\r\n 'translateY(' +\r\n (virtualizer.getVirtualItems()[0]\r\n ? virtualizer.getVirtualItems()[0].start\r\n : 0) +\r\n 'px)'\r\n \">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div #virtualItem [attr.data-index]=\"vItem.index\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"treeItemTpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: item,\r\n path: [],\r\n field: aggregation()?.column,\r\n tpl: treeItemTpl,\r\n parent: null\r\n }\">\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</aggregation-panel>\r\n\r\n<ng-template\r\n #treeItemTpl\r\n let-node=\"node\"\r\n let-path=\"path\"\r\n let-field=\"field\"\r\n let-tpl=\"tpl\"\r\n let-parent=\"parent\">\r\n <div\r\n role=\"treeitem\"\r\n [style.--level]=\"treeItemLevel(node)\"\r\n [attr.aria-selected]=\"node.$selected ?? false\"\r\n [attr.aria-expanded]=\"\r\n node.hasChildren !== false ? (node.$opened ?? false) : null\r\n \"\r\n [attr.aria-disabled]=\"node.count === 0 ? 'true' : null\"\r\n [attr.disabled]=\"node.count === 0 ? 'disabled' : null\">\r\n <a\r\n [attr.aria-label]=\"treeItemName(node) | syslang\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n node.count === 0 && 'disabled pointer-events-none',\r\n (node.$selected || node.$selectedVisually) && ''\r\n )\r\n \"\r\n (click)=\"selectTreeItem(node, parent, $event, true)\">\r\n <button\r\n variant=\"none\"\r\n [iconOnly]=\"true\"\r\n (click)=\"toggleTreeNode($event, node)\"\r\n class=\"transition-transform ease-in hover:scale-125\"\r\n aria-label=\"Open\">\r\n <ChevronRight\r\n [class]=\"\r\n cn(\r\n 'size-4 translate-x-1',\r\n node.$opened && 'rotate-90',\r\n !node.hasChildren && 'hidden'\r\n )\r\n \"\r\n width=\"16\"\r\n height=\"16\" />\r\n </button>\r\n @if (showCheckbox()) {\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ node.value }}\"\r\n [attr.disabled]=\"node.count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"node.count === 0\"\r\n (keydown.enter)=\"selectTreeItem(node, parent, $event)\"\r\n [checked]=\"node.$selected || node.$selectedVisually\" />\r\n }\r\n @if (resolvedItemTemplate(); as tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: node,\r\n field: field,\r\n selected: node.$selected || node.$selectedVisually || false,\r\n count: node.count,\r\n name: (treeItemName(node) | syslang) ?? '',\r\n searchText: searchText(),\r\n level: treeItemLevel(node),\r\n expanded: node.$opened ?? false,\r\n hasChildren: node.hasChildren ?? false\r\n }\" />\r\n } @else {\r\n @let icon = node.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"self-center justify-self-center\" />\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isTreeItemFiltered(node, field)\r\n ? 'filters.removeFilter'\r\n : 'filters.addFilter'\r\n ) | transloco) +\r\n ': ' +\r\n (treeItemName(node) | syslang)\r\n : (treeItemName(node) | syslang)\r\n \"\r\n (click)=\"onTreeItemTextClick(node, parent, $event)\">\r\n @for (\r\n chunk of (treeItemName(node) | syslang) ?? \"\"\r\n | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && node.count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n node.count\r\n }}</span>\r\n }\r\n }\r\n </a>\r\n @if (node.hasChildren && node.$opened) {\r\n <div role=\"group\">\r\n @for (child of node.items; track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: child,\r\n path: treeChildrenPath(node, path),\r\n field: field,\r\n tpl: tpl,\r\n parent: node\r\n }\">\r\n </ng-container>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: ["div[role=treeitem]:has(+div[role=treeitem]){margin-bottom:var(--agg-item-gap, 0)}div[role=treeitem]{display:block;-webkit-user-select:none;user-select:none}div[role=treeitem] a{padding-left:calc(var(--agg-tree-indent, .5rem) * var(--level, 0));line-height:var(--agg-item-height, inherit)}\n"] }]
|
|
14760
|
+
}, template: "@if (!aggregation()?.isTree) {\r\n <div class=\"p-2 text-sm text-red-500\">\r\n <triangle-alert-icon class=\"mr-1\" />\r\n The aggregationTree component does not support list aggregations. Please use\r\n the <Aggregation /> component instead.\r\n </div>\r\n}\r\n<aggregation-panel\r\n [id]=\"id()\"\r\n [collapsible]=\"collapsible()\"\r\n [collapsed]=\"collapsed()\"\r\n [isEmpty]=\"isEmpty()\"\r\n [isDate]=\"isDate()\"\r\n [aggregation]=\"aggregation()\"\r\n [showFiltersCount]=\"showFiltersCount()\"\r\n [filtersCount]=\"filtersCount()\"\r\n [hasFilters]=\"hasFilters()\"\r\n [selection]=\"selection()\"\r\n [isAllSelected]=\"isAllSelected()\"\r\n [(searchText)]=\"searchText\"\r\n [itemsLength]=\"items().length\"\r\n [hasMore]=\"aggregation()?.$hasMore ?? false\"\r\n [searchedItemsLength]=\"searchedItems().length\"\r\n (cleared)=\"clear()\"\r\n (applied)=\"apply()\"\r\n (allSelected)=\"selectAll()\"\r\n (allUnselected)=\"unselectAll()\"\r\n (loadedMore)=\"loadMore()\">\r\n <div\r\n #scrollElement\r\n class=\"max-h-(--scroll-height,20rem) w-full scrollbar-thin overflow-auto\">\r\n <div\r\n class=\"relative w-full\"\r\n [style.height]=\"virtualizer.getTotalSize() + 'px'\"\r\n role=\"tree\"\r\n [attr.aria-label]=\"aggregation()?.display | syslang | transloco\">\r\n <div\r\n class=\"absolute top-0 left-0 w-full\"\r\n [style.transform]=\"\r\n 'translateY(' +\r\n (virtualizer.getVirtualItems()[0]\r\n ? virtualizer.getVirtualItems()[0].start\r\n : 0) +\r\n 'px)'\r\n \">\r\n @for (vItem of virtualizer.getVirtualItems(); track vItem.index) {\r\n @let item = items()[vItem.index];\r\n <div #virtualItem [attr.data-index]=\"vItem.index\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"treeItemTpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: item,\r\n path: [],\r\n field: aggregation()?.column,\r\n tpl: treeItemTpl,\r\n parent: null\r\n }\">\r\n </ng-container>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n </div>\r\n</aggregation-panel>\r\n\r\n<ng-template\r\n #treeItemTpl\r\n let-node=\"node\"\r\n let-path=\"path\"\r\n let-field=\"field\"\r\n let-tpl=\"tpl\"\r\n let-parent=\"parent\">\r\n <div\r\n role=\"treeitem\"\r\n [style.--level]=\"treeItemLevel(node)\"\r\n [attr.aria-selected]=\"node.$selected ?? false\"\r\n [attr.aria-expanded]=\"\r\n node.hasChildren !== false ? (node.$opened ?? false) : null\r\n \"\r\n [attr.aria-disabled]=\"node.count === 0 ? 'true' : null\"\r\n [attr.disabled]=\"node.count === 0 ? 'disabled' : null\">\r\n <a\r\n [attr.aria-label]=\"treeItemName(node) | syslang\"\r\n [class]=\"\r\n cn(\r\n 'flex grow items-center gap-2 p-1 leading-7',\r\n node.count === 0 && 'disabled pointer-events-none',\r\n (node.$selected || node.$selectedVisually) && ''\r\n )\r\n \"\r\n (click)=\"selectTreeItem(node, parent, $event, true)\">\r\n <button\r\n variant=\"none\"\r\n [iconOnly]=\"true\"\r\n (click)=\"toggleTreeNode($event, node)\"\r\n class=\"transition-transform ease-in hover:scale-125\"\r\n aria-label=\"Open\">\r\n <ChevronRight\r\n [class]=\"\r\n cn(\r\n 'size-4 translate-x-1',\r\n node.$opened && 'rotate-90',\r\n !node.hasChildren && 'hidden'\r\n )\r\n \"\r\n width=\"16\"\r\n height=\"16\" />\r\n </button>\r\n @if (showCheckbox()) {\r\n <input\r\n type=\"checkbox\"\r\n role=\"checkbox\"\r\n value=\"{{ node.value }}\"\r\n [attr.disabled]=\"node.count === 0 ? true : null\"\r\n [attr.aria-disabled]=\"node.count === 0\"\r\n (keydown.enter)=\"selectTreeItem(node, parent, $event, true)\"\r\n [checked]=\"node.$selected || node.$selectedVisually\" />\r\n }\r\n @if (resolvedItemTemplate(); as tpl) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n $implicit: node,\r\n field: field,\r\n selected: node.$selected || node.$selectedVisually || false,\r\n count: node.count,\r\n name: (treeItemName(node) | syslang) ?? '',\r\n searchText: searchText(),\r\n level: treeItemLevel(node),\r\n expanded: node.$opened ?? false,\r\n hasChildren: node.hasChildren ?? false\r\n }\" />\r\n } @else {\r\n @let icon = node.icon;\r\n @if (icon) {\r\n <FaIcon [faClass]=\"icon\" class=\"self-center justify-self-center\" />\r\n }\r\n <span\r\n [class]=\"\r\n cn(\r\n 'line-clamp-1 break-all text-ellipsis',\r\n quickFilter() && 'hover:underline'\r\n )\r\n \"\r\n [title]=\"\r\n quickFilter()\r\n ? ((isTreeItemFiltered(node, field)\r\n ? 'filters.removeFilter'\r\n : 'filters.addFilter'\r\n ) | transloco) +\r\n ': ' +\r\n (treeItemName(node) | syslang)\r\n : (treeItemName(node) | syslang)\r\n \"\r\n (click)=\"onTreeItemTextClick(node, parent, $event)\">\r\n @for (\r\n chunk of (treeItemName(node) | syslang) ?? \"\"\r\n | highlightWord: searchText() : 10;\r\n track $index\r\n ) {\r\n <span [class]=\"{ 'font-bold': chunk.match }\" aria-hidden=\"true\">{{\r\n chunk.text\r\n }}</span>\r\n }\r\n </span>\r\n @if (showCount() && node.count > 0) {\r\n <span class=\"ml-auto px-1 text-xs empty:hidden\" aria-hidden=\"true\">{{\r\n node.count\r\n }}</span>\r\n }\r\n }\r\n </a>\r\n @if (node.hasChildren && node.$opened) {\r\n <div role=\"group\">\r\n @for (child of node.items; track $index) {\r\n <ng-container\r\n [ngTemplateOutlet]=\"tpl\"\r\n [ngTemplateOutletContext]=\"{\r\n node: child,\r\n path: treeChildrenPath(node, path),\r\n field: field,\r\n tpl: tpl,\r\n parent: node\r\n }\">\r\n </ng-container>\r\n }\r\n </div>\r\n }\r\n </div>\r\n</ng-template>\r\n", styles: ["div[role=treeitem]:has(+div[role=treeitem]){margin-bottom:var(--agg-item-gap, 0)}div[role=treeitem]{display:block;-webkit-user-select:none;user-select:none}div[role=treeitem] a{padding-left:calc(var(--agg-tree-indent, .5rem) * var(--level, 0));line-height:var(--agg-item-height, inherit)}\n"] }]
|
|
14715
14761
|
}], ctorParameters: () => [], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], column: [{ type: i0.Input, args: [{ isSignal: true, alias: "column", required: true }] }], collapsible: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsible", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], showFiltersCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "showFiltersCount", required: false }] }], showCheckbox: [{ type: i0.Input, args: [{ isSignal: true, alias: "showCheckbox", required: false }] }], expandedLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedLevel", required: false }] }], syncUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "syncUrl", required: false }] }], controlledQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "query", required: false }] }], controlledAggregation: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregation", required: false }] }], controlledSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "selection", required: false }] }], onSelect: [{ type: i0.Output, args: ["onSelect"] }], onApply: [{ type: i0.Output, args: ["onApply"] }], onClear: [{ type: i0.Output, args: ["onClear"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], filtersChange: [{ type: i0.Output, args: ["filtersChange"] }], virtualItems: [{ type: i0.ViewChildren, args: ["virtualItem", { isSignal: true }] }], scrollElement: [{ type: i0.ViewChild, args: ["scrollElement", { isSignal: true }] }], itemDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AggregationItemDirective), { isSignal: true }] }], itemTemplateInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemTemplate", required: false }] }], searchText: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchText", required: false }] }, { type: i0.Output, args: ["searchTextChange"] }] } });
|
|
14716
14762
|
|
|
14717
14763
|
class AggregationListComponent {
|
|
@@ -15392,28 +15438,55 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
15392
15438
|
class AsideFiltersComponent {
|
|
15393
15439
|
cn = cn;
|
|
15394
15440
|
class = input(...(ngDevMode ? [undefined, { debugName: "class" }] : []));
|
|
15395
|
-
|
|
15441
|
+
/**
|
|
15442
|
+
* Explicit, developer-defined list of aggregations to display.
|
|
15443
|
+
*
|
|
15444
|
+
* Each entry is an aggregation **name** (preferred) or **column** — e.g.
|
|
15445
|
+
* `['Authors', 'Sources', 'Geo']`. Although typed as `string[]`, this is the list of
|
|
15446
|
+
* aggregations to render, not a list of raw column keys.
|
|
15447
|
+
*
|
|
15448
|
+
* When provided, it fully bypasses the JSON `filters` configuration (neither
|
|
15449
|
+
* `position`, `getAuthorized`, nor the admin `hidden` rule are applied): the listed
|
|
15450
|
+
* aggregations are rendered as-is, in the given order, provided they exist in the
|
|
15451
|
+
* {@link AggregationsStore}. When empty (default), the component falls back to the
|
|
15452
|
+
* JSON-driven behavior (`position: 'left' | 'both'`, excluding `hidden` aggregations).
|
|
15453
|
+
*
|
|
15454
|
+
* Resolution tries by `name` first — the canonical, unambiguous identifier, since a
|
|
15455
|
+
* `column` can be shared by several aggregations with distinct names — then falls back
|
|
15456
|
+
* to the first aggregation matching the given column. Prefer passing names.
|
|
15457
|
+
*/
|
|
15458
|
+
aggregations = input([], ...(ngDevMode ? [{ debugName: "aggregations" }] : []));
|
|
15459
|
+
/**
|
|
15460
|
+
* When `true` (default), every section shares the same `id`, producing a native
|
|
15461
|
+
* `<details name>` accordion where a single section is open at a time. When `false`,
|
|
15462
|
+
* each section gets a unique id (based on the aggregation `name`) and expands
|
|
15463
|
+
* independently — closer to a typical SBA v2 "Facets List".
|
|
15464
|
+
*/
|
|
15465
|
+
exclusive = input(true, ...(ngDevMode ? [{ debugName: "exclusive", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
15396
15466
|
appStore = inject(AppStore);
|
|
15467
|
+
aggregationsStore = inject(AggregationsStore);
|
|
15468
|
+
aggregationsService = inject(AggregationsService);
|
|
15397
15469
|
asideFilters = computed(() => {
|
|
15398
|
-
|
|
15470
|
+
const explicit = this.aggregations();
|
|
15471
|
+
// Explicit developer-defined list → render exactly as passed, bypassing the JSON config.
|
|
15472
|
+
if (explicit.length > 0) {
|
|
15473
|
+
return explicit
|
|
15474
|
+
.map((key) => this.aggregationsStore.getAggregation(key, "name") ?? this.aggregationsStore.getAggregation(key, "column"))
|
|
15475
|
+
.filter((agg) => !!agg)
|
|
15476
|
+
.map((agg) => ({ name: agg.name, column: agg.column }));
|
|
15477
|
+
}
|
|
15478
|
+
// Default JSON-driven behavior:
|
|
15479
|
+
// `left` filters live only in the drawer, `both` filters live in the drawer and the bar.
|
|
15480
|
+
// Admin-hidden aggregations are excluded (the rule lives in the service).
|
|
15399
15481
|
const asideFilters = this.appStore.filters().filter((f) => f.position === "left" || f.position === "both");
|
|
15400
|
-
return this.appStore.getAuthorized(asideFilters);
|
|
15482
|
+
return this.aggregationsService.rejectHidden(this.appStore.getAuthorized(asideFilters));
|
|
15401
15483
|
}, ...(ngDevMode ? [{ debugName: "asideFilters" }] : []));
|
|
15402
|
-
/**
|
|
15403
|
-
* Checks if the specified column is a date filter.
|
|
15404
|
-
*
|
|
15405
|
-
* @param column The column name to check.
|
|
15406
|
-
* @returns True if the column is a date filter, false otherwise.
|
|
15407
|
-
*/
|
|
15408
|
-
isDate(column) {
|
|
15409
|
-
return this.appStore.isDateColumn(column);
|
|
15410
|
-
}
|
|
15411
15484
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: AsideFiltersComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
15412
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AsideFiltersComponent, isStandalone: true, selector: "aside-filters, AsideFilters, asidefilters", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null },
|
|
15485
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: AsideFiltersComponent, isStandalone: true, selector: "aside-filters, AsideFilters, asidefilters", inputs: { class: { classPropertyName: "class", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, aggregations: { classPropertyName: "aggregations", publicName: "aggregations", isSignal: true, isRequired: false, transformFunction: null }, exclusive: { classPropertyName: "exclusive", publicName: "exclusive", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "cn('flex flex-col gap-2 overflow-auto', class())" } }, ngImport: i0, template: `
|
|
15413
15486
|
<div class="flex flex-col gap-2">
|
|
15414
15487
|
@for (agg of asideFilters(); track agg.name) {
|
|
15415
15488
|
<Aggregation
|
|
15416
|
-
id="aside-filters"
|
|
15489
|
+
[id]="exclusive() ? 'aside-filters' : 'aside-filters-' + agg.name"
|
|
15417
15490
|
[name]="agg.name"
|
|
15418
15491
|
[column]="agg.column"
|
|
15419
15492
|
[collapsible]="true"
|
|
@@ -15432,7 +15505,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
15432
15505
|
<div class="flex flex-col gap-2">
|
|
15433
15506
|
@for (agg of asideFilters(); track agg.name) {
|
|
15434
15507
|
<Aggregation
|
|
15435
|
-
id="aside-filters"
|
|
15508
|
+
[id]="exclusive() ? 'aside-filters' : 'aside-filters-' + agg.name"
|
|
15436
15509
|
[name]="agg.name"
|
|
15437
15510
|
[column]="agg.column"
|
|
15438
15511
|
[collapsible]="true"
|
|
@@ -15445,7 +15518,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
15445
15518
|
"[class]": "cn('flex flex-col gap-2 overflow-auto', class())"
|
|
15446
15519
|
}
|
|
15447
15520
|
}]
|
|
15448
|
-
}], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }],
|
|
15521
|
+
}], propDecorators: { class: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], aggregations: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregations", required: false }] }], exclusive: [{ type: i0.Input, args: [{ isSignal: true, alias: "exclusive", required: false }] }] } });
|
|
15449
15522
|
|
|
15450
15523
|
class FilterButtonComponent {
|
|
15451
15524
|
name = input(...(ngDevMode ? [undefined, { debugName: "name" }] : []));
|
|
@@ -15656,92 +15729,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
15656
15729
|
const FILTERS_BREAKPOINT = new InjectionToken('FILTERS_BREAKPOINT', { factory: () => 5 });
|
|
15657
15730
|
|
|
15658
15731
|
class MoreComponent {
|
|
15659
|
-
cn = cn;
|
|
15660
15732
|
count = input(2, ...(ngDevMode ? [{ debugName: "count" }] : []));
|
|
15661
15733
|
includedFilters = input([], ...(ngDevMode ? [{ debugName: "includedFilters" }] : []));
|
|
15662
15734
|
excludedFilters = input([], ...(ngDevMode ? [{ debugName: "excludedFilters" }] : []));
|
|
15663
15735
|
aggregations = input(...(ngDevMode ? [undefined, { debugName: "aggregations" }] : []));
|
|
15664
15736
|
homepage = input(false, ...(ngDevMode ? [{ debugName: "homepage", transform: booleanAttribute }] : [{ transform: booleanAttribute }]));
|
|
15665
|
-
appStore = inject(AppStore);
|
|
15666
|
-
aggregationsStore = inject(AggregationsStore);
|
|
15667
|
-
queryParamsStore = inject(QueryParamsStore);
|
|
15668
15737
|
aggregationsService = inject(AggregationsService);
|
|
15669
15738
|
/**
|
|
15670
|
-
*
|
|
15739
|
+
* The overflow filters rendered as accordions: the authorized filters beyond the
|
|
15740
|
+
* first `count` (which are already shown in the bar).
|
|
15671
15741
|
*
|
|
15672
15742
|
* @remarks
|
|
15673
|
-
*
|
|
15674
|
-
*
|
|
15675
|
-
*
|
|
15676
|
-
*/
|
|
15677
|
-
filters = signal([], ...(ngDevMode ? [{ debugName: "filters" }] : []));
|
|
15678
|
-
/**
|
|
15679
|
-
* Computed property that returns an array of filters that are currently visible.
|
|
15680
|
-
* Filters out any filters where the `hidden` property is set to true.
|
|
15681
|
-
*
|
|
15682
|
-
* @returns An array of filters that are not hidden
|
|
15683
|
-
*/
|
|
15684
|
-
visibleFilters = computed(() => {
|
|
15685
|
-
const filters = this.filters().filter((f) => !f.hidden);
|
|
15686
|
-
return filters;
|
|
15687
|
-
}, ...(ngDevMode ? [{ debugName: "visibleFilters" }] : []));
|
|
15688
|
-
constructor() {
|
|
15689
|
-
effect(() => {
|
|
15690
|
-
const count = this.count();
|
|
15691
|
-
const authorizedFilters = this.aggregationsService
|
|
15692
|
-
.getAuthorizedFilters(this.aggregations(), this.includedFilters(), this.excludedFilters(), this.homepage())
|
|
15693
|
-
.toSpliced(0, count);
|
|
15694
|
-
const f = authorizedFilters.map((agg) => {
|
|
15695
|
-
const { icon = "far fa-list", hidden = false } = this.appStore.getAggregationCustomization(agg.column, agg.name) || {};
|
|
15696
|
-
return {
|
|
15697
|
-
name: agg.name,
|
|
15698
|
-
column: agg.column,
|
|
15699
|
-
icon,
|
|
15700
|
-
count: 0,
|
|
15701
|
-
isTree: false,
|
|
15702
|
-
disabled: false,
|
|
15703
|
-
hidden
|
|
15704
|
-
};
|
|
15705
|
-
});
|
|
15706
|
-
this.filters.set(f);
|
|
15707
|
-
this.updateFilters();
|
|
15708
|
-
});
|
|
15709
|
-
}
|
|
15710
|
-
clearFilter(field) {
|
|
15711
|
-
this.queryParamsStore.removeFilter(field);
|
|
15712
|
-
}
|
|
15713
|
-
updateFilters() {
|
|
15714
|
-
this.filters.update((filters) => {
|
|
15715
|
-
return filters.map((filter) => {
|
|
15716
|
-
const agg = this.aggregationsStore.getAggregation(filter.name);
|
|
15717
|
-
const f = this.queryParamsStore.getFilter({ name: agg?.name, field: agg?.column });
|
|
15718
|
-
const count = f?.count || 0;
|
|
15719
|
-
const { display = agg?.isTree ? undefined : filter.display } = this.appStore.getAggregationCustomization(filter.column) || {};
|
|
15720
|
-
return {
|
|
15721
|
-
...filter,
|
|
15722
|
-
name: agg?.name || filter.name,
|
|
15723
|
-
display,
|
|
15724
|
-
isTree: agg?.isTree || false,
|
|
15725
|
-
count,
|
|
15726
|
-
disabled: !!(!agg?.items || agg.items?.length === 0) // Disable if no items
|
|
15727
|
-
};
|
|
15728
|
-
});
|
|
15729
|
-
});
|
|
15730
|
-
}
|
|
15731
|
-
/**
|
|
15732
|
-
* Checks whether there are active filters for the current aggregation column.
|
|
15733
|
-
*
|
|
15734
|
-
* if True, the clear button is shown.
|
|
15735
|
-
*
|
|
15736
|
-
* @returns {boolean} `true` if the filter count for the aggregation column is greater than 0, otherwise `false`.
|
|
15743
|
+
* Pure display wiring — authorization (and the admin `hidden` rule) is handled by
|
|
15744
|
+
* {@link AggregationsService.getAuthorizedFilters}; `toSpliced` just drops the
|
|
15745
|
+
* filters already shown in the bar.
|
|
15737
15746
|
*/
|
|
15738
|
-
|
|
15739
|
-
|
|
15740
|
-
|
|
15741
|
-
}
|
|
15747
|
+
filters = computed(() => this.aggregationsService
|
|
15748
|
+
.getAuthorizedFilters(this.aggregations(), this.includedFilters(), this.excludedFilters(), this.homepage())
|
|
15749
|
+
.toSpliced(0, this.count()), ...(ngDevMode ? [{ debugName: "filters" }] : []));
|
|
15742
15750
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: MoreComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
15743
15751
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.25", type: MoreComponent, isStandalone: true, selector: "more, More", inputs: { count: { classPropertyName: "count", publicName: "count", isSignal: true, isRequired: false, transformFunction: null }, includedFilters: { classPropertyName: "includedFilters", publicName: "includedFilters", isSignal: true, isRequired: false, transformFunction: null }, excludedFilters: { classPropertyName: "excludedFilters", publicName: "excludedFilters", isSignal: true, isRequired: false, transformFunction: null }, aggregations: { classPropertyName: "aggregations", publicName: "aggregations", isSignal: true, isRequired: false, transformFunction: null }, homepage: { classPropertyName: "homepage", publicName: "homepage", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "divide-y divide-muted-foreground/18" }, ngImport: i0, template: `
|
|
15744
|
-
@for (filter of
|
|
15752
|
+
@for (filter of filters(); track $index) {
|
|
15745
15753
|
<Aggregation
|
|
15746
15754
|
class="w-60 max-w-80 px-1 [--height:15lh]"
|
|
15747
15755
|
id="more-filters"
|
|
@@ -15757,7 +15765,7 @@ class MoreComponent {
|
|
|
15757
15765
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: MoreComponent, decorators: [{
|
|
15758
15766
|
type: Component,
|
|
15759
15767
|
args: [{ selector: "more, More", standalone: true, imports: [AggregationComponent, TranslocoPipe], template: `
|
|
15760
|
-
@for (filter of
|
|
15768
|
+
@for (filter of filters(); track $index) {
|
|
15761
15769
|
<Aggregation
|
|
15762
15770
|
class="w-60 max-w-80 px-1 [--height:15lh]"
|
|
15763
15771
|
id="more-filters"
|
|
@@ -15771,7 +15779,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
|
|
|
15771
15779
|
`, host: {
|
|
15772
15780
|
class: "divide-y divide-muted-foreground/18"
|
|
15773
15781
|
}, styles: [":host{scrollbar-width:none}\n"] }]
|
|
15774
|
-
}],
|
|
15782
|
+
}], propDecorators: { count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], includedFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "includedFilters", required: false }] }], excludedFilters: [{ type: i0.Input, args: [{ isSignal: true, alias: "excludedFilters", required: false }] }], aggregations: [{ type: i0.Input, args: [{ isSignal: true, alias: "aggregations", required: false }] }], homepage: [{ type: i0.Input, args: [{ isSignal: true, alias: "homepage", required: false }] }] } });
|
|
15775
15783
|
|
|
15776
15784
|
class MoreButtonComponent {
|
|
15777
15785
|
appStore = inject(AppStore);
|