@acorex/charts 21.0.3-next.3 → 21.0.3-next.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/acorex-charts-bar-chart.mjs +52 -77
- package/fesm2022/acorex-charts-bar-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-chart-legend.mjs +1 -1
- package/fesm2022/acorex-charts-chart-legend.mjs.map +1 -1
- package/fesm2022/acorex-charts-chart-tooltip.mjs +1 -1
- package/fesm2022/acorex-charts-chart-tooltip.mjs.map +1 -1
- package/fesm2022/acorex-charts-donut-chart.mjs +80 -87
- package/fesm2022/acorex-charts-donut-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-funnel-chart.mjs +6 -8
- package/fesm2022/acorex-charts-funnel-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-gauge-chart.mjs +53 -34
- package/fesm2022/acorex-charts-gauge-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-heatmap-chart.mjs +6 -9
- package/fesm2022/acorex-charts-heatmap-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-hierarchy-chart.mjs +24 -65
- package/fesm2022/acorex-charts-hierarchy-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-line-chart.mjs +31 -98
- package/fesm2022/acorex-charts-line-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts.mjs +65 -1
- package/fesm2022/acorex-charts.mjs.map +1 -1
- package/package.json +1 -1
- package/types/acorex-charts-bar-chart.d.ts +3 -28
- package/types/acorex-charts-donut-chart.d.ts +8 -22
- package/types/acorex-charts-funnel-chart.d.ts +3 -25
- package/types/acorex-charts-gauge-chart.d.ts +6 -21
- package/types/acorex-charts-heatmap-chart.d.ts +2 -28
- package/types/acorex-charts-hierarchy-chart.d.ts +2 -27
- package/types/acorex-charts-line-chart.d.ts +2 -38
- package/types/acorex-charts.d.ts +52 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acorex-charts-chart-legend.mjs","sources":["../../../../packages/charts/chart-legend/src/lib/chart-legend.component.ts","../../../../packages/charts/chart-legend/src/lib/chart-legend.component.html","../../../../packages/charts/chart-legend/src/acorex-charts-chart-legend.ts"],"sourcesContent":["import { CommonModule } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n ViewEncapsulation,\n computed,\n input,\n output,\n viewChild,\n} from '@angular/core';\nimport { AXChartLegendCompatible, AXChartLegendItem, AXChartLegendOptions } from './chart-legend.type';\n\n@Component({\n selector: 'ax-chart-legend',\n templateUrl: './chart-legend.component.html',\n styleUrls: ['./chart-legend.component.css'],\n
|
|
1
|
+
{"version":3,"file":"acorex-charts-chart-legend.mjs","sources":["../../../../packages/charts/chart-legend/src/lib/chart-legend.component.ts","../../../../packages/charts/chart-legend/src/lib/chart-legend.component.html","../../../../packages/charts/chart-legend/src/acorex-charts-chart-legend.ts"],"sourcesContent":["import { CommonModule } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n ViewEncapsulation,\n computed,\n input,\n output,\n viewChild,\n} from '@angular/core';\nimport { AXChartLegendCompatible, AXChartLegendItem, AXChartLegendOptions } from './chart-legend.type';\n\n@Component({\n selector: 'ax-chart-legend',\n templateUrl: './chart-legend.component.html',\n styleUrls: ['./chart-legend.component.css'],\n imports: [CommonModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n})\nexport class AXChartLegendComponent {\n /**\n * Chart component instance\n * Must implement AXChartLegendCompatible interface\n */\n chart = input.required<AXChartLegendCompatible>();\n\n /**\n * Configuration options for the legend\n */\n options = input<AXChartLegendOptions>({});\n\n /**\n * Event emitted when a legend item is clicked\n * Returns the item that was clicked\n */\n itemClick = output<AXChartLegendItem>();\n\n /**\n * Event emitted when the mouse enters a legend item\n */\n itemMouseEnter = output<AXChartLegendItem>();\n\n /**\n * Event emitted when the mouse leaves a legend item\n */\n itemMouseLeave = output<AXChartLegendItem>();\n\n /**\n * Reference to legend container for measuring dimensions\n */\n legendContainer = viewChild<ElementRef<HTMLDivElement>>('legendContainer');\n\n // Computed values for the template\n protected showValues = computed(() => this.options()?.showValues !== false);\n protected showPercentage = computed(() => this.options()?.showPercentage !== false);\n protected containerClass = computed(() => {\n const opts = this.options();\n const className = opts?.className || '';\n const mode = opts?.mode || 'vertical';\n return {\n 'ax-chart-legend': true,\n [className]: !!className,\n [`ax-legend-${mode}`]: true,\n };\n });\n\n // Track if the legend is interactive\n protected isInteractive = computed(() => this.options()?.interactive !== false);\n\n // Compute the items to display from the chart\n protected displayItems = computed(() => {\n return this.chart().getLegendItems();\n });\n\n /**\n * Handle clicks on legend items\n */\n protected onItemClick(item: AXChartLegendItem): void {\n const chartInstance = this.chart();\n\n // If we have a chart and it's interactive, toggle the segment\n if (this.isInteractive()) {\n const isVisible = chartInstance.toggleSegment(item.id);\n // Update the item's hidden state to reflect the current state\n item.hidden = !isVisible;\n }\n\n // Always emit the item click event\n this.itemClick.emit(item);\n }\n\n /**\n * Handle mouse enter on legend items\n */\n protected onItemMouseEnter(item: AXChartLegendItem): void {\n this.chart().highlightSegment(null);\n this.itemMouseEnter.emit(item);\n this.chart().highlightSegment(item.id);\n }\n\n /**\n * Handle mouse leave on legend items\n */\n protected onItemMouseLeave(item: AXChartLegendItem): void {\n this.itemMouseLeave.emit(item);\n this.chart().highlightSegment(null);\n }\n\n /**\n * Get legend container dimensions\n */\n getDimensions(): { width: number; height: number } {\n if (!this.legendContainer()?.nativeElement) {\n return { width: 0, height: 0 };\n }\n\n return {\n width: this.legendContainer().nativeElement.offsetWidth,\n height: this.legendContainer().nativeElement.offsetHeight,\n };\n }\n}\n","<div #legendContainer [ngClass]=\"containerClass()\">\n @for (item of displayItems(); track item.id) {\n <div class=\"ax-legend-item\" [class.ax-legend-item-hidden]=\"item.hidden\" (click)=\"onItemClick(item)\"\n (mouseenter)=\"onItemMouseEnter(item)\" (mouseleave)=\"onItemMouseLeave(item)\">\n <span class=\"ax-legend-color\" [style.background-color]=\"item.color\"></span>\n <span class=\"ax-legend-name\">{{ item.name }}</span>\n @if (showValues()) {\n <span class=\"ax-legend-value\">{{ item.value | number: '1.0-2' }}</span>\n }\n @if (showPercentage()) {\n <span class=\"ax-legend-percentage\">{{ item.percentage | number: '1.0-2' }}%</span>\n }\n </div>\n }\n</div>","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -97,7 +97,7 @@ class AXChartTooltipComponent {
|
|
|
97
97
|
}
|
|
98
98
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXChartTooltipComponent, decorators: [{
|
|
99
99
|
type: Component,
|
|
100
|
-
args: [{ selector: 'ax-chart-tooltip',
|
|
100
|
+
args: [{ selector: 'ax-chart-tooltip', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (data()) {\n <div\n #tooltipContainer\n class=\"chart-tooltip\"\n [class.chart-tooltip-visible]=\"visible()\"\n [style.background-color]=\"data()!.tooltipBgColor || null\"\n [style.color]=\"data()!.tooltipColor || null\"\n [style.left.px]=\"position().x\"\n [style.top.px]=\"position().y\"\n [ngStyle]=\"style()\"\n [attr.aria-hidden]=\"!visible()\"\n >\n <div class=\"chart-tooltip-content\">\n <div class=\"chart-tooltip-body\">\n @if (!isTitleArray()) {\n <div class=\"chart-tooltip-title-row\">\n @if (singleColor()) {\n <div class=\"chart-tooltip-color\" [style.background-color]=\"singleColor()!\"></div>\n }\n <div class=\"chart-tooltip-title\">\n {{ data()!.title }}\n </div>\n </div>\n } @else {\n <div class=\"chart-tooltip-title-list\">\n @for (t of titleList(); let i = $index; track $index) {\n <div class=\"chart-tooltip-title-row\">\n @if (colorAt(i)) {\n <div class=\"chart-tooltip-color\" [style.background-color]=\"colorAt(i)!\"></div>\n }\n <div class=\"chart-tooltip-title\">\n {{ t }}\n </div>\n </div>\n }\n </div>\n }\n\n <div class=\"chart-tooltip-value-row\">\n <div class=\"chart-tooltip-value\">{{ formattedValue() }}</div>\n @if (showPercentage() && data()!.percentage) {\n <div class=\"chart-tooltip-percentage\">\n {{ data()!.percentage }}\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n}\n", styles: [":host{position:absolute;top:0;left:0;width:0;height:0;overflow:visible;pointer-events:none;z-index:10;--ax-comp-chart-tooltip-bg-color: var(--ax-comp-tooltip-bg-color, var(--ax-sys-color-dark));--ax-comp-chart-tooltip-text-color: var(--ax-comp-tooltip-text-color, var(--ax-sys-color-light))}.chart-tooltip{pointer-events:none;position:absolute;z-index:10;max-width:250px;width:max-content;border-radius:6px;border:1px solid rgba(255,255,255,.1);padding:8px 12px;font-size:.8rem;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -2px #0000000d;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background-color:rgba(var(--ax-comp-chart-tooltip-bg-color));color:rgba(var(--ax-comp-chart-tooltip-text-color));opacity:0;visibility:hidden;transition:opacity .15s ease}.chart-tooltip-visible{opacity:1;visibility:visible}.chart-tooltip-content{display:flex;align-items:center;justify-content:space-between;gap:8px}.chart-tooltip-body{display:flex;flex-direction:column;overflow:hidden;text-overflow:ellipsis}.chart-tooltip-title-row{display:flex;align-items:center;gap:6px;padding-bottom:8px}.chart-tooltip-title-list .chart-tooltip-title-row{padding-bottom:4px}.chart-tooltip-color{height:10px;width:10px;flex-shrink:0;border-radius:2px}.chart-tooltip-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:600;line-height:1.2}.chart-tooltip-value-row{display:flex;align-items:center;gap:8px;white-space:nowrap}.chart-tooltip-value{flex-grow:1;font-weight:500}.chart-tooltip-percentage{flex-shrink:0;border-radius:9999px;background-color:#fff3;padding:2px 6px;font-size:.7rem;font-weight:500}\n"] }]
|
|
101
101
|
}], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], position: [{ type: i0.Input, args: [{ isSignal: true, alias: "position", required: false }] }], visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: false }] }], showPercentage: [{ type: i0.Input, args: [{ isSignal: true, alias: "showPercentage", required: false }] }], style: [{ type: i0.Input, args: [{ isSignal: true, alias: "style", required: false }] }], tooltipContainer: [{ type: i0.ViewChild, args: ['tooltipContainer', { isSignal: true }] }] } });
|
|
102
102
|
|
|
103
103
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acorex-charts-chart-tooltip.mjs","sources":["../../../../packages/charts/chart-tooltip/src/lib/chart-tooltip.component.ts","../../../../packages/charts/chart-tooltip/src/lib/chart-tooltip.component.html","../../../../packages/charts/chart-tooltip/src/acorex-charts-chart-tooltip.ts"],"sourcesContent":["import { CommonModule } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n NgZone,\n afterNextRender,\n inject,\n input,\n viewChild,\n} from '@angular/core';\nimport { AXChartTooltipData } from './chart-tooltip.type';\n\n@Component({\n selector: 'ax-chart-tooltip',\n templateUrl: './chart-tooltip.component.html',\n styleUrls: ['./chart-tooltip.component.css'],\n
|
|
1
|
+
{"version":3,"file":"acorex-charts-chart-tooltip.mjs","sources":["../../../../packages/charts/chart-tooltip/src/lib/chart-tooltip.component.ts","../../../../packages/charts/chart-tooltip/src/lib/chart-tooltip.component.html","../../../../packages/charts/chart-tooltip/src/acorex-charts-chart-tooltip.ts"],"sourcesContent":["import { CommonModule } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n NgZone,\n afterNextRender,\n inject,\n input,\n viewChild,\n} from '@angular/core';\nimport { AXChartTooltipData } from './chart-tooltip.type';\n\n@Component({\n selector: 'ax-chart-tooltip',\n templateUrl: './chart-tooltip.component.html',\n styleUrls: ['./chart-tooltip.component.css'],\n imports: [CommonModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AXChartTooltipComponent {\n private ngZone = inject(NgZone);\n\n data = input<AXChartTooltipData | null>(null);\n position = input<{ x: number; y: number }>({ x: 0, y: 0 });\n visible = input<boolean>(false);\n\n /**\n * Whether to show the tooltip's percentage badge\n */\n showPercentage = input<boolean>(true);\n\n /**\n * Optional custom styling for the tooltip\n */\n style = input<{ [key: string]: string }>({});\n\n /**\n * Reference to tooltip container for measuring dimensions\n */\n tooltipContainer = viewChild<ElementRef<HTMLDivElement>>('tooltipContainer');\n\n // Tooltip dimensions\n protected tooltipWidth = 0;\n protected tooltipHeight = 0;\n\n constructor() {\n afterNextRender(() => {\n // Update tooltip dimensions when visible changes\n this.updateTooltipDimensions();\n });\n }\n\n // Helpers to support color as string | string[]\n protected isColorArray(): boolean {\n const color = this.data()?.color as unknown;\n return Array.isArray(color);\n }\n\n protected colorList(): string[] {\n const color = this.data()?.color as unknown;\n return Array.isArray(color) ? (color as string[]) : [];\n }\n\n protected singleColor(): string | null {\n const color = this.data()?.color as unknown;\n return typeof color === 'string' ? (color as string) : null;\n }\n\n protected isTitleArray(): boolean {\n const t = this.data()?.title as unknown;\n return Array.isArray(t);\n }\n\n protected titleList(): string[] {\n const t = this.data()?.title as unknown;\n return Array.isArray(t) ? (t as string[]) : [];\n }\n\n protected colorAt(index: number): string | null {\n const c = this.data()?.color as unknown;\n if (Array.isArray(c)) {\n return (c as string[])[index] ?? null;\n }\n return typeof c === 'string' ? (c as string) : null;\n }\n\n protected formattedValue(): string {\n const v = this.data()?.value;\n if (v == null) return '';\n if (typeof v === 'number') return v.toLocaleString();\n const num = Number(v);\n return isNaN(num) ? v : num.toLocaleString();\n }\n\n /**\n * Updates tooltip dimensions after it's rendered\n */\n protected updateTooltipDimensions(): void {\n if (this.visible() && this.tooltipContainer) {\n this.ngZone.runOutsideAngular(() => {\n // Use requestAnimationFrame to ensure dimensions are calculated after render\n requestAnimationFrame(() => {\n if (this.tooltipContainer()?.nativeElement) {\n this.tooltipWidth = this.tooltipContainer().nativeElement.offsetWidth;\n this.tooltipHeight = this.tooltipContainer().nativeElement.offsetHeight;\n }\n });\n });\n }\n }\n\n /**\n * Get adjusted tooltip position\n * Exposes properties for parent components to query tooltip dimensions\n */\n getDimensions(): { width: number; height: number } {\n return {\n width: this.tooltipWidth,\n height: this.tooltipHeight,\n };\n }\n}\n","@if (data()) {\n <div\n #tooltipContainer\n class=\"chart-tooltip\"\n [class.chart-tooltip-visible]=\"visible()\"\n [style.background-color]=\"data()!.tooltipBgColor || null\"\n [style.color]=\"data()!.tooltipColor || null\"\n [style.left.px]=\"position().x\"\n [style.top.px]=\"position().y\"\n [ngStyle]=\"style()\"\n [attr.aria-hidden]=\"!visible()\"\n >\n <div class=\"chart-tooltip-content\">\n <div class=\"chart-tooltip-body\">\n @if (!isTitleArray()) {\n <div class=\"chart-tooltip-title-row\">\n @if (singleColor()) {\n <div class=\"chart-tooltip-color\" [style.background-color]=\"singleColor()!\"></div>\n }\n <div class=\"chart-tooltip-title\">\n {{ data()!.title }}\n </div>\n </div>\n } @else {\n <div class=\"chart-tooltip-title-list\">\n @for (t of titleList(); let i = $index; track $index) {\n <div class=\"chart-tooltip-title-row\">\n @if (colorAt(i)) {\n <div class=\"chart-tooltip-color\" [style.background-color]=\"colorAt(i)!\"></div>\n }\n <div class=\"chart-tooltip-title\">\n {{ t }}\n </div>\n </div>\n }\n </div>\n }\n\n <div class=\"chart-tooltip-value-row\">\n <div class=\"chart-tooltip-value\">{{ formattedValue() }}</div>\n @if (showPercentage() && data()!.percentage) {\n <div class=\"chart-tooltip-percentage\">\n {{ data()!.percentage }}\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;MAoBa,uBAAuB,CAAA;AAC1B,IAAA,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AAE/B,IAAA,IAAI,GAAG,KAAK,CAA4B,IAAI,2EAAC;AAC7C,IAAA,QAAQ,GAAG,KAAK,CAA2B,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,+EAAC;AAC1D,IAAA,OAAO,GAAG,KAAK,CAAU,KAAK,8EAAC;AAE/B;;AAEG;AACH,IAAA,cAAc,GAAG,KAAK,CAAU,IAAI,qFAAC;AAErC;;AAEG;AACH,IAAA,KAAK,GAAG,KAAK,CAA4B,EAAE,4EAAC;AAE5C;;AAEG;AACH,IAAA,gBAAgB,GAAG,SAAS,CAA6B,kBAAkB,uFAAC;;IAGlE,YAAY,GAAG,CAAC;IAChB,aAAa,GAAG,CAAC;AAE3B,IAAA,WAAA,GAAA;QACE,eAAe,CAAC,MAAK;;YAEnB,IAAI,CAAC,uBAAuB,EAAE;AAChC,QAAA,CAAC,CAAC;IACJ;;IAGU,YAAY,GAAA;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,KAAgB;AAC3C,QAAA,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;IAC7B;IAEU,SAAS,GAAA;QACjB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,KAAgB;AAC3C,QAAA,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAI,KAAkB,GAAG,EAAE;IACxD;IAEU,WAAW,GAAA;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,KAAgB;AAC3C,QAAA,OAAO,OAAO,KAAK,KAAK,QAAQ,GAAI,KAAgB,GAAG,IAAI;IAC7D;IAEU,YAAY,GAAA;QACpB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,KAAgB;AACvC,QAAA,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IACzB;IAEU,SAAS,GAAA;QACjB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,KAAgB;AACvC,QAAA,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAI,CAAc,GAAG,EAAE;IAChD;AAEU,IAAA,OAAO,CAAC,KAAa,EAAA;QAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,KAAgB;AACvC,QAAA,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;AACpB,YAAA,OAAQ,CAAc,CAAC,KAAK,CAAC,IAAI,IAAI;QACvC;AACA,QAAA,OAAO,OAAO,CAAC,KAAK,QAAQ,GAAI,CAAY,GAAG,IAAI;IACrD;IAEU,cAAc,GAAA;QACtB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK;QAC5B,IAAI,CAAC,IAAI,IAAI;AAAE,YAAA,OAAO,EAAE;QACxB,IAAI,OAAO,CAAC,KAAK,QAAQ;AAAE,YAAA,OAAO,CAAC,CAAC,cAAc,EAAE;AACpD,QAAA,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC;AACrB,QAAA,OAAO,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,cAAc,EAAE;IAC9C;AAEA;;AAEG;IACO,uBAAuB,GAAA;QAC/B,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,gBAAgB,EAAE;AAC3C,YAAA,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,MAAK;;gBAEjC,qBAAqB,CAAC,MAAK;AACzB,oBAAA,IAAI,IAAI,CAAC,gBAAgB,EAAE,EAAE,aAAa,EAAE;wBAC1C,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC,aAAa,CAAC,WAAW;wBACrE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC,aAAa,CAAC,YAAY;oBACzE;AACF,gBAAA,CAAC,CAAC;AACJ,YAAA,CAAC,CAAC;QACJ;IACF;AAEA;;;AAGG;IACH,aAAa,GAAA;QACX,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,YAAY;YACxB,MAAM,EAAE,IAAI,CAAC,aAAa;SAC3B;IACH;uGArGW,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAvB,uBAAuB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,MAAA,EAAA,EAAA,IAAA,EAAA,EAAA,iBAAA,EAAA,MAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,cAAA,EAAA,EAAA,iBAAA,EAAA,gBAAA,EAAA,UAAA,EAAA,gBAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,kBAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,kBAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECpBpC,wrDAkDA,EAAA,MAAA,EAAA,CAAA,olDAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDjCY,YAAY,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,WAAA,EAAA,MAAA,EAAA,CAAA,SAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAGX,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBAPnC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,kBAAkB,WAGnB,CAAC,YAAY,CAAC,EAAA,eAAA,EACN,uBAAuB,CAAC,MAAM,EAAA,QAAA,EAAA,wrDAAA,EAAA,MAAA,EAAA,CAAA,olDAAA,CAAA,EAAA;okBAsBU,kBAAkB,EAAA,EAAA,QAAA,EAAA,IAAA,EAAA,CAAA,EAAA,CAAA,EAAA,EAAA,CAAA;;AExC7E;;AAEG;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AXChartComponent, AX_CHART_COLOR_PALETTE, getChartColor, getEasingFunction, computeTooltipPosition } from '@acorex/charts';
|
|
1
|
+
import { AX_CHART_DEFAULT_MESSAGES, AXChartComponent, AX_CHART_COLOR_PALETTE, mergeChartOptions, createChartInstanceId, mergeChartMessages, getChartColor, renderChartEmptyMessage, getEasingFunction, computeTooltipPosition } from '@acorex/charts';
|
|
2
2
|
import { AXChartTooltipComponent } from '@acorex/charts/chart-tooltip';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
4
|
import { InjectionToken, inject, ChangeDetectorRef, input, output, viewChild, signal, computed, afterNextRender, effect, untracked, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
|
|
@@ -11,12 +11,8 @@ const AXDonutChartDefaultConfig = {
|
|
|
11
11
|
animationDuration: 800,
|
|
12
12
|
animationEasing: 'cubic-out',
|
|
13
13
|
messages: {
|
|
14
|
-
|
|
15
|
-
noDataHelp: 'Please provide data to display the chart',
|
|
16
|
-
allHidden: 'All segments are hidden',
|
|
17
|
-
allHiddenHelp: 'Please toggle visibility of at least one segment',
|
|
14
|
+
...AX_CHART_DEFAULT_MESSAGES,
|
|
18
15
|
noDataIcon: 'fa-light fa-chart-pie',
|
|
19
|
-
allHiddenIcon: 'fa-light fa-eye-slash',
|
|
20
16
|
},
|
|
21
17
|
};
|
|
22
18
|
const AX_DONUT_CHART_CONFIG = new InjectionToken('AX_DONUT_CHART_CONFIG', {
|
|
@@ -24,11 +20,14 @@ const AX_DONUT_CHART_CONFIG = new InjectionToken('AX_DONUT_CHART_CONFIG', {
|
|
|
24
20
|
factory: () => AXDonutChartDefaultConfig,
|
|
25
21
|
});
|
|
26
22
|
function donutChartConfig(config = {}) {
|
|
27
|
-
|
|
23
|
+
return {
|
|
28
24
|
...AXDonutChartDefaultConfig,
|
|
29
25
|
...config,
|
|
26
|
+
messages: {
|
|
27
|
+
...AXDonutChartDefaultConfig.messages,
|
|
28
|
+
...config.messages,
|
|
29
|
+
},
|
|
30
30
|
};
|
|
31
|
-
return result;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
/**
|
|
@@ -85,12 +84,7 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
85
84
|
// Inject the chart colors
|
|
86
85
|
chartColors = inject(AX_CHART_COLOR_PALETTE);
|
|
87
86
|
// Computed configuration options
|
|
88
|
-
effectiveOptions = computed(() => {
|
|
89
|
-
return {
|
|
90
|
-
...this.configToken,
|
|
91
|
-
...this.options(),
|
|
92
|
-
};
|
|
93
|
-
}, ...(ngDevMode ? [{ debugName: "effectiveOptions" }] : /* istanbul ignore next */ []));
|
|
87
|
+
effectiveOptions = computed(() => mergeChartOptions(this.configToken, this.options()), ...(ngDevMode ? [{ debugName: "effectiveOptions" }] : /* istanbul ignore next */ []));
|
|
94
88
|
// Constants
|
|
95
89
|
TOOLTIP_GAP = 10;
|
|
96
90
|
HIGHLIGHT_TRANSITION_DURATION = 150;
|
|
@@ -102,21 +96,12 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
102
96
|
CHART_INSET = 12;
|
|
103
97
|
DIMMED_OPACITY = 0.5;
|
|
104
98
|
VISIBLE_OPACITY = 1;
|
|
99
|
+
instanceId = createChartInstanceId('ax-donut');
|
|
100
|
+
get segmentShadowFilterId() {
|
|
101
|
+
return `${this.instanceId}-segment-shadow`;
|
|
102
|
+
}
|
|
105
103
|
// Messages with defaults
|
|
106
|
-
effectiveMessages = computed(() => {
|
|
107
|
-
const defaultMessages = {
|
|
108
|
-
noData: 'No data available',
|
|
109
|
-
noDataHelp: 'Please provide data to display the chart',
|
|
110
|
-
allHidden: 'All segments are hidden',
|
|
111
|
-
allHiddenHelp: 'Please toggle visibility of at least one segment',
|
|
112
|
-
noDataIcon: 'fa-light fa-chart-pie',
|
|
113
|
-
allHiddenIcon: 'fa-light fa-eye-slash',
|
|
114
|
-
};
|
|
115
|
-
return {
|
|
116
|
-
...defaultMessages,
|
|
117
|
-
...this.effectiveOptions().messages,
|
|
118
|
-
};
|
|
119
|
-
}, ...(ngDevMode ? [{ debugName: "effectiveMessages" }] : /* istanbul ignore next */ []));
|
|
104
|
+
effectiveMessages = computed(() => mergeChartMessages(this.configToken.messages, this.options()?.messages), ...(ngDevMode ? [{ debugName: "effectiveMessages" }] : /* istanbul ignore next */ []));
|
|
120
105
|
// Data accessor for handling different incoming data formats
|
|
121
106
|
chartDataArray = computed(() => {
|
|
122
107
|
return this.data() || [];
|
|
@@ -211,7 +196,7 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
211
196
|
.classed('ax-donut-chart-highlighted', true)
|
|
212
197
|
.transition(this.HIGHLIGHT_TRANSITION_NAME)
|
|
213
198
|
.duration(this.HIGHLIGHT_TRANSITION_DURATION)
|
|
214
|
-
.style('filter',
|
|
199
|
+
.style('filter', `url(#${this.segmentShadowFilterId})`)
|
|
215
200
|
.style('opacity', this.VISIBLE_OPACITY);
|
|
216
201
|
}
|
|
217
202
|
getSegmentDataById(segmentId) {
|
|
@@ -235,7 +220,7 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
235
220
|
// Clear only chart SVG and message containers (preserve tooltip component)
|
|
236
221
|
this.d3
|
|
237
222
|
?.select(this.chartContainerEl().nativeElement)
|
|
238
|
-
.selectAll('svg, .ax-chart-message-container, .ax-donut-chart-no-data-message')
|
|
223
|
+
.selectAll('svg, .ax-chart-empty, .ax-chart-message-container, .ax-donut-chart-no-data-message')
|
|
239
224
|
.remove();
|
|
240
225
|
// Force full redraw
|
|
241
226
|
setTimeout(() => {
|
|
@@ -316,7 +301,7 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
316
301
|
* Clears the chart container
|
|
317
302
|
*/
|
|
318
303
|
clearChart(container) {
|
|
319
|
-
this.d3.select(container).selectAll('svg, .ax-chart-message-container, .ax-donut-chart-no-data-message').remove();
|
|
304
|
+
this.d3.select(container).selectAll('svg, .ax-chart-empty, .ax-chart-message-container, .ax-donut-chart-no-data-message').remove();
|
|
320
305
|
this._tooltipVisible.set(false);
|
|
321
306
|
}
|
|
322
307
|
/**
|
|
@@ -325,8 +310,12 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
325
310
|
showNoDataMessage() {
|
|
326
311
|
if (!this.chartContainerEl()?.nativeElement)
|
|
327
312
|
return;
|
|
328
|
-
const
|
|
329
|
-
|
|
313
|
+
const messages = this.effectiveMessages();
|
|
314
|
+
renderChartEmptyMessage(this.d3, this.chartContainerEl().nativeElement, {
|
|
315
|
+
icon: messages.noDataIcon,
|
|
316
|
+
title: messages.noData,
|
|
317
|
+
help: messages.noDataHelp,
|
|
318
|
+
});
|
|
330
319
|
}
|
|
331
320
|
/**
|
|
332
321
|
* Shows a message when all segments are hidden
|
|
@@ -334,38 +323,39 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
334
323
|
showAllSegmentsHiddenMessage() {
|
|
335
324
|
if (!this.chartContainerEl()?.nativeElement)
|
|
336
325
|
return;
|
|
337
|
-
const
|
|
338
|
-
|
|
326
|
+
const messages = this.effectiveMessages();
|
|
327
|
+
renderChartEmptyMessage(this.d3, this.chartContainerEl().nativeElement, {
|
|
328
|
+
icon: messages.allHiddenIcon,
|
|
329
|
+
title: messages.allHidden,
|
|
330
|
+
help: messages.allHiddenHelp,
|
|
331
|
+
});
|
|
339
332
|
}
|
|
340
333
|
/**
|
|
341
334
|
* Setups chart dimensions based on container and options
|
|
342
335
|
*/
|
|
343
336
|
setupDimensions(container, options) {
|
|
344
|
-
const containerWidth = container.clientWidth
|
|
345
|
-
const containerHeight = container.clientHeight
|
|
337
|
+
const containerWidth = Math.max(container.clientWidth, 1);
|
|
338
|
+
const containerHeight = Math.max(container.clientHeight, 1);
|
|
346
339
|
const desiredWidth = options?.width || containerWidth;
|
|
347
340
|
const desiredHeight = options?.height || containerHeight;
|
|
348
|
-
const boundedWidth = Math.max(this.MIN_CHART_DIMENSION, desiredWidth);
|
|
349
|
-
const boundedHeight = Math.max(this.MIN_CHART_DIMENSION, desiredHeight);
|
|
350
341
|
// Keep donut geometry square so side spacing stays visually balanced.
|
|
351
|
-
const chartSize = Math.max(
|
|
352
|
-
|
|
353
|
-
const height = chartSize;
|
|
354
|
-
return { width, height };
|
|
342
|
+
const chartSize = Math.max(1, Math.min(desiredWidth, desiredHeight));
|
|
343
|
+
return { width: chartSize, height: chartSize };
|
|
355
344
|
}
|
|
356
345
|
/**
|
|
357
346
|
* Renders the donut chart with visible data
|
|
358
347
|
*/
|
|
359
348
|
renderDonutChart(container, width, height, visibleData) {
|
|
360
|
-
const
|
|
349
|
+
const displayTotal = this.getDisplayTotal(visibleData);
|
|
350
|
+
const layoutTotal = this.getLayoutTotal(visibleData);
|
|
361
351
|
// Create SVG container with filters
|
|
362
352
|
const svg = this.createSvgWithFilters(container, width, height);
|
|
363
353
|
// Create main chart group
|
|
364
354
|
this.svg = svg.append('g').attr('transform', `translate(${width / 2}, ${height / 2})`);
|
|
365
355
|
// Create donut segments
|
|
366
|
-
this.createDonutSegments(width, height, visibleData,
|
|
356
|
+
this.createDonutSegments(width, height, visibleData, layoutTotal);
|
|
367
357
|
// Add total in center
|
|
368
|
-
this.addCenterDisplay(
|
|
358
|
+
this.addCenterDisplay(displayTotal);
|
|
369
359
|
}
|
|
370
360
|
/**
|
|
371
361
|
* Create SVG element with filter definitions for shadows
|
|
@@ -382,7 +372,7 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
382
372
|
.attr('preserveAspectRatio', 'xMidYMid meet');
|
|
383
373
|
// Add drop shadow filter
|
|
384
374
|
const defs = svg.append('defs');
|
|
385
|
-
const filter = defs.append('filter').attr('id',
|
|
375
|
+
const filter = defs.append('filter').attr('id', this.segmentShadowFilterId).attr('height', '130%');
|
|
386
376
|
filter.append('feGaussianBlur').attr('in', 'SourceAlpha').attr('stdDeviation', 2).attr('result', 'blur');
|
|
387
377
|
filter.append('feOffset').attr('in', 'blur').attr('dx', 1).attr('dy', 1).attr('result', 'offsetBlur');
|
|
388
378
|
const feComponentTransfer = filter
|
|
@@ -398,12 +388,12 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
398
388
|
/**
|
|
399
389
|
* Create and render the donut segments with animations
|
|
400
390
|
*/
|
|
401
|
-
createDonutSegments(chartWidth, chartHeight, data,
|
|
391
|
+
createDonutSegments(chartWidth, chartHeight, data, layoutTotal) {
|
|
402
392
|
this._isInitialAnimating.set(true);
|
|
403
393
|
// Create pie layout
|
|
404
394
|
const pie = this.d3
|
|
405
395
|
.pie()
|
|
406
|
-
.value((d) => d.value)
|
|
396
|
+
.value((d) => this.getSegmentLayoutValue(d.value))
|
|
407
397
|
.sort(null)
|
|
408
398
|
.padAngle(0.02);
|
|
409
399
|
// Calculate the radius of the donut chart
|
|
@@ -487,7 +477,7 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
487
477
|
});
|
|
488
478
|
// Add data labels if enabled and chart size is sufficient
|
|
489
479
|
if (this.effectiveOptions().showDataLabels && this.isChartLargeEnoughForLabels(chartWidth, chartHeight)) {
|
|
490
|
-
this.addDataLabels(labelArc, outerRadius, innerRadius,
|
|
480
|
+
this.addDataLabels(labelArc, outerRadius, innerRadius, layoutTotal, animationDuration);
|
|
491
481
|
}
|
|
492
482
|
// Determine when all segment animations are complete (handle empty case)
|
|
493
483
|
if (numSegments === 0) {
|
|
@@ -499,11 +489,11 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
499
489
|
return Math.min(width, height) >= this.MIN_CHART_FOR_LABELS;
|
|
500
490
|
}
|
|
501
491
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
502
|
-
addDataLabels(labelArc, outerRadius, innerRadius,
|
|
492
|
+
addDataLabels(labelArc, outerRadius, innerRadius, layoutTotal, animationDuration) {
|
|
503
493
|
if (!this.svg)
|
|
504
494
|
return;
|
|
505
495
|
const dataWithLabels = this.pieData.filter((d) => {
|
|
506
|
-
return this.calculateLabelFontSize(d, outerRadius, innerRadius,
|
|
496
|
+
return this.calculateLabelFontSize(d, outerRadius, innerRadius, layoutTotal) > 0;
|
|
507
497
|
});
|
|
508
498
|
const labels = this.svg
|
|
509
499
|
.selectAll('.ax-donut-chart-data-label')
|
|
@@ -520,9 +510,9 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
520
510
|
})
|
|
521
511
|
.attr('text-anchor', 'middle')
|
|
522
512
|
.attr('dominant-baseline', 'middle')
|
|
523
|
-
.style('font-size', (d) => `${this.calculateLabelFontSize(d, outerRadius, innerRadius,
|
|
524
|
-
.style('font-weight', (d) => ((d.data.value
|
|
525
|
-
.text((d) => this.getTruncatedLabelText(d, outerRadius, innerRadius,
|
|
513
|
+
.style('font-size', (d) => `${this.calculateLabelFontSize(d, outerRadius, innerRadius, layoutTotal)}px`)
|
|
514
|
+
.style('font-weight', (d) => (this.getSegmentPercentage(d.data.value, layoutTotal) >= 15 ? '600' : '500'))
|
|
515
|
+
.text((d) => this.getTruncatedLabelText(d, outerRadius, innerRadius, layoutTotal));
|
|
526
516
|
labels
|
|
527
517
|
.transition()
|
|
528
518
|
.duration(animationDuration)
|
|
@@ -537,9 +527,9 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
537
527
|
* visual hierarchy.
|
|
538
528
|
*/
|
|
539
529
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
540
|
-
calculateLabelFontSize(d, outerRadius, innerRadius,
|
|
530
|
+
calculateLabelFontSize(d, outerRadius, innerRadius, layoutTotal) {
|
|
541
531
|
const angle = d.endAngle - d.startAngle;
|
|
542
|
-
const segmentPercentage = (d.data.value
|
|
532
|
+
const segmentPercentage = this.getSegmentPercentage(d.data.value, layoutTotal);
|
|
543
533
|
if (segmentPercentage < 2)
|
|
544
534
|
return 0;
|
|
545
535
|
const ringWidth = outerRadius - innerRadius;
|
|
@@ -571,8 +561,8 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
571
561
|
* from "Label (XX%)" → "Label" → "XX%" → truncated label → truncated percentage.
|
|
572
562
|
*/
|
|
573
563
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
574
|
-
getTruncatedLabelText(d, outerRadius, innerRadius,
|
|
575
|
-
const fontSize = this.calculateLabelFontSize(d, outerRadius, innerRadius,
|
|
564
|
+
getTruncatedLabelText(d, outerRadius, innerRadius, layoutTotal) {
|
|
565
|
+
const fontSize = this.calculateLabelFontSize(d, outerRadius, innerRadius, layoutTotal);
|
|
576
566
|
if (fontSize === 0)
|
|
577
567
|
return '';
|
|
578
568
|
const ringWidth = outerRadius - innerRadius;
|
|
@@ -580,7 +570,7 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
580
570
|
const angle = d.endAngle - d.startAngle;
|
|
581
571
|
const chordLength = 2 * labelRadius * Math.sin(angle / 2);
|
|
582
572
|
const availableWidth = chordLength * 0.8;
|
|
583
|
-
const percentage = (d.data.value
|
|
573
|
+
const percentage = this.getSegmentPercentage(d.data.value, layoutTotal);
|
|
584
574
|
if (percentage < 2)
|
|
585
575
|
return '';
|
|
586
576
|
const label = d.data.label || '';
|
|
@@ -652,8 +642,9 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
652
642
|
this.highlightSegment(datum.id);
|
|
653
643
|
}
|
|
654
644
|
showTooltip(event, datum) {
|
|
655
|
-
const
|
|
656
|
-
const
|
|
645
|
+
const visibleData = untracked(() => this.chartDataArray()).filter((item) => !this.hiddenSegments.has(item.id));
|
|
646
|
+
const layoutTotal = this.getLayoutTotal(visibleData);
|
|
647
|
+
const percentage = layoutTotal > 0 ? this.getSegmentPercentage(datum.value, layoutTotal).toFixed(1) : '0';
|
|
657
648
|
const color = this.getSegmentColor(datum);
|
|
658
649
|
this._tooltipData.set({
|
|
659
650
|
title: datum.tooltipLabel || datum.label,
|
|
@@ -695,7 +686,7 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
695
686
|
/**
|
|
696
687
|
* Adds center display with total value
|
|
697
688
|
*/
|
|
698
|
-
addCenterDisplay(
|
|
689
|
+
addCenterDisplay(displayTotal) {
|
|
699
690
|
if (!this.svg)
|
|
700
691
|
return;
|
|
701
692
|
const chartContainerWidth = this.chartContainerEl().nativeElement.clientWidth;
|
|
@@ -714,7 +705,7 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
714
705
|
.style('font-size', `${baseFontSize}rem`)
|
|
715
706
|
.style('font-weight', '600')
|
|
716
707
|
.style('fill', 'rgb(var(--ax-comp-donut-chart-value-color))')
|
|
717
|
-
.text(
|
|
708
|
+
.text(displayTotal.toLocaleString());
|
|
718
709
|
// Add label
|
|
719
710
|
totalDisplay
|
|
720
711
|
.append('text')
|
|
@@ -745,7 +736,7 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
745
736
|
this._pendingTooltipCoords = null;
|
|
746
737
|
const container = this.chartContainerEl()?.nativeElement;
|
|
747
738
|
if (container) {
|
|
748
|
-
this.d3?.select(container).selectAll('svg, .ax-chart-message-container, .ax-donut-chart-no-data-message').remove();
|
|
739
|
+
this.d3?.select(container).selectAll('svg, .ax-chart-empty, .ax-chart-message-container, .ax-donut-chart-no-data-message').remove();
|
|
749
740
|
}
|
|
750
741
|
this.svg = null;
|
|
751
742
|
this.pieData = [];
|
|
@@ -754,20 +745,6 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
754
745
|
this._currentlyHighlightedSegment.set(null);
|
|
755
746
|
}
|
|
756
747
|
// ===== Helper utilities for modularity and maintainability =====
|
|
757
|
-
renderMessage(container, iconClass, messageText, helpText, specificClass) {
|
|
758
|
-
// Clear container area first (preserve tooltip component)
|
|
759
|
-
this.d3.select(container).selectAll('svg, .ax-chart-message-container, .ax-donut-chart-no-data-message').remove();
|
|
760
|
-
const messageContainer = this.d3
|
|
761
|
-
.select(container)
|
|
762
|
-
.append('div')
|
|
763
|
-
.attr('class', `ax-chart-message-container ax-donut-chart-message-background ${specificClass}`);
|
|
764
|
-
messageContainer
|
|
765
|
-
.append('div')
|
|
766
|
-
.attr('class', 'ax-chart-message-icon ax-donut-chart-no-data-icon')
|
|
767
|
-
.html(`<i class="${iconClass} fa-2x"></i>`);
|
|
768
|
-
messageContainer.append('div').attr('class', 'ax-chart-message-text ax-donut-chart-no-data-text').text(messageText);
|
|
769
|
-
messageContainer.append('div').attr('class', 'ax-chart-message-help ax-donut-chart-no-data-help').text(helpText);
|
|
770
|
-
}
|
|
771
748
|
// computeTooltipPosition moved to shared chart utils
|
|
772
749
|
/**
|
|
773
750
|
* Gets an accessibility label describing the donut chart for screen readers
|
|
@@ -778,36 +755,52 @@ class AXDonutChartComponent extends AXChartComponent {
|
|
|
778
755
|
if (!data || data.length === 0) {
|
|
779
756
|
return 'Empty donut chart. No data available.';
|
|
780
757
|
}
|
|
781
|
-
const
|
|
758
|
+
const displayTotal = this.getDisplayTotal(data);
|
|
759
|
+
const layoutTotal = this.getLayoutTotal(data);
|
|
782
760
|
const segmentDescriptions = data
|
|
783
761
|
.map((segment) => {
|
|
784
|
-
const percentage =
|
|
762
|
+
const percentage = layoutTotal > 0 ? this.getSegmentPercentage(segment.value, layoutTotal).toFixed(1) : '0';
|
|
785
763
|
return `${segment.label}: ${segment.value} (${percentage}%)`;
|
|
786
764
|
})
|
|
787
765
|
.join('; ');
|
|
788
|
-
return `Donut chart with ${data.length} segments. Total value: ${
|
|
766
|
+
return `Donut chart with ${data.length} segments. Total value: ${displayTotal}. ${segmentDescriptions}`;
|
|
789
767
|
}
|
|
790
768
|
/**
|
|
791
769
|
* Returns the data items for the legend
|
|
792
770
|
* @implements AXChartLegendCompatible
|
|
793
771
|
*/
|
|
794
772
|
getLegendItems() {
|
|
795
|
-
const
|
|
796
|
-
|
|
773
|
+
const data = this.chartDataArray();
|
|
774
|
+
const layoutTotal = this.getLayoutTotal(data);
|
|
775
|
+
return data.map((item, index) => ({
|
|
797
776
|
id: item.id,
|
|
798
777
|
name: item.label || `Segment ${index + 1}`,
|
|
799
778
|
value: item.value,
|
|
800
779
|
color: this.getSegmentColor(item),
|
|
801
|
-
percentage:
|
|
780
|
+
percentage: this.getSegmentPercentage(item.value, layoutTotal),
|
|
802
781
|
hidden: this.isSegmentHidden(item.id),
|
|
803
782
|
}));
|
|
804
783
|
}
|
|
784
|
+
getSegmentLayoutValue(value) {
|
|
785
|
+
return Math.abs(value);
|
|
786
|
+
}
|
|
787
|
+
getLayoutTotal(data) {
|
|
788
|
+
return data.reduce((sum, item) => sum + this.getSegmentLayoutValue(item.value), 0);
|
|
789
|
+
}
|
|
790
|
+
getDisplayTotal(data) {
|
|
791
|
+
return data.reduce((sum, item) => sum + item.value, 0);
|
|
792
|
+
}
|
|
793
|
+
getSegmentPercentage(segmentValue, layoutTotal) {
|
|
794
|
+
if (layoutTotal <= 0)
|
|
795
|
+
return 0;
|
|
796
|
+
return (this.getSegmentLayoutValue(segmentValue) / layoutTotal) * 100;
|
|
797
|
+
}
|
|
805
798
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXDonutChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
806
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: AXDonutChartComponent, isStandalone: true, selector: "ax-donut-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", segmentHover: "segmentHover" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-donut-chart\" #chartContainer role=\"img\" [attr.aria-label]=\"getAccessibilityLabel()\">\n <!-- Tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-donut-chart{display:block;width:100%;height:100%;min-height:
|
|
799
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.9", type: AXDonutChartComponent, isStandalone: true, selector: "ax-donut-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", segmentHover: "segmentHover" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-donut-chart\" #chartContainer role=\"img\" [attr.aria-label]=\"getAccessibilityLabel()\">\n <!-- Tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: [".ax-chart-empty{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;display:flex;align-items:center;justify-content:center;box-sizing:border-box;padding:1rem;pointer-events:none}.ax-chart-empty__card{text-align:center;padding:1.5rem;border-radius:.5rem;border:1px solid rgba(var(--ax-sys-color-surface));width:80%;max-width:300px;box-sizing:border-box}.ax-chart-empty__icon{opacity:.6;margin-bottom:.75rem}.ax-chart-empty__title{font-size:1rem;font-weight:600;margin-bottom:.5rem}.ax-chart-empty__help{font-size:.8rem;opacity:.6}ax-donut-chart{display:block;width:100%;height:100%;min-height:clamp(220px,38vw,360px);box-sizing:border-box;--ax-comp-donut-chart-bg-color: 0, 0, 0, 0;--ax-comp-donut-chart-data-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-donut-chart-value-color: var(--ax-sys-color-on-lightest-surface)}ax-donut-chart .ax-donut-chart{width:100%;height:100%;position:relative;min-height:0;box-sizing:border-box;padding:clamp(.5rem,1.2vw,.875rem);border-radius:.5rem;overflow:hidden;background-color:rgba(var(--ax-comp-donut-chart-bg-color))}ax-donut-chart .ax-donut-chart svg{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden}ax-donut-chart .ax-donut-chart svg g:has(text){font-family:inherit}\n"], dependencies: [{ kind: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
807
800
|
}
|
|
808
801
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXDonutChartComponent, decorators: [{
|
|
809
802
|
type: Component,
|
|
810
|
-
args: [{ selector: 'ax-donut-chart', encapsulation: ViewEncapsulation.None, imports: [AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-donut-chart\" #chartContainer role=\"img\" [attr.aria-label]=\"getAccessibilityLabel()\">\n <!-- Tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-donut-chart{display:block;width:100%;height:100%;min-height:
|
|
803
|
+
args: [{ selector: 'ax-donut-chart', encapsulation: ViewEncapsulation.None, imports: [AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-donut-chart\" #chartContainer role=\"img\" [attr.aria-label]=\"getAccessibilityLabel()\">\n <!-- Tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"true\"\n ></ax-chart-tooltip>\n</div>\n", styles: [".ax-chart-empty{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;display:flex;align-items:center;justify-content:center;box-sizing:border-box;padding:1rem;pointer-events:none}.ax-chart-empty__card{text-align:center;padding:1.5rem;border-radius:.5rem;border:1px solid rgba(var(--ax-sys-color-surface));width:80%;max-width:300px;box-sizing:border-box}.ax-chart-empty__icon{opacity:.6;margin-bottom:.75rem}.ax-chart-empty__title{font-size:1rem;font-weight:600;margin-bottom:.5rem}.ax-chart-empty__help{font-size:.8rem;opacity:.6}ax-donut-chart{display:block;width:100%;height:100%;min-height:clamp(220px,38vw,360px);box-sizing:border-box;--ax-comp-donut-chart-bg-color: 0, 0, 0, 0;--ax-comp-donut-chart-data-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-donut-chart-value-color: var(--ax-sys-color-on-lightest-surface)}ax-donut-chart .ax-donut-chart{width:100%;height:100%;position:relative;min-height:0;box-sizing:border-box;padding:clamp(.5rem,1.2vw,.875rem);border-radius:.5rem;overflow:hidden;background-color:rgba(var(--ax-comp-donut-chart-bg-color))}ax-donut-chart .ax-donut-chart svg{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden}ax-donut-chart .ax-donut-chart svg g:has(text){font-family:inherit}\n"] }]
|
|
811
804
|
}], ctorParameters: () => [], propDecorators: { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], segmentClick: [{ type: i0.Output, args: ["segmentClick"] }], segmentHover: [{ type: i0.Output, args: ["segmentHover"] }], chartContainerEl: [{ type: i0.ViewChild, args: ['chartContainer', { isSignal: true }] }] } });
|
|
812
805
|
|
|
813
806
|
/**
|