@acorex/charts 20.6.30 → 20.6.32

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.
@@ -1,20 +1,18 @@
1
- import { AXChartComponent, getEasingFunction, computeTooltipPosition } from '@acorex/charts';
1
+ import { AXChartComponent, getEasingFunction, resolveCssColorInContext, computeTooltipPosition } from '@acorex/charts';
2
2
  import { AXChartTooltipComponent } from '@acorex/charts/chart-tooltip';
3
- import { AXPlatform } from '@acorex/core/platform';
4
3
  import * as i0 from '@angular/core';
5
4
  import { InjectionToken, input, output, viewChild, signal, inject, computed, afterNextRender, effect, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
6
- import { map } from 'rxjs';
7
5
 
8
6
  const AXFunnelChartDefaultConfig = {
9
- margin: { top: 20, right: 160, bottom: 20, left: 160 },
7
+ margin: { top: 16, right: 16, bottom: 16, left: 16 },
10
8
  neckWidth: 0.3,
11
9
  showLabels: true,
10
+ showSegmentValues: true,
12
11
  labelOffset: 24,
13
12
  showTooltip: true,
14
13
  animationDuration: 1000,
15
14
  animationEasing: 'cubic-out',
16
- startColor: 'rgb(var(--ax-sys-color-primary-50))',
17
- endColor: 'rgb(var(--ax-sys-color-primary-950))',
15
+ color: 'rgb(var(--ax-sys-color-primary-500))',
18
16
  messages: {
19
17
  noData: 'No funnel data available',
20
18
  noDataIcon: 'fa-light fa-filter-list',
@@ -38,9 +36,8 @@ class AXFunnelChartComponent extends AXChartComponent {
38
36
  d3;
39
37
  _initialized = signal(false, ...(ngDevMode ? [{ debugName: "_initialized" }] : []));
40
38
  _rendered = signal(false, ...(ngDevMode ? [{ debugName: "_rendered" }] : []));
41
- platformService = inject(AXPlatform);
42
- isRtl = signal(this.platformService.isRtl(), ...(ngDevMode ? [{ debugName: "isRtl" }] : []));
43
- directionSub;
39
+ resizeObserver = null;
40
+ resizeDebounceId = null;
44
41
  // Tooltip Signals
45
42
  _tooltipVisible = signal(false, ...(ngDevMode ? [{ debugName: "_tooltipVisible" }] : []));
46
43
  _tooltipPosition = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "_tooltipPosition" }] : []));
@@ -59,14 +56,7 @@ class AXFunnelChartComponent extends AXChartComponent {
59
56
  afterNextRender(() => {
60
57
  this._initialized.set(true);
61
58
  this.loadD3();
62
- this.directionSub = this.platformService.directionChange
63
- .pipe(map((i) => i.data === 'rtl'))
64
- .subscribe((isRtl) => {
65
- this.isRtl.set(isRtl);
66
- if (this._rendered()) {
67
- this.updateChart();
68
- }
69
- });
59
+ this.attachResizeObserver();
70
60
  });
71
61
  effect(() => {
72
62
  // Trigger update on data or option change
@@ -78,9 +68,34 @@ class AXFunnelChartComponent extends AXChartComponent {
78
68
  });
79
69
  }
80
70
  ngOnDestroy() {
81
- this.directionSub?.unsubscribe();
71
+ this.detachResizeObserver();
82
72
  this.cleanupChart();
83
73
  }
74
+ attachResizeObserver() {
75
+ if (typeof ResizeObserver === 'undefined')
76
+ return;
77
+ const el = this.chartContainerEl().nativeElement;
78
+ this.resizeObserver = new ResizeObserver(() => {
79
+ if (this.resizeDebounceId !== null) {
80
+ clearTimeout(this.resizeDebounceId);
81
+ }
82
+ this.resizeDebounceId = window.setTimeout(() => {
83
+ this.resizeDebounceId = null;
84
+ if (this._rendered() && this.d3) {
85
+ this.updateChart();
86
+ }
87
+ }, 80);
88
+ });
89
+ this.resizeObserver.observe(el);
90
+ }
91
+ detachResizeObserver() {
92
+ this.resizeObserver?.disconnect();
93
+ this.resizeObserver = null;
94
+ if (this.resizeDebounceId !== null) {
95
+ clearTimeout(this.resizeDebounceId);
96
+ this.resizeDebounceId = null;
97
+ }
98
+ }
84
99
  async loadD3() {
85
100
  if (this.d3)
86
101
  return;
@@ -104,27 +119,34 @@ class AXFunnelChartComponent extends AXChartComponent {
104
119
  return;
105
120
  }
106
121
  const container = this.chartContainerEl().nativeElement;
107
- const width = container.clientWidth;
108
- const height = container.clientHeight;
122
+ const width = Math.max(0, container.clientWidth);
123
+ const height = Math.max(0, container.clientHeight);
124
+ if (width < 2 || height < 2) {
125
+ return;
126
+ }
109
127
  const opt = this.effectiveOptions();
110
128
  const margin = opt.margin;
111
- const isRtl = this.isRtl();
112
- const labelOffset = opt.labelOffset ?? 24;
129
+ const easing = getEasingFunction(this.d3, opt.animationEasing);
130
+ const resolvedBaseForIntensity = this.resolveCssColor(opt.color ?? 'rgb(99, 102, 241)');
113
131
  this.svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
114
132
  const svg = this.d3
115
133
  .select(this.svgElement)
116
134
  .attr('width', '100%')
117
135
  .attr('height', '100%')
118
136
  .attr('viewBox', `0 0 ${width} ${height}`)
119
- .attr('preserveAspectRatio', 'xMidYMid meet');
137
+ .attr('preserveAspectRatio', 'xMidYMid meet')
138
+ .attr('class', 'ax-funnel-svg');
120
139
  container.appendChild(this.svgElement);
121
140
  const innerWidth = width - margin.left - margin.right;
122
141
  const innerHeight = height - margin.top - margin.bottom;
142
+ if (innerWidth < 2 || innerHeight < 2) {
143
+ return;
144
+ }
123
145
  const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);
124
146
  const sliceHeight = innerHeight / data.length;
125
147
  const maxValue = data[0].value;
126
148
  const minValue = data[data.length - 1]?.value ?? maxValue;
127
- const easing = getEasingFunction(this.d3, opt.animationEasing);
149
+ const showSegmentValues = opt.showSegmentValues !== false;
128
150
  data.forEach((d, i) => {
129
151
  const topVal = d.value;
130
152
  const bottomVal = data[i + 1]?.value ?? d.value * opt.neckWidth;
@@ -136,7 +158,7 @@ class AXFunnelChartComponent extends AXChartComponent {
136
158
  const yBottom = (i + 1) * sliceHeight;
137
159
  const pathData = `M ${xTop},${yTop} L ${xTop + topW},${yTop} L ${xBottom + bottomW},${yBottom} L ${xBottom},${yBottom} Z`;
138
160
  const sliceGroup = g.append('g').attr('class', 'funnel-slice-container');
139
- const computedColor = this.resolveSliceColor(d, i, opt, minValue, maxValue);
161
+ const computedColor = this.resolveSliceColor(d, i, opt, minValue, maxValue, resolvedBaseForIntensity);
140
162
  const path = sliceGroup
141
163
  .append('path')
142
164
  .attr('class', 'funnel-slice')
@@ -161,27 +183,49 @@ class AXFunnelChartComponent extends AXChartComponent {
161
183
  .delay(i * 80)
162
184
  .ease(easing)
163
185
  .style('opacity', 1);
186
+ sliceGroup
187
+ .append('title')
188
+ .text(`${d.name}: ${d.value.toLocaleString()}`);
164
189
  if (opt.showLabels) {
190
+ const { cx, cy, width: midW } = this.trapezoidInteriorMetrics(xTop, topW, xBottom, bottomW, yTop, yBottom);
191
+ const { nameSize, valueSize, showValueLine, displayName } = this.computeSegmentLabelTypography(sliceHeight, midW, d.name, showSegmentValues);
192
+ const { primary, secondary } = this.pickOnSliceTextColors(computedColor);
165
193
  const labelGroup = sliceGroup.append('g').attr('class', 'funnel-label-group').style('opacity', 0);
166
- const labelX = isRtl ? innerWidth / 2 - topW / 2 - labelOffset : innerWidth / 2 + topW / 2 + labelOffset;
167
- // In RTL documents, SVG `text-anchor: end` can expand *into* the plot area because "end"
168
- // becomes the logical left edge. Using `start` makes the label expand away from the slice
169
- // on both LTR (to the right) and RTL (to the left).
170
- const anchor = 'start';
171
- labelGroup
172
- .append('text')
173
- .attr('class', 'funnel-label-name')
174
- .attr('x', labelX)
175
- .attr('y', yTop + sliceHeight / 2 - 5)
176
- .attr('text-anchor', anchor)
177
- .text(d.name);
178
- labelGroup
179
- .append('text')
180
- .attr('class', 'funnel-label-value')
181
- .attr('x', labelX)
182
- .attr('y', yTop + sliceHeight / 2 + 15)
183
- .attr('text-anchor', anchor)
184
- .text(d.value.toLocaleString());
194
+ const lineGap = Math.max(2, sliceHeight * 0.06);
195
+ if (showValueLine && valueSize > 0) {
196
+ labelGroup
197
+ .append('text')
198
+ .attr('class', 'funnel-label-name')
199
+ .attr('x', cx)
200
+ .attr('y', cy - (valueSize * 0.55 + lineGap * 0.5))
201
+ .attr('text-anchor', 'middle')
202
+ .attr('dominant-baseline', 'middle')
203
+ .style('font-size', `${nameSize}px`)
204
+ .style('fill', primary)
205
+ .text(displayName);
206
+ labelGroup
207
+ .append('text')
208
+ .attr('class', 'funnel-label-value')
209
+ .attr('x', cx)
210
+ .attr('y', cy + (nameSize * 0.55 + lineGap * 0.5))
211
+ .attr('text-anchor', 'middle')
212
+ .attr('dominant-baseline', 'middle')
213
+ .style('font-size', `${valueSize}px`)
214
+ .style('fill', secondary)
215
+ .text(d.value.toLocaleString());
216
+ }
217
+ else {
218
+ labelGroup
219
+ .append('text')
220
+ .attr('class', 'funnel-label-name funnel-label-name--single')
221
+ .attr('x', cx)
222
+ .attr('y', cy)
223
+ .attr('text-anchor', 'middle')
224
+ .attr('dominant-baseline', 'middle')
225
+ .style('font-size', `${nameSize}px`)
226
+ .style('fill', primary)
227
+ .text(displayName);
228
+ }
185
229
  labelGroup
186
230
  .transition()
187
231
  .duration(600)
@@ -190,23 +234,106 @@ class AXFunnelChartComponent extends AXChartComponent {
190
234
  }
191
235
  });
192
236
  }
193
- resolveSliceColor(item, index, opt, minValue, maxValue) {
237
+ trapezoidInteriorMetrics(xTop, topW, xBottom, bottomW, yTop, yBottom) {
238
+ const cy = (yTop + yBottom) / 2;
239
+ const denom = yBottom - yTop || 1;
240
+ const t = (cy - yTop) / denom;
241
+ const xL = xTop + t * (xBottom - xTop);
242
+ const xR = xTop + topW + t * (xBottom + bottomW - (xTop + topW));
243
+ return { cx: (xL + xR) / 2, cy, width: Math.max(0, xR - xL) };
244
+ }
245
+ computeSegmentLabelTypography(sliceHeight, segmentInnerWidth, name, wantValue) {
246
+ const charFactor = 0.52;
247
+ const minFont = 6.5;
248
+ const maxName = 15;
249
+ const innerW = Math.max(segmentInnerWidth, 8);
250
+ let showValueLine = wantValue && sliceHeight >= 17 && innerW >= 38;
251
+ const fitNameOnly = () => {
252
+ const raw = Math.min(maxName + 2, sliceHeight * 0.42, innerW / Math.max(name.length * charFactor, 2.5));
253
+ return Math.max(minFont, raw);
254
+ };
255
+ const fitBoth = () => {
256
+ const vBudget = sliceHeight * 0.76;
257
+ let nameSize = Math.min(maxName, vBudget / 2.45, innerW / Math.max(name.length * charFactor, 2.5));
258
+ nameSize = Math.max(minFont, nameSize);
259
+ let valueSize = Math.max(minFont, Math.min(nameSize * 0.88, vBudget / 2.45));
260
+ const stackH = nameSize * 1.18 + valueSize * 1.08 + sliceHeight * 0.05;
261
+ if (stackH > sliceHeight * 0.88) {
262
+ const scale = (sliceHeight * 0.88) / stackH;
263
+ nameSize = Math.max(minFont, nameSize * scale);
264
+ valueSize = Math.max(minFont, valueSize * scale);
265
+ }
266
+ return { nameSize, valueSize };
267
+ };
268
+ let nameSize;
269
+ let valueSize = 0;
270
+ if (showValueLine) {
271
+ const both = fitBoth();
272
+ nameSize = both.nameSize;
273
+ valueSize = both.valueSize;
274
+ if (nameSize * 1.2 + valueSize * 1.05 > sliceHeight * 0.9) {
275
+ showValueLine = false;
276
+ }
277
+ }
278
+ if (!showValueLine) {
279
+ nameSize = fitNameOnly();
280
+ }
281
+ const maxChars = Math.max(2, Math.floor(innerW / (nameSize * charFactor)));
282
+ const displayName = this.truncateLabel(name, maxChars);
283
+ return { nameSize, valueSize, showValueLine, displayName };
284
+ }
285
+ truncateLabel(name, maxChars) {
286
+ if (name.length <= maxChars)
287
+ return name;
288
+ if (maxChars <= 1)
289
+ return '…';
290
+ return `${name.slice(0, maxChars - 1)}…`;
291
+ }
292
+ pickOnSliceTextColors(sliceColorCss) {
293
+ const resolved = this.resolveCssColor(sliceColorCss);
294
+ const c = this.d3.color(resolved);
295
+ if (!c) {
296
+ return { primary: 'rgba(255,255,255,0.95)', secondary: 'rgba(255,255,255,0.78)' };
297
+ }
298
+ const rgb = this.d3.rgb(c);
299
+ const a = Math.max(0, Math.min(1, rgb.opacity));
300
+ // Blend slice color over white (typical card/surface) so low-alpha intensity ramps read correctly.
301
+ const br = (rgb.r / 255) * a + (1 - a);
302
+ const bg = (rgb.g / 255) * a + (1 - a);
303
+ const bb = (rgb.b / 255) * a + (1 - a);
304
+ const lum = 0.2126 * br + 0.7152 * bg + 0.0722 * bb;
305
+ if (lum > 0.62) {
306
+ return { primary: 'rgba(15,23,42,0.92)', secondary: 'rgba(15,23,42,0.72)' };
307
+ }
308
+ return { primary: 'rgba(255,255,255,0.96)', secondary: 'rgba(255,255,255,0.78)' };
309
+ }
310
+ resolveSliceColor(item, index, opt, minValue, maxValue, resolvedBaseRgb) {
194
311
  if (item.color)
195
- return item.color;
312
+ return this.resolveCssColor(item.color);
196
313
  const palette = opt.colors?.filter(Boolean) ?? [];
197
314
  if (palette.length > 0) {
198
315
  const key = String(item.id ?? item.name ?? index);
199
316
  const idx = this.hashStringToUint32(key) % palette.length;
200
- return palette[idx] ?? opt.startColor ?? '#1e1b4b';
317
+ const raw = palette[idx] ?? opt.color ?? 'rgb(99, 102, 241)';
318
+ return this.resolveCssColor(raw);
201
319
  }
202
- const startColor = opt.startColor ?? '#1e1b4b';
203
- const endColor = opt.endColor ?? '#818cf8';
204
- const resolvedStartColor = this.resolveCssColor(startColor);
205
- const resolvedEndColor = this.resolveCssColor(endColor);
206
- const range = maxValue - minValue;
207
- const t = range === 0 ? 1 : (item.value - minValue) / range;
320
+ const rangeMin = opt.valueRange?.min ?? minValue;
321
+ const rangeMax = opt.valueRange?.max ?? maxValue;
322
+ const span = rangeMax - rangeMin;
323
+ const t = span === 0 ? 1 : (item.value - rangeMin) / span;
324
+ const clamped = Math.max(0, Math.min(1, t));
325
+ return this.applyIntensityToResolvedRgb(resolvedBaseRgb, clamped);
326
+ }
327
+ /** `resolvedRgb` must be a computed `rgb()` / `rgba()` string (e.g. from {@link resolveCssColor}). */
328
+ applyIntensityToResolvedRgb(resolvedRgb, t) {
208
329
  const clamped = Math.max(0, Math.min(1, t));
209
- return this.d3.interpolateRgb(resolvedStartColor, resolvedEndColor)(clamped);
330
+ const parsed = this.d3.color(resolvedRgb);
331
+ if (!parsed)
332
+ return resolvedRgb;
333
+ const rgb = this.d3.rgb(parsed);
334
+ const minOpacity = 0.12;
335
+ const opacity = minOpacity + clamped * (1 - minOpacity);
336
+ return `rgba(${Math.round(rgb.r)},${Math.round(rgb.g)},${Math.round(rgb.b)},${opacity})`;
210
337
  }
211
338
  hashStringToUint32(input) {
212
339
  let hash = 5381;
@@ -216,16 +343,7 @@ class AXFunnelChartComponent extends AXChartComponent {
216
343
  return hash >>> 0;
217
344
  }
218
345
  resolveCssColor(color) {
219
- const container = this.chartContainerEl().nativeElement;
220
- const probe = document.createElement('span');
221
- probe.style.color = color;
222
- probe.style.position = 'absolute';
223
- probe.style.left = '-9999px';
224
- probe.style.top = '-9999px';
225
- container.appendChild(probe);
226
- const computed = getComputedStyle(probe).color;
227
- probe.remove();
228
- return computed || color;
346
+ return resolveCssColorInContext(this.chartContainerEl().nativeElement, color);
229
347
  }
230
348
  updateChart() {
231
349
  this.createChart();
@@ -260,11 +378,11 @@ class AXFunnelChartComponent extends AXChartComponent {
260
378
  }
261
379
  }
262
380
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXFunnelChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
263
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: AXFunnelChartComponent, isStandalone: true, selector: "ax-funnel-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { segmentClick: "segmentClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-funnel-chart-container\" role=\"img\" #chartContainer>\n @if (data()?.length === 0) {\n <div class=\"ax-funnel-no-data\">\n <i [class]=\"effectiveOptions().messages?.noDataIcon\"></i>\n <p class=\"ax-funnel-no-data-text\">{{ effectiveOptions().messages?.noData }}</p>\n </div>\n }\n</div>\n\n<ax-chart-tooltip [data]=\"tooltipData()\" [position]=\"tooltipPosition()\" [visible]=\"tooltipVisible()\">\n</ax-chart-tooltip>\n", styles: ["ax-funnel-chart{display:block;width:100%;height:100%;min-height:350px;--ax-comp-funnel-bg: 0, 0, 0, 0;--ax-comp-funnel-text: var(--ax-sys-color-on-surface);--ax-comp-funnel-label-secondary: var(--ax-sys-color-on-surface-variant);--ax-comp-funnel-slice-opacity: .9;--ax-comp-funnel-dim-opacity: .25}ax-funnel-chart .ax-funnel-chart-container{position:relative;width:100%;height:100%;overflow:hidden;background-color:rgba(var(--ax-comp-funnel-bg));padding:1rem}ax-funnel-chart .ax-funnel-chart-container svg{display:block;width:100%;height:100%;overflow:visible}ax-funnel-chart .ax-funnel-chart-container svg .funnel-slice{cursor:pointer;opacity:var(--ax-comp-funnel-slice-opacity);transition:opacity .3s cubic-bezier(.4,0,.2,1)}ax-funnel-chart .ax-funnel-chart-container svg .funnel-slice.is-active{opacity:1;filter:saturate(1.2)}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group{pointer-events:none}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group .funnel-label-name{font-size:12px;font-weight:700;fill:rgb(var(--ax-comp-funnel-text));text-transform:uppercase;letter-spacing:.05em}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group .funnel-label-value{font-size:13px;fill:rgb(var(--ax-comp-funnel-label-secondary));font-variant-numeric:tabular-nums;font-weight:500}ax-funnel-chart .ax-funnel-chart-container svg.is-dimmed .funnel-slice:not(.is-active){opacity:var(--ax-comp-funnel-dim-opacity)}ax-funnel-chart .ax-funnel-no-data{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;color:rgba(var(--ax-comp-funnel-text),.6)}ax-funnel-chart .ax-funnel-no-data i{font-size:2rem;margin-bottom:.5rem}\n"], dependencies: [{ kind: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
381
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: AXFunnelChartComponent, isStandalone: true, selector: "ax-funnel-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { segmentClick: "segmentClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-funnel-chart-container\" role=\"img\" #chartContainer>\n @if (data()?.length === 0) {\n <div class=\"ax-funnel-no-data\">\n <i [class]=\"effectiveOptions().messages?.noDataIcon\"></i>\n <p class=\"ax-funnel-no-data-text\">{{ effectiveOptions().messages?.noData }}</p>\n </div>\n }\n</div>\n\n<ax-chart-tooltip [data]=\"tooltipData()\" [position]=\"tooltipPosition()\" [visible]=\"tooltipVisible()\">\n</ax-chart-tooltip>\n", styles: ["ax-funnel-chart{display:block;width:100%;height:100%;min-height:clamp(220px,42vmin,520px);container-type:size;--ax-comp-funnel-bg: 0, 0, 0, 0;--ax-comp-funnel-text: var(--ax-sys-color-on-surface);--ax-comp-funnel-label-secondary: var(--ax-sys-color-on-surface-variant);--ax-comp-funnel-slice-opacity: 1;--ax-comp-funnel-dim-opacity: .22;--ax-comp-funnel-slice-stroke: rgba(255, 255, 255, .14);--ax-comp-funnel-slice-shadow: 0 10px 28px rgba(15, 23, 42, .12)}ax-funnel-chart .ax-funnel-chart-container{position:relative;width:100%;height:100%;min-height:inherit;overflow:hidden;border-radius:clamp(10px,2cqw,20px);background-color:rgba(var(--ax-comp-funnel-bg));padding:clamp(.5rem,2cqw,1.25rem);box-sizing:border-box}ax-funnel-chart .ax-funnel-chart-container svg{display:block;width:100%;height:100%;overflow:visible}ax-funnel-chart .ax-funnel-chart-container svg .funnel-slice{cursor:pointer;opacity:var(--ax-comp-funnel-slice-opacity);stroke:var(--ax-comp-funnel-slice-stroke);stroke-width:1;vector-effect:non-scaling-stroke;filter:drop-shadow(var(--ax-comp-funnel-slice-shadow));transition:filter .28s cubic-bezier(.4,0,.2,1),opacity .28s cubic-bezier(.4,0,.2,1)}ax-funnel-chart .ax-funnel-chart-container svg .funnel-slice.is-active{opacity:1;filter:drop-shadow(var(--ax-comp-funnel-slice-shadow)) saturate(1.08) brightness(1.03)}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group{pointer-events:none;font-family:inherit;font-synthesis:none;text-rendering:geometricPrecision}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group .funnel-label-name{font-weight:650;letter-spacing:.02em}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group .funnel-label-name--single{font-weight:680;letter-spacing:.015em}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group .funnel-label-value{font-weight:560;font-variant-numeric:tabular-nums;letter-spacing:.01em}ax-funnel-chart .ax-funnel-chart-container svg.is-dimmed .funnel-slice:not(.is-active){opacity:var(--ax-comp-funnel-dim-opacity)}ax-funnel-chart .ax-funnel-no-data{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;color:rgba(var(--ax-comp-funnel-text),.6)}ax-funnel-chart .ax-funnel-no-data i{font-size:clamp(1.5rem,4cqw,2rem);margin-bottom:.5rem}ax-funnel-chart .ax-funnel-no-data .ax-funnel-no-data-text{font-size:clamp(.875rem,2.5cqw,1rem)}\n"], dependencies: [{ kind: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
264
382
  }
265
383
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXFunnelChartComponent, decorators: [{
266
384
  type: Component,
267
- args: [{ selector: 'ax-funnel-chart', encapsulation: ViewEncapsulation.None, imports: [AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-funnel-chart-container\" role=\"img\" #chartContainer>\n @if (data()?.length === 0) {\n <div class=\"ax-funnel-no-data\">\n <i [class]=\"effectiveOptions().messages?.noDataIcon\"></i>\n <p class=\"ax-funnel-no-data-text\">{{ effectiveOptions().messages?.noData }}</p>\n </div>\n }\n</div>\n\n<ax-chart-tooltip [data]=\"tooltipData()\" [position]=\"tooltipPosition()\" [visible]=\"tooltipVisible()\">\n</ax-chart-tooltip>\n", styles: ["ax-funnel-chart{display:block;width:100%;height:100%;min-height:350px;--ax-comp-funnel-bg: 0, 0, 0, 0;--ax-comp-funnel-text: var(--ax-sys-color-on-surface);--ax-comp-funnel-label-secondary: var(--ax-sys-color-on-surface-variant);--ax-comp-funnel-slice-opacity: .9;--ax-comp-funnel-dim-opacity: .25}ax-funnel-chart .ax-funnel-chart-container{position:relative;width:100%;height:100%;overflow:hidden;background-color:rgba(var(--ax-comp-funnel-bg));padding:1rem}ax-funnel-chart .ax-funnel-chart-container svg{display:block;width:100%;height:100%;overflow:visible}ax-funnel-chart .ax-funnel-chart-container svg .funnel-slice{cursor:pointer;opacity:var(--ax-comp-funnel-slice-opacity);transition:opacity .3s cubic-bezier(.4,0,.2,1)}ax-funnel-chart .ax-funnel-chart-container svg .funnel-slice.is-active{opacity:1;filter:saturate(1.2)}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group{pointer-events:none}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group .funnel-label-name{font-size:12px;font-weight:700;fill:rgb(var(--ax-comp-funnel-text));text-transform:uppercase;letter-spacing:.05em}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group .funnel-label-value{font-size:13px;fill:rgb(var(--ax-comp-funnel-label-secondary));font-variant-numeric:tabular-nums;font-weight:500}ax-funnel-chart .ax-funnel-chart-container svg.is-dimmed .funnel-slice:not(.is-active){opacity:var(--ax-comp-funnel-dim-opacity)}ax-funnel-chart .ax-funnel-no-data{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;color:rgba(var(--ax-comp-funnel-text),.6)}ax-funnel-chart .ax-funnel-no-data i{font-size:2rem;margin-bottom:.5rem}\n"] }]
385
+ args: [{ selector: 'ax-funnel-chart', encapsulation: ViewEncapsulation.None, imports: [AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-funnel-chart-container\" role=\"img\" #chartContainer>\n @if (data()?.length === 0) {\n <div class=\"ax-funnel-no-data\">\n <i [class]=\"effectiveOptions().messages?.noDataIcon\"></i>\n <p class=\"ax-funnel-no-data-text\">{{ effectiveOptions().messages?.noData }}</p>\n </div>\n }\n</div>\n\n<ax-chart-tooltip [data]=\"tooltipData()\" [position]=\"tooltipPosition()\" [visible]=\"tooltipVisible()\">\n</ax-chart-tooltip>\n", styles: ["ax-funnel-chart{display:block;width:100%;height:100%;min-height:clamp(220px,42vmin,520px);container-type:size;--ax-comp-funnel-bg: 0, 0, 0, 0;--ax-comp-funnel-text: var(--ax-sys-color-on-surface);--ax-comp-funnel-label-secondary: var(--ax-sys-color-on-surface-variant);--ax-comp-funnel-slice-opacity: 1;--ax-comp-funnel-dim-opacity: .22;--ax-comp-funnel-slice-stroke: rgba(255, 255, 255, .14);--ax-comp-funnel-slice-shadow: 0 10px 28px rgba(15, 23, 42, .12)}ax-funnel-chart .ax-funnel-chart-container{position:relative;width:100%;height:100%;min-height:inherit;overflow:hidden;border-radius:clamp(10px,2cqw,20px);background-color:rgba(var(--ax-comp-funnel-bg));padding:clamp(.5rem,2cqw,1.25rem);box-sizing:border-box}ax-funnel-chart .ax-funnel-chart-container svg{display:block;width:100%;height:100%;overflow:visible}ax-funnel-chart .ax-funnel-chart-container svg .funnel-slice{cursor:pointer;opacity:var(--ax-comp-funnel-slice-opacity);stroke:var(--ax-comp-funnel-slice-stroke);stroke-width:1;vector-effect:non-scaling-stroke;filter:drop-shadow(var(--ax-comp-funnel-slice-shadow));transition:filter .28s cubic-bezier(.4,0,.2,1),opacity .28s cubic-bezier(.4,0,.2,1)}ax-funnel-chart .ax-funnel-chart-container svg .funnel-slice.is-active{opacity:1;filter:drop-shadow(var(--ax-comp-funnel-slice-shadow)) saturate(1.08) brightness(1.03)}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group{pointer-events:none;font-family:inherit;font-synthesis:none;text-rendering:geometricPrecision}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group .funnel-label-name{font-weight:650;letter-spacing:.02em}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group .funnel-label-name--single{font-weight:680;letter-spacing:.015em}ax-funnel-chart .ax-funnel-chart-container svg .funnel-label-group .funnel-label-value{font-weight:560;font-variant-numeric:tabular-nums;letter-spacing:.01em}ax-funnel-chart .ax-funnel-chart-container svg.is-dimmed .funnel-slice:not(.is-active){opacity:var(--ax-comp-funnel-dim-opacity)}ax-funnel-chart .ax-funnel-no-data{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;color:rgba(var(--ax-comp-funnel-text),.6)}ax-funnel-chart .ax-funnel-no-data i{font-size:clamp(1.5rem,4cqw,2rem);margin-bottom:.5rem}ax-funnel-chart .ax-funnel-no-data .ax-funnel-no-data-text{font-size:clamp(.875rem,2.5cqw,1rem)}\n"] }]
268
386
  }], ctorParameters: () => [] });
269
387
 
270
388
  /**