@sinequa/atomic-angular 1.16.0 → 2.0.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 +23 -18
- package/components/navbar-tabs/i18n/de.json +4 -2
- package/components/navbar-tabs/i18n/en.json +4 -2
- package/components/navbar-tabs/i18n/fr.json +5 -3
- package/components/preview/i18n/de.json +12 -0
- package/components/preview/i18n/en.json +12 -0
- package/components/preview/i18n/fr.json +12 -0
- package/components/sort-selector/i18n/de.json +1 -0
- package/components/sort-selector/i18n/en.json +1 -0
- package/components/sort-selector/i18n/fr.json +1 -0
- package/components/sponsored-results/i18n/de.json +3 -0
- package/components/sponsored-results/i18n/en.json +3 -0
- package/components/sponsored-results/i18n/fr.json +3 -0
- package/document-workspaces/i18n/de.json +6 -2
- package/document-workspaces/i18n/en.json +6 -2
- package/document-workspaces/i18n/fr.json +6 -2
- package/document-workspaces/i18n/ja.json +6 -2
- package/features/{drawer → advanced-search}/i18n/de.json +12 -1
- package/features/{drawer → advanced-search}/i18n/en.json +12 -1
- package/features/{drawer → advanced-search}/i18n/fr.json +13 -2
- package/features/alerts/i18n/de.json +7 -1
- package/features/alerts/i18n/en.json +7 -1
- package/features/alerts/i18n/fr.json +7 -1
- package/features/auth/i18n/de.json +6 -0
- package/features/auth/i18n/en.json +6 -0
- package/features/auth/i18n/fr.json +6 -1
- package/features/collections/i18n/de.json +4 -1
- package/features/collections/i18n/en.json +4 -1
- package/features/collections/i18n/fr.json +4 -1
- package/features/dialogs/i18n/de.json +11 -1
- package/features/dialogs/i18n/en.json +11 -1
- package/features/dialogs/i18n/fr.json +11 -1
- package/features/export/i18n/de.json +5 -1
- package/features/export/i18n/en.json +5 -1
- package/features/export/i18n/fr.json +5 -1
- package/features/feedback/i18n/de.json +2 -1
- package/features/feedback/i18n/en.json +2 -1
- package/features/feedback/i18n/fr.json +2 -1
- package/features/filters/i18n/de.json +10 -3
- package/features/filters/i18n/en.json +10 -3
- package/features/filters/i18n/fr.json +11 -4
- package/features/labels/i18n/de.json +5 -1
- package/features/labels/i18n/en.json +5 -1
- package/features/labels/i18n/fr.json +5 -1
- package/features/list-panel/i18n/de.json +4 -0
- package/features/list-panel/i18n/en.json +4 -0
- package/features/list-panel/i18n/fr.json +4 -0
- package/features/searches/i18n/de.json +2 -1
- package/features/searches/i18n/en.json +2 -1
- package/features/searches/i18n/fr.json +3 -2
- package/features/sheet/i18n/de.json +3 -0
- package/features/sheet/i18n/en.json +3 -0
- package/features/sheet/i18n/fr.json +3 -0
- package/features/user-profile/i18n/de.json +22 -1
- package/features/user-profile/i18n/en.json +22 -1
- package/features/user-profile/i18n/fr.json +22 -1
- package/fesm2022/sinequa-atomic-angular-charts-heatmap.mjs +294 -0
- package/fesm2022/sinequa-atomic-angular-charts-heatmap.mjs.map +1 -0
- package/fesm2022/sinequa-atomic-angular-charts.mjs +969 -0
- package/fesm2022/sinequa-atomic-angular-charts.mjs.map +1 -0
- package/fesm2022/sinequa-atomic-angular-document-workspaces.mjs +11188 -7846
- package/fesm2022/sinequa-atomic-angular-document-workspaces.mjs.map +1 -1
- package/fesm2022/sinequa-atomic-angular-workspace-elements.mjs +85 -152
- package/fesm2022/sinequa-atomic-angular-workspace-elements.mjs.map +1 -1
- package/fesm2022/sinequa-atomic-angular.mjs +30683 -17302
- package/fesm2022/sinequa-atomic-angular.mjs.map +1 -1
- package/package.json +60 -23
- package/styles/calendar.css +88 -0
- package/types/sinequa-atomic-angular-charts-heatmap.d.ts +154 -0
- package/types/sinequa-atomic-angular-charts.d.ts +462 -0
- package/types/sinequa-atomic-angular-document-workspaces.d.ts +2527 -0
- package/types/sinequa-atomic-angular-workspace-elements.d.ts +49 -0
- package/types/sinequa-atomic-angular.d.ts +19918 -0
- package/workspace-elements/styles/lexiq-workspaces.css +7310 -4010
- package/document-workspaces/index.d.ts +0 -2116
- package/elements/index.d.ts +0 -346
- package/fesm2022/sinequa-atomic-angular-elements.mjs +0 -7526
- package/fesm2022/sinequa-atomic-angular-elements.mjs.map +0 -1
- package/index.d.ts +0 -16119
- package/workspace-elements/index.d.ts +0 -37
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { Chart } from "chart.js";
|
|
2
|
+
import { MatrixController, MatrixElement } from "chartjs-chart-matrix";
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import { Component, afterNextRender, computed, input, output, signal } from "@angular/core";
|
|
5
|
+
import { cn } from "cn";
|
|
6
|
+
import { buildHeatmap, galactikPrimitive, quantileScale, withAlpha } from "@sinequa/atomic-angular";
|
|
7
|
+
import { ChartCanvasComponent, createAxisClickPlugin } from "@sinequa/atomic-angular/charts";
|
|
8
|
+
let registered = false;
|
|
9
|
+
function setupMatrixChart() {
|
|
10
|
+
if (registered) return;
|
|
11
|
+
Chart.register(MatrixController, MatrixElement);
|
|
12
|
+
registered = true;
|
|
13
|
+
}
|
|
14
|
+
var AggregationHeatmapComponent = class AggregationHeatmapComponent {
|
|
15
|
+
cn = cn;
|
|
16
|
+
class = input("", ...ngDevMode ? [{ debugName: "class" }] : /* istanbul ignore next */ []);
|
|
17
|
+
aggregation = input(void 0, ...ngDevMode ? [{ debugName: "aggregation" }] : /* istanbul ignore next */ []);
|
|
18
|
+
encoding = input({
|
|
19
|
+
kind: "separator",
|
|
20
|
+
separator: "$@$"
|
|
21
|
+
}, ...ngDevMode ? [{ debugName: "encoding" }] : /* istanbul ignore next */ []);
|
|
22
|
+
maxX = input(20, ...ngDevMode ? [{ debugName: "maxX" }] : /* istanbul ignore next */ []);
|
|
23
|
+
maxY = input(20, ...ngDevMode ? [{ debugName: "maxY" }] : /* istanbul ignore next */ []);
|
|
24
|
+
emptyLabel = input("No data", ...ngDevMode ? [{ debugName: "emptyLabel" }] : /* istanbul ignore next */ []);
|
|
25
|
+
enableAxisSelection = input(true, ...ngDevMode ? [{ debugName: "enableAxisSelection" }] : /* istanbul ignore next */ []);
|
|
26
|
+
cellSelected = output();
|
|
27
|
+
axisSelected = output();
|
|
28
|
+
axisClick = createAxisClickPlugin((hit) => this.selectAxis(hit.axis, hit.label));
|
|
29
|
+
rampColor = signal(galactikPrimitive("blue-500"), ...ngDevMode ? [{ debugName: "rampColor" }] : /* istanbul ignore next */ []);
|
|
30
|
+
constructor() {
|
|
31
|
+
setupMatrixChart();
|
|
32
|
+
afterNextRender(() => this.rampColor.set(galactikPrimitive("blue-500")));
|
|
33
|
+
}
|
|
34
|
+
plugins = computed(() => this.enableAxisSelection() ? [this.axisClick] : [], ...ngDevMode ? [{ debugName: "plugins" }] : /* istanbul ignore next */ []);
|
|
35
|
+
heatmap = computed(() => buildHeatmap(this.aggregation(), {
|
|
36
|
+
encoding: this.encoding(),
|
|
37
|
+
maxX: this.maxX(),
|
|
38
|
+
maxY: this.maxY()
|
|
39
|
+
}), ...ngDevMode ? [{ debugName: "heatmap" }] : /* istanbul ignore next */ []);
|
|
40
|
+
isEmpty = computed(() => this.heatmap().cells.length === 0, ...ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []);
|
|
41
|
+
selectAxis(axis, label) {
|
|
42
|
+
const heatmap = this.heatmap();
|
|
43
|
+
const value = (axis === "x" ? heatmap.xValues : heatmap.yValues).get(label) ?? label;
|
|
44
|
+
this.axisSelected.emit({
|
|
45
|
+
axis,
|
|
46
|
+
label,
|
|
47
|
+
value
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
config = computed(() => {
|
|
51
|
+
const { cells, xLabels, yLabels } = this.heatmap();
|
|
52
|
+
const intensity = quantileScale(cells.map((cell) => cell.v));
|
|
53
|
+
const ramp = this.rampColor();
|
|
54
|
+
return {
|
|
55
|
+
type: "matrix",
|
|
56
|
+
data: { datasets: [{
|
|
57
|
+
data: cells.map((cell) => ({
|
|
58
|
+
x: cell.x,
|
|
59
|
+
y: cell.y,
|
|
60
|
+
v: cell.v
|
|
61
|
+
})),
|
|
62
|
+
backgroundColor: (ctx) => {
|
|
63
|
+
const point = ctx.raw;
|
|
64
|
+
return withAlpha(ramp, .12 + .88 * intensity(point?.v ?? 0));
|
|
65
|
+
},
|
|
66
|
+
borderColor: withAlpha(galactikPrimitive("grey-500"), .25),
|
|
67
|
+
borderWidth: 1,
|
|
68
|
+
width: (ctx) => (ctx.chart.chartArea?.width ?? 0) / Math.max(1, xLabels.length) - 2,
|
|
69
|
+
height: (ctx) => (ctx.chart.chartArea?.height ?? 0) / Math.max(1, yLabels.length) - 2
|
|
70
|
+
}] },
|
|
71
|
+
options: {
|
|
72
|
+
scales: {
|
|
73
|
+
x: {
|
|
74
|
+
type: "category",
|
|
75
|
+
labels: xLabels,
|
|
76
|
+
offset: true,
|
|
77
|
+
grid: { display: false },
|
|
78
|
+
ticks: { autoSkip: false }
|
|
79
|
+
},
|
|
80
|
+
y: {
|
|
81
|
+
type: "category",
|
|
82
|
+
labels: yLabels,
|
|
83
|
+
offset: true,
|
|
84
|
+
grid: { display: false },
|
|
85
|
+
ticks: { autoSkip: false }
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
plugins: {
|
|
89
|
+
legend: { display: false },
|
|
90
|
+
tooltip: { callbacks: {
|
|
91
|
+
title: () => "",
|
|
92
|
+
label: (ctx) => {
|
|
93
|
+
const point = ctx.raw;
|
|
94
|
+
return `${String(point?.x ?? "")} / ${String(point?.y ?? "")}: ${point?.v ?? 0}`;
|
|
95
|
+
}
|
|
96
|
+
} }
|
|
97
|
+
},
|
|
98
|
+
onClick: (_event, elements) => {
|
|
99
|
+
const first = elements[0];
|
|
100
|
+
if (!first) return;
|
|
101
|
+
const cell = cells[first.index];
|
|
102
|
+
if (cell) this.cellSelected.emit({
|
|
103
|
+
x: cell.x,
|
|
104
|
+
y: cell.y,
|
|
105
|
+
xValue: cell.xValue,
|
|
106
|
+
yValue: cell.yValue,
|
|
107
|
+
count: cell.v
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}, ...ngDevMode ? [{ debugName: "config" }] : /* istanbul ignore next */ []);
|
|
113
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
114
|
+
minVersion: "12.0.0",
|
|
115
|
+
version: "22.2.1",
|
|
116
|
+
ngImport: i0,
|
|
117
|
+
type: AggregationHeatmapComponent,
|
|
118
|
+
deps: [],
|
|
119
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
120
|
+
});
|
|
121
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
122
|
+
minVersion: "17.0.0",
|
|
123
|
+
version: "22.2.1",
|
|
124
|
+
type: AggregationHeatmapComponent,
|
|
125
|
+
isStandalone: true,
|
|
126
|
+
selector: "AggregationHeatmap, aggregation-heatmap, aggregationheatmap",
|
|
127
|
+
inputs: {
|
|
128
|
+
class: {
|
|
129
|
+
classPropertyName: "class",
|
|
130
|
+
publicName: "class",
|
|
131
|
+
isSignal: true,
|
|
132
|
+
isRequired: false,
|
|
133
|
+
transformFunction: null
|
|
134
|
+
},
|
|
135
|
+
aggregation: {
|
|
136
|
+
classPropertyName: "aggregation",
|
|
137
|
+
publicName: "aggregation",
|
|
138
|
+
isSignal: true,
|
|
139
|
+
isRequired: false,
|
|
140
|
+
transformFunction: null
|
|
141
|
+
},
|
|
142
|
+
encoding: {
|
|
143
|
+
classPropertyName: "encoding",
|
|
144
|
+
publicName: "encoding",
|
|
145
|
+
isSignal: true,
|
|
146
|
+
isRequired: false,
|
|
147
|
+
transformFunction: null
|
|
148
|
+
},
|
|
149
|
+
maxX: {
|
|
150
|
+
classPropertyName: "maxX",
|
|
151
|
+
publicName: "maxX",
|
|
152
|
+
isSignal: true,
|
|
153
|
+
isRequired: false,
|
|
154
|
+
transformFunction: null
|
|
155
|
+
},
|
|
156
|
+
maxY: {
|
|
157
|
+
classPropertyName: "maxY",
|
|
158
|
+
publicName: "maxY",
|
|
159
|
+
isSignal: true,
|
|
160
|
+
isRequired: false,
|
|
161
|
+
transformFunction: null
|
|
162
|
+
},
|
|
163
|
+
emptyLabel: {
|
|
164
|
+
classPropertyName: "emptyLabel",
|
|
165
|
+
publicName: "emptyLabel",
|
|
166
|
+
isSignal: true,
|
|
167
|
+
isRequired: false,
|
|
168
|
+
transformFunction: null
|
|
169
|
+
},
|
|
170
|
+
enableAxisSelection: {
|
|
171
|
+
classPropertyName: "enableAxisSelection",
|
|
172
|
+
publicName: "enableAxisSelection",
|
|
173
|
+
isSignal: true,
|
|
174
|
+
isRequired: false,
|
|
175
|
+
transformFunction: null
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
outputs: {
|
|
179
|
+
cellSelected: "cellSelected",
|
|
180
|
+
axisSelected: "axisSelected"
|
|
181
|
+
},
|
|
182
|
+
host: { properties: { "class": "cn(\"block h-full w-full\", class())" } },
|
|
183
|
+
ngImport: i0,
|
|
184
|
+
template: `
|
|
185
|
+
@if (isEmpty()) {
|
|
186
|
+
<p class="flex h-full items-center justify-center text-sm text-(--font-neutral-muted)">{{ emptyLabel() }}</p>
|
|
187
|
+
} @else {
|
|
188
|
+
<ChartCanvas [config]="config()" [plugins]="plugins()" />
|
|
189
|
+
}
|
|
190
|
+
`,
|
|
191
|
+
isInline: true,
|
|
192
|
+
dependencies: [{
|
|
193
|
+
kind: "component",
|
|
194
|
+
type: ChartCanvasComponent,
|
|
195
|
+
selector: "ChartCanvas, chart-canvas, chartcanvas",
|
|
196
|
+
inputs: [
|
|
197
|
+
"class",
|
|
198
|
+
"config",
|
|
199
|
+
"plugins"
|
|
200
|
+
]
|
|
201
|
+
}]
|
|
202
|
+
});
|
|
203
|
+
};
|
|
204
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
205
|
+
minVersion: "12.0.0",
|
|
206
|
+
version: "22.2.1",
|
|
207
|
+
ngImport: i0,
|
|
208
|
+
type: AggregationHeatmapComponent,
|
|
209
|
+
decorators: [{
|
|
210
|
+
type: Component,
|
|
211
|
+
args: [{
|
|
212
|
+
selector: "AggregationHeatmap, aggregation-heatmap, aggregationheatmap",
|
|
213
|
+
imports: [ChartCanvasComponent],
|
|
214
|
+
template: `
|
|
215
|
+
@if (isEmpty()) {
|
|
216
|
+
<p class="flex h-full items-center justify-center text-sm text-(--font-neutral-muted)">{{ emptyLabel() }}</p>
|
|
217
|
+
} @else {
|
|
218
|
+
<ChartCanvas [config]="config()" [plugins]="plugins()" />
|
|
219
|
+
}
|
|
220
|
+
`,
|
|
221
|
+
host: { "[class]": "cn(\"block h-full w-full\", class())" }
|
|
222
|
+
}]
|
|
223
|
+
}],
|
|
224
|
+
ctorParameters: () => [],
|
|
225
|
+
propDecorators: {
|
|
226
|
+
class: [{
|
|
227
|
+
type: i0.Input,
|
|
228
|
+
args: [{
|
|
229
|
+
isSignal: true,
|
|
230
|
+
alias: "class",
|
|
231
|
+
required: false
|
|
232
|
+
}]
|
|
233
|
+
}],
|
|
234
|
+
aggregation: [{
|
|
235
|
+
type: i0.Input,
|
|
236
|
+
args: [{
|
|
237
|
+
isSignal: true,
|
|
238
|
+
alias: "aggregation",
|
|
239
|
+
required: false
|
|
240
|
+
}]
|
|
241
|
+
}],
|
|
242
|
+
encoding: [{
|
|
243
|
+
type: i0.Input,
|
|
244
|
+
args: [{
|
|
245
|
+
isSignal: true,
|
|
246
|
+
alias: "encoding",
|
|
247
|
+
required: false
|
|
248
|
+
}]
|
|
249
|
+
}],
|
|
250
|
+
maxX: [{
|
|
251
|
+
type: i0.Input,
|
|
252
|
+
args: [{
|
|
253
|
+
isSignal: true,
|
|
254
|
+
alias: "maxX",
|
|
255
|
+
required: false
|
|
256
|
+
}]
|
|
257
|
+
}],
|
|
258
|
+
maxY: [{
|
|
259
|
+
type: i0.Input,
|
|
260
|
+
args: [{
|
|
261
|
+
isSignal: true,
|
|
262
|
+
alias: "maxY",
|
|
263
|
+
required: false
|
|
264
|
+
}]
|
|
265
|
+
}],
|
|
266
|
+
emptyLabel: [{
|
|
267
|
+
type: i0.Input,
|
|
268
|
+
args: [{
|
|
269
|
+
isSignal: true,
|
|
270
|
+
alias: "emptyLabel",
|
|
271
|
+
required: false
|
|
272
|
+
}]
|
|
273
|
+
}],
|
|
274
|
+
enableAxisSelection: [{
|
|
275
|
+
type: i0.Input,
|
|
276
|
+
args: [{
|
|
277
|
+
isSignal: true,
|
|
278
|
+
alias: "enableAxisSelection",
|
|
279
|
+
required: false
|
|
280
|
+
}]
|
|
281
|
+
}],
|
|
282
|
+
cellSelected: [{
|
|
283
|
+
type: i0.Output,
|
|
284
|
+
args: ["cellSelected"]
|
|
285
|
+
}],
|
|
286
|
+
axisSelected: [{
|
|
287
|
+
type: i0.Output,
|
|
288
|
+
args: ["axisSelected"]
|
|
289
|
+
}]
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
export { AggregationHeatmapComponent, setupMatrixChart };
|
|
293
|
+
|
|
294
|
+
//# sourceMappingURL=sinequa-atomic-angular-charts-heatmap.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sinequa-atomic-angular-charts-heatmap.mjs","names":[],"sources":["../../../projects/atomic-angular/charts/heatmap/matrix-setup.ts","../../../projects/atomic-angular/charts/heatmap/aggregation-heatmap.ts"],"sourcesContent":["import { Chart } from \"chart.js\";\nimport { MatrixController, MatrixElement } from \"chartjs-chart-matrix\";\n\nlet registered = false;\n\n/**\n * Registers the `matrix` controller and element that `<AggregationHeatmap>` draws with.\n *\n * Chart.js has no matrix controller of its own; `chartjs-chart-matrix` adds one. Idempotent on\n * purpose: it is called from the constructor of every `<AggregationHeatmap>`, so it must be safe to\n * call many times. Registering from the component rather than from application bootstrap is what\n * keeps the entry point self-contained — an application that never imports\n * `@sinequa/atomic-angular/charts/heatmap` never runs this, and never needs `chartjs-chart-matrix`\n * installed.\n *\n * This import must stay a VALUE import. A `import type` would be elided at compile time, and with\n * it the `declare module \"chart.js\"` augmentation the package ships — `\"matrix\"` would stop being a\n * known chart type and the configuration below would no longer type-check.\n */\nexport function setupMatrixChart(): void {\n if (registered) return;\n Chart.register(MatrixController, MatrixElement);\n registered = true;\n}\n","import { afterNextRender, Component, computed, input, output, signal, type Signal } from \"@angular/core\";\nimport type { ChartConfiguration, Plugin, ScriptableContext } from \"chart.js\";\nimport type { MatrixDataPoint } from \"chartjs-chart-matrix\";\n\nimport { cn } from \"cn\";\n\nimport {\n buildHeatmap,\n galactikPrimitive,\n quantileScale,\n withAlpha,\n type AggregationLike,\n type HeatmapAxisSelection,\n type HeatmapCell,\n type HeatmapCellSelection,\n type HeatmapEncoding\n} from \"@sinequa/atomic-angular\";\nimport { ChartCanvasComponent, createAxisClickPlugin } from \"@sinequa/atomic-angular/charts\";\n\nimport { setupMatrixChart } from \"./matrix-setup\";\n\n/**\n * A two-dimensional distribution rendered as a canvas heatmap, on Chart.js.\n *\n * **`<Heatmap>` from the main entry point is the one to reach for first.** It renders a CSS grid,\n * needs no dependency at all, is accessible and selectable, and its axis labels are real buttons.\n * This component exists for the case that one cannot serve: a matrix large enough that hundreds of\n * DOM nodes become the bottleneck. Canvas rendering is flat in the cell count; a DOM grid is not.\n *\n * What it costs, and what it does not:\n *\n * - **it costs a dependency** — `chartjs-chart-matrix`, which is why this is its own entry point.\n * See the note in `public-api.ts`;\n * - **it costs accessibility** — a canvas is opaque to screen readers and to text selection, and\n * nothing brings that back. Weigh this before choosing it over `<Heatmap>`;\n * - **it does NOT cost the axis click.** Clicking a label to filter a whole row or column works\n * here too, through `createAxisClickPlugin()`. SBA v2's canvas heatmap had to hit-test painted\n * pixels for this and this component does the same, but the hit test is written once, in the\n * plugin, and `(axisSelected)` emits the same payload as `<Heatmap>`.\n *\n * The API is deliberately the same as `<Heatmap>`'s, so the two are interchangeable: same inputs,\n * same `(cellSelected)` and `(axisSelected)` payloads. Swapping one for the other is an import\n * change.\n *\n * **The host must have a resolved height** — see `<ChartCanvas>`.\n */\n@Component({\n selector: \"AggregationHeatmap, aggregation-heatmap, aggregationheatmap\",\n imports: [ChartCanvasComponent],\n template: `\n @if (isEmpty()) {\n <p class=\"flex h-full items-center justify-center text-sm text-(--font-neutral-muted)\">{{ emptyLabel() }}</p>\n } @else {\n <ChartCanvas [config]=\"config()\" [plugins]=\"plugins()\" />\n }\n `,\n host: {\n \"[class]\": 'cn(\"block h-full w-full\", class())'\n }\n})\nexport class AggregationHeatmapComponent {\n cn = cn;\n\n /**\n * Extra classes for the host element.\n * @default \"\"\n */\n readonly class = input(\"\");\n\n /**\n * The two-dimensional aggregation to render.\n * @default undefined\n */\n readonly aggregation = input<AggregationLike | undefined>(undefined);\n\n /**\n * How the two dimensions are encoded in `item.value`. Sinequa uses three different encodings\n * depending on how the aggregation was configured; see `heatmap-data.ts`.\n * @default { kind: \"separator\", separator: \"$@$\" }\n */\n readonly encoding = input<HeatmapEncoding>({ kind: \"separator\", separator: \"$@$\" });\n\n /**\n * Maximum number of columns kept, ranked by total weight.\n * @default 20\n */\n readonly maxX = input(20);\n\n /**\n * Maximum number of rows kept, ranked by total weight.\n * @default 20\n */\n readonly maxY = input(20);\n\n /**\n * Text shown when there is nothing to display.\n * @default \"No data\"\n */\n readonly emptyLabel = input(\"No data\");\n\n /**\n * Makes the axis labels clickable. Turn it off when nothing is wired to `(axisSelected)`: on a\n * canvas the pointer cursor is the only affordance a label can offer, and an affordance that\n * filters nothing is worse than none.\n * @default true\n */\n readonly enableAxisSelection = input(true);\n\n /** Emitted when a cell is clicked. */\n readonly cellSelected = output<HeatmapCellSelection>();\n\n /** Emitted when an axis label is clicked — filter a whole row or column. */\n readonly axisSelected = output<HeatmapAxisSelection>();\n\n /**\n * Built ONCE, as a field, never inside a `computed()` — the same rule as the timeline brush. The\n * plugin keeps hover state in its closure, and `<ChartCanvas>` only reads `plugins` when it builds\n * the Chart.js instance; a fresh object on every recompute would be both pointless and stateless.\n */\n private readonly axisClick: Plugin = createAxisClickPlugin(hit => this.selectAxis(hit.axis, hit.label));\n\n /**\n * Base colour of the intensity ramp, resolved once the DOM exists.\n *\n * Read after the first render rather than in a field initialiser: `getComputedStyle` returns\n * nothing useful before styles have applied, and the fallback would silently win. A *primitive*\n * ramp, so the heatmap keeps its identity when the theme flips.\n */\n private readonly rampColor = signal(galactikPrimitive(\"blue-500\"));\n\n constructor() {\n setupMatrixChart();\n afterNextRender(() => this.rampColor.set(galactikPrimitive(\"blue-500\")));\n }\n\n // Annotated rather than inferred, exactly as in `<ChartCanvas>`: the inferred type would have to\n // name chart.js' internal `AnyObject` by path, which is not portable in an emitted .d.ts.\n protected readonly plugins: Signal<Plugin[]> = computed(() => (this.enableAxisSelection() ? [this.axisClick] : []));\n\n private readonly heatmap = computed(() => buildHeatmap(this.aggregation(), { encoding: this.encoding(), maxX: this.maxX(), maxY: this.maxY() }));\n\n protected readonly isEmpty = computed(() => this.heatmap().cells.length === 0);\n\n /**\n * The plugin can only report what is painted — the label. The caller needs the filterable value,\n * which `buildHeatmap()` indexes per axis; they differ under the \"cross\" encoding.\n */\n private selectAxis(axis: \"x\" | \"y\", label: string): void {\n const heatmap = this.heatmap();\n const value = (axis === \"x\" ? heatmap.xValues : heatmap.yValues).get(label) ?? label;\n this.axisSelected.emit({ axis, label, value });\n }\n\n protected readonly config: Signal<ChartConfiguration> = computed(() => {\n const { cells, xLabels, yLabels } = this.heatmap();\n\n // Quantile scale rather than count/max: see the rationale in `heatmap-data.ts`.\n const intensity = quantileScale(cells.map(cell => cell.v));\n const ramp = this.rampColor();\n\n /*\n * Typed as `ChartConfiguration<\"matrix\">`, then cast ONCE on the final expression.\n *\n * Casting the whole literal — which is what most matrix examples do, Sinequa's usage-analytics\n * included — switches off type checking for every scriptable callback inside it, so a wrong\n * `ctx` shape or a misspelled option passes silently. The friction is only at the boundary:\n * `ChartConfiguration` without a type argument is a union over every chart type and TypeScript\n * will not narrow into it. Everything above the cast stays checked.\n */\n const config: ChartConfiguration<\"matrix\"> = {\n type: \"matrix\",\n data: {\n datasets: [\n {\n data: cells.map(cell => ({ x: cell.x, y: cell.y, v: cell.v })),\n backgroundColor: (ctx: ScriptableContext<\"matrix\">) => {\n // `raw` is typed `unknown` under strict mode; narrow it explicitly.\n const point = ctx.raw as MatrixDataPoint | undefined;\n // Floor at 0.12 so the lightest non-empty cell is still visibly a cell.\n return withAlpha(ramp, 0.12 + 0.88 * intensity(point?.v ?? 0));\n },\n borderColor: withAlpha(galactikPrimitive(\"grey-500\"), 0.25),\n borderWidth: 1,\n // Divide the plot area by the number of categories, minus a hairline gap. Both are\n // natively scriptable on a matrix dataset, so no hand-written callback types are needed.\n width: ctx => (ctx.chart.chartArea?.width ?? 0) / Math.max(1, xLabels.length) - 2,\n height: ctx => (ctx.chart.chartArea?.height ?? 0) / Math.max(1, yLabels.length) - 2\n }\n ]\n },\n options: {\n scales: {\n // `offset: true` puts categories BETWEEN gridlines rather than on them, which is what\n // centres each tile under its label. `autoSkip: false` keeps every label: on a heatmap a\n // hidden label makes its whole row unreadable — and, labels being click targets, a\n // skipped one would also be an unreachable filter.\n x: { type: \"category\", labels: xLabels, offset: true, grid: { display: false }, ticks: { autoSkip: false } },\n y: { type: \"category\", labels: yLabels, offset: true, grid: { display: false }, ticks: { autoSkip: false } }\n },\n plugins: {\n legend: { display: false },\n tooltip: {\n callbacks: {\n // The default title would be the raw index; blank it and put everything in the label.\n title: () => \"\",\n label: ctx => {\n const point = ctx.raw as MatrixDataPoint | undefined;\n return `${String(point?.x ?? \"\")} / ${String(point?.y ?? \"\")}: ${point?.v ?? 0}`;\n }\n }\n }\n },\n onClick: (_event, elements) => {\n const first = elements[0];\n if (!first) return;\n const cell: HeatmapCell | undefined = cells[first.index];\n if (cell) this.cellSelected.emit({ x: cell.x, y: cell.y, xValue: cell.xValue, yValue: cell.yValue, count: cell.v });\n }\n }\n };\n\n return config as unknown as ChartConfiguration;\n });\n}\n"],"mappings":"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"}
|