@acorex/charts 20.6.31 → 20.6.33

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,19 +1,18 @@
1
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
- color: 'rgb(99, 102, 241)',
15
+ color: 'rgb(var(--ax-sys-color-primary-500))',
17
16
  messages: {
18
17
  noData: 'No funnel data available',
19
18
  noDataIcon: 'fa-light fa-filter-list',
@@ -25,6 +24,11 @@ const AX_FUNNEL_CHART_CONFIG = new InjectionToken('AX_FUNNEL_CHART_CONFIG', {
25
24
  });
26
25
 
27
26
  class AXFunnelChartComponent extends AXChartComponent {
27
+ /**
28
+ * Fixed SVG coordinate system; the graphic scales to the container via `width`/`height: 100%` + `viewBox`.
29
+ * Margins and geometry are expressed in these units (same numeric scale as a ~400px reference viewport).
30
+ */
31
+ static VIEW_BOX_SIZE = 400;
28
32
  // Inputs
29
33
  data = input([], ...(ngDevMode ? [{ debugName: "data" }] : []));
30
34
  options = input({}, ...(ngDevMode ? [{ debugName: "options" }] : []));
@@ -37,9 +41,6 @@ class AXFunnelChartComponent extends AXChartComponent {
37
41
  d3;
38
42
  _initialized = signal(false, ...(ngDevMode ? [{ debugName: "_initialized" }] : []));
39
43
  _rendered = signal(false, ...(ngDevMode ? [{ debugName: "_rendered" }] : []));
40
- platformService = inject(AXPlatform);
41
- isRtl = signal(this.platformService.isRtl(), ...(ngDevMode ? [{ debugName: "isRtl" }] : []));
42
- directionSub;
43
44
  // Tooltip Signals
44
45
  _tooltipVisible = signal(false, ...(ngDevMode ? [{ debugName: "_tooltipVisible" }] : []));
45
46
  _tooltipPosition = signal({ x: 0, y: 0 }, ...(ngDevMode ? [{ debugName: "_tooltipPosition" }] : []));
@@ -58,14 +59,6 @@ class AXFunnelChartComponent extends AXChartComponent {
58
59
  afterNextRender(() => {
59
60
  this._initialized.set(true);
60
61
  this.loadD3();
61
- this.directionSub = this.platformService.directionChange
62
- .pipe(map((i) => i.data === 'rtl'))
63
- .subscribe((isRtl) => {
64
- this.isRtl.set(isRtl);
65
- if (this._rendered()) {
66
- this.updateChart();
67
- }
68
- });
69
62
  });
70
63
  effect(() => {
71
64
  // Trigger update on data or option change
@@ -77,7 +70,6 @@ class AXFunnelChartComponent extends AXChartComponent {
77
70
  });
78
71
  }
79
72
  ngOnDestroy() {
80
- this.directionSub?.unsubscribe();
81
73
  this.cleanupChart();
82
74
  }
83
75
  async loadD3() {
@@ -103,28 +95,32 @@ class AXFunnelChartComponent extends AXChartComponent {
103
95
  return;
104
96
  }
105
97
  const container = this.chartContainerEl().nativeElement;
106
- const width = container.clientWidth;
107
- const height = container.clientHeight;
98
+ const vb = AXFunnelChartComponent.VIEW_BOX_SIZE;
99
+ const width = vb;
100
+ const height = vb;
108
101
  const opt = this.effectiveOptions();
109
102
  const margin = opt.margin;
110
- const isRtl = this.isRtl();
111
- const labelOffset = opt.labelOffset ?? 24;
103
+ const easing = getEasingFunction(this.d3, opt.animationEasing);
104
+ const resolvedBaseForIntensity = this.resolveCssColor(opt.color ?? 'rgb(99, 102, 241)');
112
105
  this.svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
113
106
  const svg = this.d3
114
107
  .select(this.svgElement)
115
108
  .attr('width', '100%')
116
109
  .attr('height', '100%')
117
110
  .attr('viewBox', `0 0 ${width} ${height}`)
118
- .attr('preserveAspectRatio', 'xMidYMid meet');
111
+ .attr('preserveAspectRatio', 'xMidYMid meet')
112
+ .attr('class', 'ax-funnel-svg');
119
113
  container.appendChild(this.svgElement);
120
114
  const innerWidth = width - margin.left - margin.right;
121
115
  const innerHeight = height - margin.top - margin.bottom;
116
+ if (innerWidth < 1 || innerHeight < 1) {
117
+ return;
118
+ }
122
119
  const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);
123
120
  const sliceHeight = innerHeight / data.length;
124
121
  const maxValue = data[0].value;
125
122
  const minValue = data[data.length - 1]?.value ?? maxValue;
126
- const easing = getEasingFunction(this.d3, opt.animationEasing);
127
- const resolvedBaseForIntensity = this.resolveCssColor(opt.color ?? 'rgb(99, 102, 241)');
123
+ const showSegmentValues = opt.showSegmentValues !== false;
128
124
  data.forEach((d, i) => {
129
125
  const topVal = d.value;
130
126
  const bottomVal = data[i + 1]?.value ?? d.value * opt.neckWidth;
@@ -161,27 +157,49 @@ class AXFunnelChartComponent extends AXChartComponent {
161
157
  .delay(i * 80)
162
158
  .ease(easing)
163
159
  .style('opacity', 1);
160
+ sliceGroup
161
+ .append('title')
162
+ .text(`${d.name}: ${d.value.toLocaleString()}`);
164
163
  if (opt.showLabels) {
164
+ const { cx, cy, width: midW } = this.trapezoidInteriorMetrics(xTop, topW, xBottom, bottomW, yTop, yBottom);
165
+ const { nameSize, valueSize, showValueLine, displayName } = this.computeSegmentLabelTypography(sliceHeight, midW, d.name, showSegmentValues);
166
+ const { primary, secondary } = this.pickOnSliceTextColors(computedColor);
165
167
  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());
168
+ const lineGap = Math.max(2, sliceHeight * 0.06);
169
+ if (showValueLine && valueSize > 0) {
170
+ labelGroup
171
+ .append('text')
172
+ .attr('class', 'funnel-label-name')
173
+ .attr('x', cx)
174
+ .attr('y', cy - (valueSize * 0.55 + lineGap * 0.5))
175
+ .attr('text-anchor', 'middle')
176
+ .attr('dominant-baseline', 'middle')
177
+ .style('font-size', `${nameSize}`)
178
+ .style('fill', primary)
179
+ .text(displayName);
180
+ labelGroup
181
+ .append('text')
182
+ .attr('class', 'funnel-label-value')
183
+ .attr('x', cx)
184
+ .attr('y', cy + (nameSize * 0.55 + lineGap * 0.5))
185
+ .attr('text-anchor', 'middle')
186
+ .attr('dominant-baseline', 'middle')
187
+ .style('font-size', `${valueSize}`)
188
+ .style('fill', secondary)
189
+ .text(d.value.toLocaleString());
190
+ }
191
+ else {
192
+ labelGroup
193
+ .append('text')
194
+ .attr('class', 'funnel-label-name funnel-label-name--single')
195
+ .attr('x', cx)
196
+ .attr('y', cy)
197
+ .attr('text-anchor', 'middle')
198
+ .attr('dominant-baseline', 'middle')
199
+ .style('font-size', `${nameSize}`)
200
+ .style('fill', primary)
201
+ .text(displayName);
202
+ }
185
203
  labelGroup
186
204
  .transition()
187
205
  .duration(600)
@@ -190,6 +208,79 @@ class AXFunnelChartComponent extends AXChartComponent {
190
208
  }
191
209
  });
192
210
  }
211
+ trapezoidInteriorMetrics(xTop, topW, xBottom, bottomW, yTop, yBottom) {
212
+ const cy = (yTop + yBottom) / 2;
213
+ const denom = yBottom - yTop || 1;
214
+ const t = (cy - yTop) / denom;
215
+ const xL = xTop + t * (xBottom - xTop);
216
+ const xR = xTop + topW + t * (xBottom + bottomW - (xTop + topW));
217
+ return { cx: (xL + xR) / 2, cy, width: Math.max(0, xR - xL) };
218
+ }
219
+ computeSegmentLabelTypography(sliceHeight, segmentInnerWidth, name, wantValue) {
220
+ const charFactor = 0.52;
221
+ const minFont = 6.5;
222
+ const maxName = 15;
223
+ const innerW = Math.max(segmentInnerWidth, 8);
224
+ let showValueLine = wantValue && sliceHeight >= 17 && innerW >= 38;
225
+ const fitNameOnly = () => {
226
+ const raw = Math.min(maxName + 2, sliceHeight * 0.42, innerW / Math.max(name.length * charFactor, 2.5));
227
+ return Math.max(minFont, raw);
228
+ };
229
+ const fitBoth = () => {
230
+ const vBudget = sliceHeight * 0.76;
231
+ let nameSize = Math.min(maxName, vBudget / 2.45, innerW / Math.max(name.length * charFactor, 2.5));
232
+ nameSize = Math.max(minFont, nameSize);
233
+ let valueSize = Math.max(minFont, Math.min(nameSize * 0.88, vBudget / 2.45));
234
+ const stackH = nameSize * 1.18 + valueSize * 1.08 + sliceHeight * 0.05;
235
+ if (stackH > sliceHeight * 0.88) {
236
+ const scale = (sliceHeight * 0.88) / stackH;
237
+ nameSize = Math.max(minFont, nameSize * scale);
238
+ valueSize = Math.max(minFont, valueSize * scale);
239
+ }
240
+ return { nameSize, valueSize };
241
+ };
242
+ let nameSize;
243
+ let valueSize = 0;
244
+ if (showValueLine) {
245
+ const both = fitBoth();
246
+ nameSize = both.nameSize;
247
+ valueSize = both.valueSize;
248
+ if (nameSize * 1.2 + valueSize * 1.05 > sliceHeight * 0.9) {
249
+ showValueLine = false;
250
+ }
251
+ }
252
+ if (!showValueLine) {
253
+ nameSize = fitNameOnly();
254
+ }
255
+ const maxChars = Math.max(2, Math.floor(innerW / (nameSize * charFactor)));
256
+ const displayName = this.truncateLabel(name, maxChars);
257
+ return { nameSize, valueSize, showValueLine, displayName };
258
+ }
259
+ truncateLabel(name, maxChars) {
260
+ if (name.length <= maxChars)
261
+ return name;
262
+ if (maxChars <= 1)
263
+ return '…';
264
+ return `${name.slice(0, maxChars - 1)}…`;
265
+ }
266
+ pickOnSliceTextColors(sliceColorCss) {
267
+ const resolved = this.resolveCssColor(sliceColorCss);
268
+ const c = this.d3.color(resolved);
269
+ if (!c) {
270
+ return { primary: 'rgba(255,255,255,0.95)', secondary: 'rgba(255,255,255,0.78)' };
271
+ }
272
+ const rgb = this.d3.rgb(c);
273
+ const a = Math.max(0, Math.min(1, rgb.opacity));
274
+ // Blend slice color over white (typical card/surface) so low-alpha intensity ramps read correctly.
275
+ const br = (rgb.r / 255) * a + (1 - a);
276
+ const bg = (rgb.g / 255) * a + (1 - a);
277
+ const bb = (rgb.b / 255) * a + (1 - a);
278
+ const lum = 0.2126 * br + 0.7152 * bg + 0.0722 * bb;
279
+ if (lum > 0.62) {
280
+ return { primary: 'rgba(15,23,42,0.92)', secondary: 'rgba(15,23,42,0.72)' };
281
+ }
282
+ return { primary: 'rgba(255,255,255,0.96)', secondary: 'rgba(255,255,255,0.78)' };
283
+ }
193
284
  resolveSliceColor(item, index, opt, minValue, maxValue, resolvedBaseRgb) {
194
285
  if (item.color)
195
286
  return this.resolveCssColor(item.color);
@@ -261,11 +352,11 @@ class AXFunnelChartComponent extends AXChartComponent {
261
352
  }
262
353
  }
263
354
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXFunnelChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
264
- 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 });
355
+ 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:0;box-sizing:border-box;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:0;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%;min-height:0;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 });
265
356
  }
266
357
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXFunnelChartComponent, decorators: [{
267
358
  type: Component,
268
- 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"] }]
359
+ 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:0;box-sizing:border-box;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:0;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%;min-height:0;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"] }]
269
360
  }], ctorParameters: () => [] });
270
361
 
271
362
  /**