@sinequa/atomic-angular 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +299 -139
- package/fesm2022/sinequa-atomic-angular.mjs +92 -18
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/package.json +2 -2
- package/types/sinequa-atomic-angular.d.ts +196 -28
|
@@ -3354,7 +3354,8 @@ const QUERY_PARAMS_DEFAULTS = {
|
|
|
3354
3354
|
aggregations: void 0,
|
|
3355
3355
|
orderBy: "",
|
|
3356
3356
|
groupBy: "",
|
|
3357
|
-
neuralSearch: void 0
|
|
3357
|
+
neuralSearch: void 0,
|
|
3358
|
+
aggregation_overrides: void 0
|
|
3358
3359
|
};
|
|
3359
3360
|
const QueryParamsStore = signalStore({ providedIn: "root" }, withDevtools("QueryParams"), withQueryParamsFeatures());
|
|
3360
3361
|
function withQueryParamsFeatures() {
|
|
@@ -3578,7 +3579,7 @@ function withQueryParamsFeatures() {
|
|
|
3578
3579
|
};
|
|
3579
3580
|
},
|
|
3580
3581
|
getQuery() {
|
|
3581
|
-
const { filters: allFilters = [], sort, tab, basket, text: queryText = "", spellingCorrectionMode } = getState(store);
|
|
3582
|
+
const { filters: allFilters = [], sort, tab, basket, text: queryText = "", spellingCorrectionMode, aggregation_overrides } = getState(store);
|
|
3582
3583
|
let { name } = getState(store);
|
|
3583
3584
|
if (!name) name = (route.snapshot.firstChild?.children[0]?.data)?.["queryName"] || appStore.getDefaultQuery()?.name || "";
|
|
3584
3585
|
let text = queryText;
|
|
@@ -3599,7 +3600,8 @@ function withQueryParamsFeatures() {
|
|
|
3599
3600
|
spellingCorrectionMode,
|
|
3600
3601
|
tab,
|
|
3601
3602
|
text,
|
|
3602
|
-
basket
|
|
3603
|
+
basket,
|
|
3604
|
+
aggregation_overrides
|
|
3603
3605
|
}));
|
|
3604
3606
|
}
|
|
3605
3607
|
})));
|
|
@@ -22849,6 +22851,7 @@ var AggregationDateComponent = class AggregationDateComponent {
|
|
|
22849
22851
|
onClear = output();
|
|
22850
22852
|
selectionChange = output();
|
|
22851
22853
|
filtersChange = output();
|
|
22854
|
+
aggregationOverridesChange = output();
|
|
22852
22855
|
isCollapsed = linkedSignal(() => this.collapsed(), ...ngDevMode ? [{ debugName: "isCollapsed" }] : /* istanbul ignore next */ []);
|
|
22853
22856
|
expanded = computed(() => this.isCollapsed() ? null : "", ...ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []);
|
|
22854
22857
|
searchText = model("", ...ngDevMode ? [{ debugName: "searchText" }] : /* istanbul ignore next */ []);
|
|
@@ -22972,15 +22975,58 @@ var AggregationDateComponent = class AggregationDateComponent {
|
|
|
22972
22975
|
kind: "option",
|
|
22973
22976
|
option: this.optionToItemValue(option) ?? option
|
|
22974
22977
|
} : this.toRangeSelection(filter));
|
|
22978
|
+
this.aggregationOverridesChange.emit(this.overridesWithMask(this.selectedMask()));
|
|
22975
22979
|
this.filtersChange.emit(legacyFilterToFilter(filter));
|
|
22976
22980
|
}
|
|
22981
|
+
selectedMask() {
|
|
22982
|
+
const option = this.dateModel().option;
|
|
22983
|
+
if (!option || option === "custom-range") return void 0;
|
|
22984
|
+
return this.dateOptions().find((o) => o.display === option)?.mask;
|
|
22985
|
+
}
|
|
22986
|
+
currentOverrides() {
|
|
22987
|
+
return this.isControlled() ? this.controlledQuery()?.aggregation_overrides : this.queryParamsStore.aggregation_overrides?.();
|
|
22988
|
+
}
|
|
22989
|
+
overridesWithMask(mask) {
|
|
22990
|
+
const key = this.aggregation()?.name ?? this.name();
|
|
22991
|
+
const overrides = { ...this.currentOverrides() };
|
|
22992
|
+
if (key) {
|
|
22993
|
+
if (mask) overrides[key] = { mask };
|
|
22994
|
+
else delete overrides[key];
|
|
22995
|
+
}
|
|
22996
|
+
return Object.keys(overrides).length > 0 ? overrides : void 0;
|
|
22997
|
+
}
|
|
22998
|
+
storeMask(mask) {
|
|
22999
|
+
const next = this.overridesWithMask(mask);
|
|
23000
|
+
if (JSON.stringify(next) === JSON.stringify(this.currentOverrides())) return;
|
|
23001
|
+
this.queryParamsStore.patch({ aggregation_overrides: next }, void 0, { syncUrl: false });
|
|
23002
|
+
}
|
|
23003
|
+
maskedLabel(option) {
|
|
23004
|
+
if (!option.mask || !option.value || option.display !== option.value) return void 0;
|
|
23005
|
+
const [year, month] = option.value.split("-").map(Number);
|
|
23006
|
+
const format = option.mask === "YYYY" ? { year: "numeric" } : {
|
|
23007
|
+
year: "numeric",
|
|
23008
|
+
month: "long"
|
|
23009
|
+
};
|
|
23010
|
+
try {
|
|
23011
|
+
return new Intl.DateTimeFormat(this.lang(), {
|
|
23012
|
+
...format,
|
|
23013
|
+
timeZone: "UTC"
|
|
23014
|
+
}).format(new Date(Date.UTC(year, (month ?? 1) - 1, 1)));
|
|
23015
|
+
} catch {
|
|
23016
|
+
return;
|
|
23017
|
+
}
|
|
23018
|
+
}
|
|
22977
23019
|
select() {
|
|
22978
23020
|
this.selection.set(true);
|
|
22979
23021
|
}
|
|
22980
23022
|
apply() {
|
|
22981
23023
|
try {
|
|
22982
23024
|
if (this.isControlled()) this.emitCurrentSelection();
|
|
22983
|
-
else
|
|
23025
|
+
else {
|
|
23026
|
+
const filter = this.getFormValueFilter();
|
|
23027
|
+
this.storeMask(this.selectedMask());
|
|
23028
|
+
this.queryParamsStore.updateFilter(filter, void 0, { syncUrl: this.syncUrl() });
|
|
23029
|
+
}
|
|
22984
23030
|
this.selection.set(false);
|
|
22985
23031
|
this.onApply.emit();
|
|
22986
23032
|
} catch (err) {
|
|
@@ -23000,8 +23046,12 @@ var AggregationDateComponent = class AggregationDateComponent {
|
|
|
23000
23046
|
if (agg && notify) {
|
|
23001
23047
|
if (this.isControlled()) {
|
|
23002
23048
|
this.selectionChange.emit({ kind: "range" });
|
|
23049
|
+
this.aggregationOverridesChange.emit(this.overridesWithMask(void 0));
|
|
23003
23050
|
this.filtersChange.emit(void 0);
|
|
23004
|
-
} else
|
|
23051
|
+
} else {
|
|
23052
|
+
this.storeMask(void 0);
|
|
23053
|
+
this.queryParamsStore.removeFilterByName(agg.name, void 0, { syncUrl: this.syncUrl() });
|
|
23054
|
+
}
|
|
23005
23055
|
this.onClear.emit();
|
|
23006
23056
|
}
|
|
23007
23057
|
}
|
|
@@ -23060,8 +23110,8 @@ var AggregationDateComponent = class AggregationDateComponent {
|
|
|
23060
23110
|
this.clear(false);
|
|
23061
23111
|
return;
|
|
23062
23112
|
}
|
|
23063
|
-
const { operator
|
|
23064
|
-
const code = this.
|
|
23113
|
+
const { operator } = filter;
|
|
23114
|
+
const code = this.findPresetCode(filter) ?? "custom-range";
|
|
23065
23115
|
let from = null, to = null;
|
|
23066
23116
|
if (code === "custom-range") switch (operator) {
|
|
23067
23117
|
case "lte":
|
|
@@ -23074,12 +23124,7 @@ var AggregationDateComponent = class AggregationDateComponent {
|
|
|
23074
23124
|
from = filter.start;
|
|
23075
23125
|
to = filter.end;
|
|
23076
23126
|
break;
|
|
23077
|
-
case "and": {
|
|
23078
|
-
const filters = filter.filters;
|
|
23079
|
-
from = filters?.find((f) => f.operator === "gte")?.value ?? null;
|
|
23080
|
-
to = filters?.find((f) => f.operator === "lte")?.value ?? null;
|
|
23081
|
-
break;
|
|
23082
|
-
}
|
|
23127
|
+
case "and": ({from, to} = this.toBounds(filter.filters));
|
|
23083
23128
|
}
|
|
23084
23129
|
const formValue = {
|
|
23085
23130
|
option: code,
|
|
@@ -23090,6 +23135,29 @@ var AggregationDateComponent = class AggregationDateComponent {
|
|
|
23090
23135
|
};
|
|
23091
23136
|
this.setModel(formValue);
|
|
23092
23137
|
}
|
|
23138
|
+
findPresetCode(filter) {
|
|
23139
|
+
const options = this.dateOptions();
|
|
23140
|
+
const exact = options.find((option) => option.operator === filter.operator && option.value === filter.value);
|
|
23141
|
+
if (exact) return exact.display;
|
|
23142
|
+
if (filter.operator !== "and") return void 0;
|
|
23143
|
+
const { from, to } = this.toBounds(filter.filters);
|
|
23144
|
+
if (!from || !to) return void 0;
|
|
23145
|
+
return options.find((option) => {
|
|
23146
|
+
if (option.operator !== "and") return false;
|
|
23147
|
+
const bounds = this.toBounds(option.filters);
|
|
23148
|
+
return bounds.from === from && bounds.to === to;
|
|
23149
|
+
})?.display;
|
|
23150
|
+
}
|
|
23151
|
+
toBounds(filters) {
|
|
23152
|
+
const bound = (...operators) => {
|
|
23153
|
+
const value = filters?.find((f) => operators.includes(f.operator))?.value;
|
|
23154
|
+
return value === void 0 || value === null ? null : String(value);
|
|
23155
|
+
};
|
|
23156
|
+
return {
|
|
23157
|
+
from: bound("gte"),
|
|
23158
|
+
to: bound("lte", "lt")
|
|
23159
|
+
};
|
|
23160
|
+
}
|
|
23093
23161
|
updateFormFromSelection(sel) {
|
|
23094
23162
|
if (sel?.kind === "option") {
|
|
23095
23163
|
const code = this.itemValueToOption(sel.option);
|
|
@@ -23151,11 +23219,11 @@ var AggregationDateComponent = class AggregationDateComponent {
|
|
|
23151
23219
|
to: filter.end
|
|
23152
23220
|
};
|
|
23153
23221
|
case "and": {
|
|
23154
|
-
const
|
|
23222
|
+
const { from, to } = this.toBounds(filter.filters);
|
|
23155
23223
|
return {
|
|
23156
23224
|
kind: "range",
|
|
23157
|
-
from:
|
|
23158
|
-
to:
|
|
23225
|
+
from: from ?? void 0,
|
|
23226
|
+
to: to ?? void 0
|
|
23159
23227
|
};
|
|
23160
23228
|
}
|
|
23161
23229
|
default: return {
|
|
@@ -23344,12 +23412,13 @@ var AggregationDateComponent = class AggregationDateComponent {
|
|
|
23344
23412
|
onClear: "onClear",
|
|
23345
23413
|
selectionChange: "selectionChange",
|
|
23346
23414
|
filtersChange: "filtersChange",
|
|
23415
|
+
aggregationOverridesChange: "aggregationOverridesChange",
|
|
23347
23416
|
searchText: "searchTextChange"
|
|
23348
23417
|
},
|
|
23349
23418
|
host: { properties: { "class": "cn(\"@container block\", isEmpty() && \"hidden\")" } },
|
|
23350
23419
|
providers: [provideTranslocoScope("filters")],
|
|
23351
23420
|
ngImport: i0,
|
|
23352
|
-
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 buttons (apply, clear, chevron) are the keyboard stops, see the\n chevron below. -->\n<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\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 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 (hasFilters()) {\n @let clearLabel = \"filters.clearFilters\" | transloco;\n <button\n type=\"button\"\n variant=\"tertiary\"\n [iconOnly]=\"true\"\n size=\"sm\"\n class=\"shrink-0\"\n [aria-label]=\"clearLabel\"\n [tooltip]=\"clearLabel\"\n (click)=\"$event.stopPropagation(); clear()\">\n <filter-x-icon class=\"shrink-0\" />\n </button>\n }\n\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n @let label = \"filters.apply\" | transloco;\n <button\n type=\"button\"\n variant=\"accent\"\n size=\"sm\"\n class=\"overflow-hidden\"\n [aria-label]=\"label\"\n [tooltip]=\"label\"\n (click)=\"$event.stopPropagation(); apply()\">\n <FilterIcon class=\"shrink-0\" />\n <span class=\"truncate\">{{ label }}</span>\n </button>\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\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 (keydown.enter)=\"onOptionEnter($event)\">\n <div\n role=\"group\"\n [attr.aria-label]=\"groupName ? (groupName | syslang | transloco) : null\">\n <ul\n class=\"flex max-h-(--scroll-height,20rem) snap-y snap-start scrollbar-thin flex-col gap-0.5 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track
|
|
23421
|
+
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 buttons (apply, clear, chevron) are the keyboard stops, see the\n chevron below. -->\n<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\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 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 (hasFilters()) {\n @let clearLabel = \"filters.clearFilters\" | transloco;\n <button\n type=\"button\"\n variant=\"tertiary\"\n [iconOnly]=\"true\"\n size=\"sm\"\n class=\"shrink-0\"\n [aria-label]=\"clearLabel\"\n [tooltip]=\"clearLabel\"\n (click)=\"$event.stopPropagation(); clear()\">\n <filter-x-icon class=\"shrink-0\" />\n </button>\n }\n\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n @let label = \"filters.apply\" | transloco;\n <button\n type=\"button\"\n variant=\"accent\"\n size=\"sm\"\n class=\"overflow-hidden\"\n [aria-label]=\"label\"\n [tooltip]=\"label\"\n (click)=\"$event.stopPropagation(); apply()\">\n <FilterIcon class=\"shrink-0\" />\n <span class=\"truncate\">{{ label }}</span>\n </button>\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\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 (keydown.enter)=\"onOptionEnter($event)\">\n <div\n role=\"group\"\n [attr.aria-label]=\"groupName ? (groupName | syslang | transloco) : null\">\n <ul\n class=\"flex max-h-(--scroll-height,20rem) 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</details>\n",
|
|
23353
23422
|
styles: [":host{min-width:200px}ul[role=list]{scrollbar-width:thin}\n"],
|
|
23354
23423
|
dependencies: [
|
|
23355
23424
|
{
|
|
@@ -23484,7 +23553,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
23484
23553
|
TooltipDirective
|
|
23485
23554
|
],
|
|
23486
23555
|
host: { "[class]": "cn(\"@container block\", isEmpty() && \"hidden\")" },
|
|
23487
|
-
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 buttons (apply, clear, chevron) are the keyboard stops, see the\n chevron below. -->\n<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\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 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 (hasFilters()) {\n @let clearLabel = \"filters.clearFilters\" | transloco;\n <button\n type=\"button\"\n variant=\"tertiary\"\n [iconOnly]=\"true\"\n size=\"sm\"\n class=\"shrink-0\"\n [aria-label]=\"clearLabel\"\n [tooltip]=\"clearLabel\"\n (click)=\"$event.stopPropagation(); clear()\">\n <filter-x-icon class=\"shrink-0\" />\n </button>\n }\n\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n @let label = \"filters.apply\" | transloco;\n <button\n type=\"button\"\n variant=\"accent\"\n size=\"sm\"\n class=\"overflow-hidden\"\n [aria-label]=\"label\"\n [tooltip]=\"label\"\n (click)=\"$event.stopPropagation(); apply()\">\n <FilterIcon class=\"shrink-0\" />\n <span class=\"truncate\">{{ label }}</span>\n </button>\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\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 (keydown.enter)=\"onOptionEnter($event)\">\n <div\n role=\"group\"\n [attr.aria-label]=\"groupName ? (groupName | syslang | transloco) : null\">\n <ul\n class=\"flex max-h-(--scroll-height,20rem) snap-y snap-start scrollbar-thin flex-col gap-0.5 overflow-auto pt-2\"\n role=\"list\">\n @for (option of dateOptions(); track
|
|
23556
|
+
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 buttons (apply, clear, chevron) are the keyboard stops, see the\n chevron below. -->\n<details [attr.open]=\"expanded()\" [attr.name]=\"id()\" class=\"group space-y-2\">\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 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 (hasFilters()) {\n @let clearLabel = \"filters.clearFilters\" | transloco;\n <button\n type=\"button\"\n variant=\"tertiary\"\n [iconOnly]=\"true\"\n size=\"sm\"\n class=\"shrink-0\"\n [aria-label]=\"clearLabel\"\n [tooltip]=\"clearLabel\"\n (click)=\"$event.stopPropagation(); clear()\">\n <filter-x-icon class=\"shrink-0\" />\n </button>\n }\n\n @if (\n (!isControlled() || emitOn() === \"apply\") &&\n selection() &&\n validSelection()\n ) {\n @let label = \"filters.apply\" | transloco;\n <button\n type=\"button\"\n variant=\"accent\"\n size=\"sm\"\n class=\"overflow-hidden\"\n [aria-label]=\"label\"\n [tooltip]=\"label\"\n (click)=\"$event.stopPropagation(); apply()\">\n <FilterIcon class=\"shrink-0\" />\n <span class=\"truncate\">{{ label }}</span>\n </button>\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\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 (keydown.enter)=\"onOptionEnter($event)\">\n <div\n role=\"group\"\n [attr.aria-label]=\"groupName ? (groupName | syslang | transloco) : null\">\n <ul\n class=\"flex max-h-(--scroll-height,20rem) 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</details>\n",
|
|
23488
23557
|
styles: [":host{min-width:200px}ul[role=list]{scrollbar-width:thin}\n"]
|
|
23489
23558
|
}]
|
|
23490
23559
|
}],
|
|
@@ -23622,6 +23691,10 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
23622
23691
|
type: i0.Output,
|
|
23623
23692
|
args: ["filtersChange"]
|
|
23624
23693
|
}],
|
|
23694
|
+
aggregationOverridesChange: [{
|
|
23695
|
+
type: i0.Output,
|
|
23696
|
+
args: ["aggregationOverridesChange"]
|
|
23697
|
+
}],
|
|
23625
23698
|
searchText: [{
|
|
23626
23699
|
type: i0.Input,
|
|
23627
23700
|
args: [{
|
|
@@ -26474,6 +26547,7 @@ var AggregationComponent = class AggregationComponent {
|
|
|
26474
26547
|
"onClear",
|
|
26475
26548
|
"selectionChange",
|
|
26476
26549
|
"filtersChange",
|
|
26550
|
+
"aggregationOverridesChange",
|
|
26477
26551
|
"searchTextChange"
|
|
26478
26552
|
]
|
|
26479
26553
|
}
|