@acorex/charts 19.13.2

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.
Files changed (46) hide show
  1. package/README.md +72 -0
  2. package/bar-chart/README.md +3 -0
  3. package/bar-chart/index.d.ts +3 -0
  4. package/bar-chart/lib/bar-chart.component.d.ts +123 -0
  5. package/bar-chart/lib/bar-chart.config.d.ts +6 -0
  6. package/bar-chart/lib/bar-chart.type.d.ts +44 -0
  7. package/chart-tooltip/README.md +3 -0
  8. package/chart-tooltip/index.d.ts +2 -0
  9. package/chart-tooltip/lib/chart-tooltip.component.d.ts +43 -0
  10. package/chart-tooltip/lib/chart-tooltip.type.d.ts +7 -0
  11. package/donut-chart/README.md +3 -0
  12. package/donut-chart/index.d.ts +3 -0
  13. package/donut-chart/lib/donut-chart.component.d.ts +143 -0
  14. package/donut-chart/lib/donut-chart.config.d.ts +6 -0
  15. package/donut-chart/lib/donut-chart.type.d.ts +25 -0
  16. package/fesm2022/acorex-charts-bar-chart.mjs +563 -0
  17. package/fesm2022/acorex-charts-bar-chart.mjs.map +1 -0
  18. package/fesm2022/acorex-charts-chart-tooltip.mjs +75 -0
  19. package/fesm2022/acorex-charts-chart-tooltip.mjs.map +1 -0
  20. package/fesm2022/acorex-charts-donut-chart.mjs +616 -0
  21. package/fesm2022/acorex-charts-donut-chart.mjs.map +1 -0
  22. package/fesm2022/acorex-charts-gauge-chart.mjs +548 -0
  23. package/fesm2022/acorex-charts-gauge-chart.mjs.map +1 -0
  24. package/fesm2022/acorex-charts-hierarchy-chart.mjs +652 -0
  25. package/fesm2022/acorex-charts-hierarchy-chart.mjs.map +1 -0
  26. package/fesm2022/acorex-charts-line-chart.mjs +738 -0
  27. package/fesm2022/acorex-charts-line-chart.mjs.map +1 -0
  28. package/fesm2022/acorex-charts.mjs +8 -0
  29. package/fesm2022/acorex-charts.mjs.map +1 -0
  30. package/gauge-chart/README.md +3 -0
  31. package/gauge-chart/index.d.ts +3 -0
  32. package/gauge-chart/lib/gauge-chart.component.d.ts +110 -0
  33. package/gauge-chart/lib/gauge-chart.config.d.ts +6 -0
  34. package/gauge-chart/lib/gauge-chart.type.d.ts +37 -0
  35. package/hierarchy-chart/README.md +61 -0
  36. package/hierarchy-chart/index.d.ts +3 -0
  37. package/hierarchy-chart/lib/hierarchy-chart.component.d.ts +99 -0
  38. package/hierarchy-chart/lib/hierarchy-chart.config.d.ts +6 -0
  39. package/hierarchy-chart/lib/hierarchy-chart.type.d.ts +227 -0
  40. package/index.d.ts +1 -0
  41. package/line-chart/README.md +3 -0
  42. package/line-chart/index.d.ts +3 -0
  43. package/line-chart/lib/line-chart.component.d.ts +96 -0
  44. package/line-chart/lib/line-chart.config.d.ts +6 -0
  45. package/line-chart/lib/line-chart.type.d.ts +61 -0
  46. package/package.json +48 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acorex-charts-donut-chart.mjs","sources":["../../../../libs/charts/donut-chart/src/lib/donut-chart.config.ts","../../../../libs/charts/donut-chart/src/lib/donut-chart.component.ts","../../../../libs/charts/donut-chart/src/lib/donut-chart.component.html","../../../../libs/charts/donut-chart/src/acorex-charts-donut-chart.ts"],"sourcesContent":["import { AX_GLOBAL_CONFIG } from '@acorex/core/config';\nimport { InjectionToken, inject } from '@angular/core';\nimport { set } from 'lodash-es';\nimport { AXPDonutChartOption } from './donut-chart.type';\n\nexport const AXDonutChartDefaultConfig: AXPDonutChartOption = {\n showTooltip: true,\n donutWidth: 35,\n cornerRadius: 4,\n animationDuration: 800,\n animationEasing: 'cubic-out',\n};\n\nexport const AX_DONUT_CHART_CONFIG = new InjectionToken<AXPDonutChartOption>('AX_DONUT_CHART_CONFIG', {\n providedIn: 'root',\n factory: () => {\n const global = inject(AX_GLOBAL_CONFIG);\n set(global, 'chart.donutChart', AXDonutChartDefaultConfig);\n return AXDonutChartDefaultConfig;\n },\n});\n\nexport type PartialDonutChartConfig = Partial<AXPDonutChartOption>;\n\nexport function donutChartConfig(config: PartialDonutChartConfig = {}): AXPDonutChartOption {\n const result = {\n ...AXDonutChartDefaultConfig,\n ...config,\n };\n return result;\n}\n","import { AXChartTooltipComponent, AXChartTooltipData } from '@acorex/charts/chart-tooltip';\nimport { CommonModule } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n ElementRef,\n OnDestroy,\n afterNextRender,\n computed,\n effect,\n inject,\n input,\n output,\n signal,\n viewChild,\n} from '@angular/core';\nimport { AX_DONUT_CHART_CONFIG } from './donut-chart.config';\nimport { AXPDonutChartData, AXPDonutChartOption, AXPDonutChartValue } from './donut-chart.type';\n\nexport const AXDonutChartColors = {\n // Modern color palette suitable for data visualization\n defaultColors: [\n '#4361ee', // Blue\n '#3a0ca3', // Purple\n '#7209b7', // Violet\n '#f72585', // Pink\n '#4cc9f0', // Light Blue\n '#4895ef', // Sky Blue\n '#560bad', // Deep Purple\n '#f15bb5', // Light Pink\n '#00bbf9', // Cyan\n '#00f5d4', // Teal\n ],\n\n // Get a color from the palette by index with wraparound\n getColor: (index: number, customPalette?: string[]): string => {\n const palette = customPalette || AXDonutChartColors.defaultColors;\n return palette[index % palette.length];\n },\n};\n\n/**\n * Donut Chart Component\n * Displays data in a circular donut chart with interactive segments\n */\n@Component({\n selector: 'ax-donut-chart',\n templateUrl: './donut-chart.component.html',\n styleUrls: ['./donut-chart.component.scss'],\n standalone: true,\n imports: [CommonModule, AXChartTooltipComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AXDonutChartComponent implements OnDestroy {\n // Dependency Injection\n private cdr = inject(ChangeDetectorRef);\n\n // Inputs\n /** Chart data input */\n data = input<AXPDonutChartValue>([]);\n\n /** Chart options input */\n options = input<AXPDonutChartOption>({});\n\n // Outputs\n /** Emitted when a segment is clicked */\n segmentClick = output<AXPDonutChartData>();\n\n /** Emitted when a segment has mouse hover\n * Can be used by external elements to highlight this segment\n */\n segmentHover = output<AXPDonutChartData | null>();\n\n // Chart container reference\n private readonly chartContainerEl = viewChild.required<ElementRef<HTMLDivElement>>('chartContainer');\n\n // D3 reference - loaded asynchronously\n protected d3!: typeof import('d3');\n\n // Chart SVG reference\n private svg!: any;\n private pieData: any[] = [];\n\n // State management\n private hiddenSegments = new Set<string>();\n private _initialized = signal(false);\n private _rendered = signal(false);\n\n // Tooltip state\n private _tooltipVisible = signal(false);\n private _tooltipPosition = signal({ x: 0, y: 0 });\n private _tooltipData = signal<AXChartTooltipData>({\n title: '',\n value: 0,\n percentage: '0%',\n color: '',\n });\n\n // Public computed properties for the template\n protected tooltipVisible = this._tooltipVisible.asReadonly();\n protected tooltipPosition = this._tooltipPosition.asReadonly();\n protected tooltipData = this._tooltipData.asReadonly();\n\n // Inject configuration\n private configToken = inject(AX_DONUT_CHART_CONFIG);\n\n // Computed configuration options\n protected effectiveOptions = computed(() => {\n return {\n ...this.configToken,\n ...this.options(),\n };\n });\n\n // Data accessor for handling different incoming data formats\n protected chartDataArray = computed((): AXPDonutChartData[] => {\n return this.data() || [];\n });\n\n // Color accessor method\n protected getColor(index: number): string {\n return AXDonutChartColors.getColor(index);\n }\n\n // Segment visibility check\n protected isSegmentHidden(id: string): boolean {\n return this.hiddenSegments.has(id);\n }\n\n constructor() {\n // Dynamically load D3 and initialize the chart when the component is ready\n afterNextRender(() => {\n this._initialized.set(true);\n this.loadD3();\n });\n\n // Watch for changes to redraw the chart\n effect(() => {\n // Access inputs to track them\n this.data();\n this.effectiveOptions();\n\n // Only update if already rendered\n if (this._rendered()) {\n this.updateChart();\n }\n });\n }\n\n /**\n * Highlights a specific segment by ID\n * @param id The segment ID to highlight, or null to clear highlight\n */\n highlightSegment(id: string | null): void {\n if (!this.svg) return;\n\n // Reset all segments first\n this.svg\n .selectAll('path')\n .classed('ax-donut-chart-highlighted', false)\n .classed('ax-donut-chart-dimmed', false)\n .attr('transform', 'scale(1)');\n\n if (id !== null) {\n // Highlight the target segment\n this.svg\n .selectAll('path')\n .filter((d: any) => d?.data?.id === id)\n .classed('ax-donut-chart-highlighted', true)\n .attr('transform', 'scale(1.02)');\n\n // Dim other segments\n this.svg\n .selectAll('path')\n .filter((d: any) => d?.data?.id !== id)\n .classed('ax-donut-chart-dimmed', true);\n }\n\n this.cdr.detectChanges();\n }\n\n /**\n * Toggles visibility of a segment by ID\n * @param id Segment ID to toggle\n * @returns New visibility state (true = visible, false = hidden)\n */\n toggleSegment(id: string): boolean {\n this.toggleSegmentVisibility(id);\n return !this.isSegmentHidden(id);\n }\n\n ngOnDestroy(): void {\n this.cleanupChart();\n }\n\n /**\n * Loads D3.js dynamically\n */\n protected async loadD3(): Promise<void> {\n try {\n this.d3 = await import('d3');\n // If container is ready, create chart\n if (this._initialized() && this.chartContainerEl()) {\n this.createChart();\n this._rendered.set(true);\n }\n } catch (error) {\n console.error('Failed to load D3.js:', error);\n }\n }\n\n protected onSegmentClick(item: AXPDonutChartData): void {\n this.toggleSegmentVisibility(item.id);\n this.segmentClick.emit(item);\n }\n\n /**\n * Creates the donut chart\n */\n protected createChart(): void {\n if (!this.d3 || !this.chartContainerEl()?.nativeElement) return;\n\n try {\n const containerElement = this.chartContainerEl().nativeElement;\n\n this.clearChart(containerElement);\n\n const data = this.chartDataArray();\n\n if (!data || data.length === 0) {\n this.showNoDataMessage(containerElement);\n return;\n }\n\n // Filter out hidden segments\n const visibleData = data.filter((item) => !this.hiddenSegments.has(item.id));\n\n // If all segments are hidden, show message\n if (visibleData.length === 0) {\n this.showAllSegmentsHiddenMessage(containerElement);\n return;\n }\n\n const options = this.effectiveOptions();\n const { width, height } = this.setupDimensions(containerElement, options);\n this.renderDonutChart(containerElement, width, height, visibleData);\n } catch (error) {\n console.error('Error creating donut chart:', error);\n this.handleChartError();\n }\n }\n\n /**\n * Updates the chart with new data\n */\n protected updateChart(): void {\n this.createChart(); // Recreate the chart with updated data\n }\n\n /**\n * Clears the chart container\n */\n private clearChart(container: HTMLElement): void {\n this.d3.select(container).selectAll('svg').remove();\n this._tooltipVisible.set(false);\n }\n\n /**\n * Shows a message when no data is available\n */\n private showNoDataMessage(container: HTMLElement): void {\n const messageContainer = this.d3\n .select(container)\n .append('div')\n .attr('class', 'ax-donut-chart-no-data-message')\n .style('width', 'auto')\n .style('text-align', 'center');\n\n // Add an icon\n messageContainer\n .append('div')\n .attr('class', 'ax-donut-chart-no-data-icon')\n .html('<i class=\"fa-light fa-chart-pie-simple fa-2x\"></i>');\n\n // Add text message and help text\n messageContainer.append('div').attr('class', 'ax-donut-chart-no-data-text').text('No data available');\n messageContainer\n .append('div')\n .attr('class', 'ax-donut-chart-no-data-help')\n .text('Please provide data in the correct format');\n }\n\n /**\n * Shows a message when all segments are hidden\n */\n private showAllSegmentsHiddenMessage(container: HTMLElement): void {\n this.clearChart(container);\n\n // Add a simple div to ensure proper positioning\n const wrapper = this.d3\n .select(container)\n .append('div')\n .style('position', 'relative')\n .style('width', '100%')\n .style('height', '100%');\n\n const messageContainer = wrapper\n .append('div')\n .attr('class', 'ax-donut-chart-no-data-message')\n .style('position', 'absolute')\n .style('left', '50%')\n .style('top', '50%')\n .style('transform', 'translate(-50%, -50%)')\n .style('text-align', 'center')\n .style('z-index', '10')\n .style('background-color', 'rgba(255, 255, 255, 0.95)')\n .style('padding', '1.5rem')\n .style('border-radius', '0.5rem')\n .style('box-shadow', '0 2px 12px rgba(0, 0, 0, 0.08)')\n .style('width', '80%')\n .style('max-width', '300px');\n\n // Add an icon\n messageContainer\n .append('div')\n .attr('class', 'ax-donut-chart-no-data-icon')\n .style('color', 'var(--ax-text-muted, #999)')\n .style('margin-bottom', '0.75rem')\n .html('<i class=\"fa-light fa-eye-slash fa-2x\"></i>');\n\n // Add text message and help text\n messageContainer\n .append('div')\n .attr('class', 'ax-donut-chart-no-data-text')\n .style('font-size', '1rem')\n .style('font-weight', '600')\n .style('color', 'var(--ax-text-color, #333)')\n .style('margin-bottom', '0.5rem')\n .text('All segments are hidden');\n\n messageContainer\n .append('div')\n .attr('class', 'ax-donut-chart-no-data-help')\n .style('font-size', '0.8rem')\n .style('color', 'var(--ax-text-muted, #999)')\n .text('Click on a segment to show data');\n }\n\n /**\n * Setups chart dimensions based on container and options\n */\n private setupDimensions(container: HTMLElement, options: AXPDonutChartOption): { width: number; height: number } {\n // Get container dimensions or use defaults\n const containerWidth = container.clientWidth || 400;\n const containerHeight = container.clientHeight || 400;\n\n // Ensure minimum dimensions for the chart\n const minDim = 200;\n const width = Math.max(options?.width || containerWidth, minDim);\n const height = Math.max(options?.height || containerHeight, minDim);\n\n return { width, height };\n }\n\n /**\n * Renders the donut chart with visible data\n */\n private renderDonutChart(\n container: HTMLElement,\n width: number,\n height: number,\n visibleData: AXPDonutChartData[],\n ): void {\n const total = visibleData.reduce((sum, item) => sum + item.value, 0);\n\n // Create SVG container with filters\n const svg = this.createSvgWithFilters(container, width, height);\n\n // Create main chart group\n this.svg = svg.append('g').attr('transform', `translate(${width / 2}, ${height / 2})`);\n\n // Create donut segments\n this.createDonutSegments(width, height, visibleData, total);\n\n // Add total in center\n this.addCenterDisplay(total);\n }\n\n /**\n * Create SVG element with filter definitions for shadows\n */\n private createSvgWithFilters(container: HTMLElement, width: number, height: number): any {\n const svg = this.d3\n .select(container)\n .append('svg')\n .attr('width', '100%')\n .attr('height', '100%')\n .attr('viewBox', `0 0 ${width} ${height}`)\n .attr('preserveAspectRatio', 'xMidYMid meet');\n\n // Add drop shadow filter\n const defs = svg.append('defs');\n const filter = defs.append('filter').attr('id', 'ax-donut-chart-segment-shadow').attr('height', '130%');\n\n filter.append('feGaussianBlur').attr('in', 'SourceAlpha').attr('stdDeviation', 2).attr('result', 'blur');\n filter.append('feOffset').attr('in', 'blur').attr('dx', 1).attr('dy', 1).attr('result', 'offsetBlur');\n\n const feComponentTransfer = filter\n .append('feComponentTransfer')\n .attr('in', 'offsetBlur')\n .attr('result', 'offsetBlur');\n\n feComponentTransfer.append('feFuncA').attr('type', 'linear').attr('slope', 0.3);\n\n const feMerge = filter.append('feMerge');\n feMerge.append('feMergeNode').attr('in', 'offsetBlur');\n feMerge.append('feMergeNode').attr('in', 'SourceGraphic');\n\n return svg;\n }\n\n /**\n * Create and render the donut segments with animations\n */\n private createDonutSegments(chartWidth: number, chartHeight: number, data: AXPDonutChartData[], total: number): void {\n // Create pie layout\n const pie = this.d3\n .pie<AXPDonutChartData>()\n .value((d) => d.value)\n .sort(null)\n .padAngle(0.02);\n\n // Calculate the radius of the donut chart\n const radius = (Math.min(chartWidth, chartHeight) / 2) * 0.85;\n\n // Calculate inner radius based on donutWidth percentage\n const donutWidthPercent = this.effectiveOptions().donutWidth / 100;\n const innerRadius = radius * (1 - donutWidthPercent);\n\n // Create arc generator with the configured radius and corner radius\n const arc = this.d3\n .arc()\n .innerRadius(innerRadius)\n .outerRadius(radius * 0.95)\n .cornerRadius(this.effectiveOptions().cornerRadius);\n\n // Create hover arc for animation\n const hoverArc = this.d3\n .arc()\n .innerRadius(innerRadius)\n .outerRadius(radius + 10)\n .cornerRadius(this.effectiveOptions().cornerRadius);\n\n // Get animation options\n const animationDuration = this.effectiveOptions().animationDuration;\n const animationEasing = this.getEasingFunction(this.effectiveOptions().animationEasing);\n\n // Generate pie data\n this.pieData = pie(data);\n\n // Add segments with animation\n const segments = this.svg\n .selectAll('path')\n .data(this.pieData)\n .enter()\n .append('path')\n .attr('class', 'ax-donut-chart-segment')\n .attr('fill', (d: any, i: number) => this.getColor(i))\n .style('opacity', 0)\n .style('cursor', 'pointer') // Add cursor pointer to segments\n .on('mouseenter', (event: MouseEvent, d: any) => {\n if (!this.effectiveOptions().showTooltip) return;\n this.handleSegmentHover(event, d, hoverArc, arc, total);\n })\n .on('mouseleave', (event: MouseEvent, d: any) => {\n this.handleSegmentLeave(event, d, arc);\n })\n .on('mousemove', (event: MouseEvent) => {\n if (this._tooltipVisible()) {\n this.updateTooltipPosition(event);\n }\n })\n .on('click', (event: MouseEvent, d: any) => {\n this.onSegmentClick(d.data);\n });\n\n // Animate segments\n segments\n .transition()\n .duration(animationDuration)\n .ease(animationEasing)\n .delay((d: any, i: number) => i * 50)\n .style('opacity', 1)\n .attrTween('d', (d: any) => {\n const interpolate = this.d3.interpolate({ startAngle: d.startAngle, endAngle: d.startAngle }, d);\n return (t: number) => arc(interpolate(t)) as string;\n });\n }\n\n /**\n * Gets the appropriate D3 easing function based on the option string\n */\n private getEasingFunction(easing?: string): any {\n switch (easing) {\n case 'linear':\n return this.d3.easeLinear;\n case 'ease':\n return this.d3.easePolyInOut;\n case 'ease-in':\n return this.d3.easePolyIn;\n case 'ease-out':\n return this.d3.easePolyOut;\n case 'ease-in-out':\n return this.d3.easePolyInOut;\n case 'cubic':\n return this.d3.easeCubic;\n case 'cubic-in':\n return this.d3.easeCubicIn;\n case 'cubic-out':\n return this.d3.easeCubicOut;\n case 'cubic-in-out':\n return this.d3.easeCubicInOut;\n default:\n return this.d3.easeCubicOut; // Default easing\n }\n }\n\n /**\n * Handle hover effects on a segment\n */\n private handleSegmentHover(event: MouseEvent, d: any, hoverArc: any, normalArc: any, total: number): void {\n const segmentData = d.data;\n const percentage = ((segmentData.value / total) * 100).toFixed(1);\n const segmentColor = this.d3.select(event.currentTarget as HTMLElement).attr('fill') as string;\n\n // Update tooltip data\n this._tooltipData.set({\n title: segmentData.name,\n value: segmentData.value,\n percentage: `${percentage}%`,\n color: segmentColor,\n });\n\n // Show tooltip\n this._tooltipVisible.set(true);\n this.updateTooltipPosition(event);\n this.cdr.detectChanges();\n\n // Emit segment hover event\n this.segmentHover.emit(segmentData);\n\n // Apply hover effect\n this.d3\n .select(event.currentTarget as HTMLElement)\n .transition()\n .duration(200)\n .attr('d', (d) => hoverArc(d));\n }\n\n /**\n * Handles mouse leave from segments\n */\n private handleSegmentLeave(event: MouseEvent, d: any, normalArc: any): void {\n // Hide tooltip\n this._tooltipVisible.set(false);\n this.cdr.detectChanges();\n\n // Emit null to indicate no segment is hovered\n this.segmentHover.emit(null);\n\n // Remove hover effect\n this.d3\n .select(event.currentTarget as HTMLElement)\n .transition()\n .duration(200)\n .attr('d', (d) => normalArc(d));\n }\n\n /**\n * Updates tooltip position\n * Ensures the tooltip is visible by adjusting position when near edges\n */\n private updateTooltipPosition(event: MouseEvent): void {\n const container = this.chartContainerEl().nativeElement.getBoundingClientRect();\n const x = event.clientX - container.left;\n const y = event.clientY - container.top;\n\n // Get container dimensions to check if we're near the edge\n const containerWidth = container.width;\n const containerHeight = container.height;\n\n // Tooltip dimensions approximation (can't get exact dimensions without rendering)\n const tooltipWidth = 150; // Approximate width of tooltip\n const tooltipHeight = 100; // Approximate height of tooltip\n\n // Calculate position with edge detection\n let tooltipX = x;\n let tooltipY = y;\n\n // Check if we're too close to the right edge\n const rightEdgeDistance = containerWidth - x;\n if (rightEdgeDistance < tooltipWidth + 20) {\n // Place tooltip to the left of the cursor with no gap\n tooltipX = x - tooltipWidth + 20; // Overlap more with cursor position\n }\n\n // Check if we're too close to the bottom edge\n const bottomEdgeDistance = containerHeight - y;\n if (bottomEdgeDistance < tooltipHeight + 10) {\n // Move tooltip up if near bottom edge\n tooltipY = y - tooltipHeight;\n }\n\n this._tooltipPosition.set({\n x: tooltipX,\n y: tooltipY,\n });\n this.cdr.detectChanges();\n }\n\n /**\n * Toggles the visibility of a segment\n */\n private toggleSegmentVisibility(id: string): void {\n if (this.hiddenSegments.has(id)) {\n this.hiddenSegments.delete(id);\n } else {\n this.hiddenSegments.add(id);\n }\n\n // Hide tooltip when toggling segments\n this._tooltipVisible.set(false);\n\n this.updateChart();\n }\n\n /**\n * Adds center display with total value\n */\n private addCenterDisplay(total: number): void {\n if (!this.svg) return;\n\n // Calculate appropriate font sizes based on chart dimensions\n const chartContainerWidth = this.chartContainerEl().nativeElement.clientWidth;\n const baseFontSize = Math.max(1.4, Math.min(2.4, chartContainerWidth / 200)); // Scale between 1.4rem and 2.4rem\n const subTextFontSize = baseFontSize * 0.5;\n\n // Create group for the total display\n const totalDisplay = this.svg\n .append('g')\n .attr('class', 'ax-donut-chart-total-display')\n .attr('text-anchor', 'middle');\n\n // Add total value\n totalDisplay\n .append('text')\n .attr('class', 'ax-donut-chart-total-value')\n .style('font-size', `${baseFontSize}rem`)\n .style('font-weight', '600')\n .style('fill', 'currentColor')\n .text(total.toLocaleString());\n\n // Add label\n totalDisplay\n .append('text')\n .attr('class', 'ax-donut-chart-total-label')\n .attr('dy', '1.4em')\n .style('font-size', `${subTextFontSize}rem`)\n .style('fill', 'currentColor')\n .style('fill-opacity', '0.8')\n .text('Total');\n }\n\n /**\n * Handles chart rendering errors\n */\n private handleChartError(): void {\n const container = this.chartContainerEl()?.nativeElement;\n if (container) {\n this.showNoDataMessage(container);\n }\n }\n\n /**\n * Cleans up chart resources\n */\n private cleanupChart(): void {\n if (this.svg) {\n this.d3.select(this.chartContainerEl()?.nativeElement).selectAll('svg').remove();\n this.svg = null;\n this.pieData = [];\n }\n this.hiddenSegments.clear();\n this._tooltipVisible.set(false);\n }\n}\n","<div class=\"ax-donut-chart\" #chartContainer>\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","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,548 @@
1
+ import { CommonModule } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { InjectionToken, inject, input, viewChild, signal, computed, afterNextRender, effect, ChangeDetectionStrategy, Component } from '@angular/core';
4
+ import { AX_GLOBAL_CONFIG } from '@acorex/core/config';
5
+ import { set } from 'lodash-es';
6
+
7
+ const AXGaugeChartDefaultConfig = {
8
+ minValue: 0,
9
+ maxValue: 100,
10
+ gaugeWidth: 22,
11
+ cornerRadius: 5,
12
+ backgroundColor: 'transparent',
13
+ baseColor: '#e2e8f0',
14
+ showValue: true,
15
+ valueColor: '#6366f1',
16
+ valueFontSize: 24,
17
+ labelFontSize: 16,
18
+ animationDuration: 750,
19
+ animationEasing: 'cubic-out',
20
+ };
21
+ const AX_GAUGE_CHART_CONFIG = new InjectionToken('AX_GAUGE_CHART_CONFIG', {
22
+ providedIn: 'root',
23
+ factory: () => {
24
+ const global = inject(AX_GLOBAL_CONFIG);
25
+ set(global, 'chart.gaugeChart', AXGaugeChartDefaultConfig);
26
+ return AXGaugeChartDefaultConfig;
27
+ },
28
+ });
29
+ function gaugeChartConfig(config = {}) {
30
+ const result = {
31
+ ...AXGaugeChartDefaultConfig,
32
+ ...config,
33
+ };
34
+ return result;
35
+ }
36
+
37
+ /**
38
+ * Gauge Chart Component
39
+ * Renders a semi-circular gauge chart with animated needle and thresholds
40
+ */
41
+ class AXGaugeChartComponent {
42
+ // Inputs
43
+ /** Chart value input */
44
+ value = input(0);
45
+ /** Chart options input */
46
+ options = input({});
47
+ // Chart container reference
48
+ chartContainerEl = viewChild.required('chartContainer');
49
+ // SVG element reference (created in the code)
50
+ svgElement = null;
51
+ // D3 reference - loaded asynchronously
52
+ d3;
53
+ // Signals for component state
54
+ _initialized = signal(false);
55
+ _rendered = signal(false);
56
+ _prevValue = signal(null);
57
+ // Track previous options to detect changes
58
+ _prevOptionsString = '';
59
+ // Inject configuration
60
+ configToken = inject(AX_GAUGE_CHART_CONFIG);
61
+ // Computed configuration options
62
+ effectiveOptions = computed(() => {
63
+ return {
64
+ ...this.configToken,
65
+ ...this.options(),
66
+ };
67
+ });
68
+ constructor() {
69
+ // Dynamically load D3 and initialize the chart when the component is ready
70
+ afterNextRender(() => {
71
+ this._initialized.set(true);
72
+ this.loadD3();
73
+ });
74
+ // Watch for changes to redraw the chart
75
+ effect(() => {
76
+ // Access inputs to track them
77
+ const currentValue = this.value();
78
+ const options = this.effectiveOptions();
79
+ // Update previous value
80
+ this._prevValue.set(currentValue);
81
+ // Only update if already rendered
82
+ if (this._rendered()) {
83
+ // If only the value changed, just update the value display and dial
84
+ const optionsString = JSON.stringify(options);
85
+ if (this._prevOptionsString === optionsString) {
86
+ this.updateValueAndDial(currentValue);
87
+ }
88
+ else {
89
+ // If options changed, recreate the whole chart
90
+ this._prevOptionsString = optionsString;
91
+ this.updateChart();
92
+ }
93
+ }
94
+ });
95
+ }
96
+ ngOnDestroy() {
97
+ this.cleanupChart();
98
+ }
99
+ /**
100
+ * Loads D3.js dynamically
101
+ */
102
+ async loadD3() {
103
+ try {
104
+ this.d3 = await import('d3');
105
+ // If container is ready, create chart
106
+ if (this._initialized() && this.chartContainerEl()) {
107
+ this.createChart();
108
+ this._rendered.set(true);
109
+ }
110
+ }
111
+ catch (error) {
112
+ console.error('Failed to load D3.js:', error);
113
+ }
114
+ }
115
+ /**
116
+ * Creates the gauge chart with all elements
117
+ */
118
+ createChart() {
119
+ // Clear container and create SVG
120
+ if (this.svgElement) {
121
+ this.svgElement.remove();
122
+ }
123
+ // Create SVG element
124
+ this.svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
125
+ this.chartContainerEl().nativeElement.appendChild(this.svgElement);
126
+ // Get current options
127
+ const options = this.effectiveOptions();
128
+ // Store options string for comparison
129
+ this._prevOptionsString = JSON.stringify(options);
130
+ const backgroundColor = options.backgroundColor;
131
+ const baseColor = options.baseColor;
132
+ const showValue = options.showValue;
133
+ const valueColor = options.valueColor;
134
+ const valueFontSize = options.valueFontSize;
135
+ const labelFontSize = options.labelFontSize;
136
+ const minValue = options.minValue;
137
+ const maxValue = options.maxValue;
138
+ const thresholds = options.thresholds ?? [];
139
+ const label = options.label ?? '';
140
+ const gaugeWidth = options.gaugeWidth;
141
+ const cornerRadius = options.cornerRadius;
142
+ const animationDuration = options.animationDuration;
143
+ // Calculate responsive dimensions
144
+ const containerElement = this.chartContainerEl().nativeElement;
145
+ const containerWidth = containerElement.clientWidth;
146
+ const containerHeight = containerElement.clientHeight;
147
+ // Set dimensions based on options or container size
148
+ let width, height;
149
+ if (options.width && options.height) {
150
+ // Use the provided dimensions
151
+ width = options.width;
152
+ height = options.height;
153
+ // Apply explicit dimensions to the container
154
+ containerElement.style.width = `${width}px`;
155
+ containerElement.style.height = `${height}px`;
156
+ }
157
+ else {
158
+ // Use container dimensions
159
+ width = containerWidth;
160
+ height = containerHeight;
161
+ }
162
+ // Ensure minimum dimensions
163
+ width = Math.max(width, 100);
164
+ height = Math.max(height, 50);
165
+ // For a semi-circular gauge, width should be approximately twice the height
166
+ const size = Math.min(width, height * 2);
167
+ const margin = size * 0.1;
168
+ // Set up SVG with responsive viewBox
169
+ const svg = this.d3
170
+ .select(this.svgElement)
171
+ .attr('width', '100%')
172
+ .attr('height', '100%')
173
+ .attr('viewBox', `0 0 ${size} ${size / 2}`)
174
+ .attr('preserveAspectRatio', 'xMidYMid meet');
175
+ // Create a group for the chart with margin and move it to show only the top half
176
+ const chartGroup = svg.append('g').attr('transform', `translate(${size / 2}, ${size / 2 - margin})`);
177
+ // Define gauge parameters
178
+ const radius = size / 2 - margin;
179
+ const innerRadius = radius - gaugeWidth;
180
+ const outerRadius = radius;
181
+ // Create gradient definitions
182
+ this.createGradients(svg, thresholds, baseColor, valueColor);
183
+ // Draw the background arc
184
+ this.drawBackgroundArc(chartGroup, innerRadius, outerRadius, cornerRadius, backgroundColor);
185
+ // Draw the threshold arcs if thresholds exist
186
+ if (thresholds.length > 0) {
187
+ this.drawThresholds(chartGroup, innerRadius, outerRadius, minValue, maxValue, thresholds, cornerRadius);
188
+ }
189
+ // Draw tick marks
190
+ this.drawTicks(chartGroup, outerRadius, minValue, maxValue);
191
+ // Draw the dial/needle with animation
192
+ this.drawDial(chartGroup, radius, this.value(), minValue, maxValue, valueColor, animationDuration);
193
+ // Draw the value display (after the dial so it's on top)
194
+ if (showValue) {
195
+ this.drawValueDisplay(chartGroup, this.value(), label, radius, valueColor, valueFontSize, labelFontSize);
196
+ }
197
+ }
198
+ /**
199
+ * Updates the chart when options change
200
+ */
201
+ updateChart() {
202
+ this.createChart();
203
+ }
204
+ /**
205
+ * Updates only the value display and dial position without recreating the chart
206
+ */
207
+ updateValueAndDial(value) {
208
+ if (!this.svgElement)
209
+ return;
210
+ const options = this.effectiveOptions();
211
+ const minValue = options.minValue;
212
+ const maxValue = options.maxValue;
213
+ const animationDuration = options.animationDuration;
214
+ const easingFunction = this.getEasingFunction(options.animationEasing);
215
+ const currentValue = value;
216
+ const svg = this.d3.select(this.svgElement);
217
+ // Get chart size from SVG viewBox
218
+ const viewBoxValues = svg.attr('viewBox')?.split(' ').map(Number) || [0, 0, 300, 150];
219
+ const size = viewBoxValues[2];
220
+ const margin = size * 0.1;
221
+ const radius = size / 2 - margin;
222
+ // Update value text
223
+ svg.select('.gauge-value').text(currentValue.toLocaleString());
224
+ // Update needle position
225
+ const angle = this.scaleValueToAngle(currentValue, minValue, maxValue);
226
+ const angleInDegrees = this.radiansToDegrees(angle - Math.PI / 2);
227
+ svg
228
+ .select('.gauge-needle')
229
+ .transition()
230
+ .duration(animationDuration)
231
+ .ease(easingFunction)
232
+ .attr('transform', `rotate(${angleInDegrees})`);
233
+ }
234
+ /**
235
+ * Cleans up chart resources
236
+ */
237
+ cleanupChart() {
238
+ if (this.svgElement) {
239
+ this.svgElement.remove();
240
+ this.svgElement = null;
241
+ }
242
+ }
243
+ /**
244
+ * Creates gradient definitions for thresholds
245
+ */
246
+ createGradients(svg, thresholds, baseColor, valueColor) {
247
+ const defs = svg.append('defs');
248
+ // Create a radial gradient for the background
249
+ const bgGradient = defs
250
+ .append('radialGradient')
251
+ .attr('id', 'gauge-bg-gradient')
252
+ .attr('cx', '50%')
253
+ .attr('cy', '50%')
254
+ .attr('r', '50%')
255
+ .attr('gradientUnits', 'userSpaceOnUse');
256
+ if (thresholds.length === 0) {
257
+ // Default gradient when no thresholds are provided
258
+ bgGradient
259
+ .append('stop')
260
+ .attr('offset', '0%')
261
+ .attr('stop-color', this.d3.color(valueColor)?.brighter(0.5).toString() || valueColor);
262
+ bgGradient.append('stop').attr('offset', '100%').attr('stop-color', valueColor);
263
+ }
264
+ else {
265
+ bgGradient
266
+ .append('stop')
267
+ .attr('offset', '0%')
268
+ .attr('stop-color', this.d3.color(baseColor)?.brighter(0.5).toString() || baseColor);
269
+ bgGradient.append('stop').attr('offset', '100%').attr('stop-color', baseColor);
270
+ // Create gradients for each threshold
271
+ thresholds.forEach((threshold, i) => {
272
+ const gradient = defs
273
+ .append('linearGradient')
274
+ .attr('id', `threshold-gradient-${i}`)
275
+ .attr('gradientUnits', 'userSpaceOnUse')
276
+ .attr('x1', '-1')
277
+ .attr('y1', '0')
278
+ .attr('x2', '1')
279
+ .attr('y2', '0');
280
+ gradient
281
+ .append('stop')
282
+ .attr('offset', '0%')
283
+ .attr('stop-color', this.d3.color(threshold.color)?.brighter(0.5).toString() || threshold.color);
284
+ gradient.append('stop').attr('offset', '100%').attr('stop-color', threshold.color);
285
+ });
286
+ }
287
+ }
288
+ /**
289
+ * Draws the background arc
290
+ */
291
+ drawBackgroundArc(chartGroup, innerRadius, outerRadius, cornerRadius, backgroundColor) {
292
+ const backgroundArc = this.d3
293
+ .arc()
294
+ .innerRadius(innerRadius)
295
+ .outerRadius(outerRadius)
296
+ .startAngle(-Math.PI / 2) // Start from bottom (-90 degrees)
297
+ .endAngle(Math.PI / 2) // End at top (90 degrees)
298
+ .cornerRadius(cornerRadius);
299
+ chartGroup
300
+ .append('path')
301
+ .attr('d', backgroundArc({
302
+ innerRadius,
303
+ outerRadius,
304
+ startAngle: -Math.PI / 2,
305
+ endAngle: Math.PI / 2,
306
+ }))
307
+ .attr('fill', backgroundColor === 'transparent' ? 'url(#gauge-bg-gradient)' : backgroundColor)
308
+ .attr('filter', 'drop-shadow(0px 2px 3px rgba(0,0,0,0.1))');
309
+ }
310
+ /**
311
+ * Draws the threshold arcs with colors
312
+ */
313
+ drawThresholds(chartGroup, innerRadius, outerRadius, minValue, maxValue, thresholds, cornerRadius) {
314
+ const arc = this.d3
315
+ .arc()
316
+ .innerRadius(innerRadius)
317
+ .outerRadius(outerRadius * 0.98)
318
+ .cornerRadius(cornerRadius);
319
+ // Sort thresholds by value in ascending order
320
+ const sortedThresholds = [...thresholds].sort((a, b) => a.value - b.value);
321
+ // Calculate all angles first using the color angle calculation
322
+ const angles = sortedThresholds.map((t) => this.scaleValueToColorAngle(t.value, minValue, maxValue));
323
+ // Start from the minimum value angle
324
+ let previousEndAngle = this.scaleValueToColorAngle(minValue, minValue, maxValue);
325
+ sortedThresholds.forEach((threshold, i) => {
326
+ const endAngle = angles[i];
327
+ chartGroup
328
+ .append('path')
329
+ .attr('d', arc({
330
+ innerRadius,
331
+ outerRadius,
332
+ startAngle: previousEndAngle,
333
+ endAngle,
334
+ }))
335
+ .attr('fill', `url(#threshold-gradient-${i})`)
336
+ .attr('class', 'threshold-arc')
337
+ .attr('data-value', threshold.value);
338
+ // Update the previous end angle for the next threshold
339
+ previousEndAngle = endAngle;
340
+ });
341
+ // Draw the last segment if the last threshold is less than the max value
342
+ if (previousEndAngle < Math.PI / 2) {
343
+ chartGroup
344
+ .append('path')
345
+ .attr('d', arc({
346
+ innerRadius,
347
+ outerRadius,
348
+ startAngle: previousEndAngle,
349
+ endAngle: Math.PI / 2,
350
+ }))
351
+ .attr('fill', 'url(#gauge-bg-gradient)')
352
+ .attr('class', 'threshold-arc');
353
+ }
354
+ }
355
+ /**
356
+ * Draws tick marks and labels around the gauge
357
+ */
358
+ drawTicks(chartGroup, radius, minValue, maxValue) {
359
+ const tickCount = 5; // Number of ticks including min and max
360
+ const tickLength = radius * 0.1;
361
+ const labelOffset = radius * 0.15;
362
+ // Create a group for the ticks
363
+ const tickGroup = chartGroup.append('g').attr('class', 'ticks');
364
+ // Generate tick values
365
+ const tickValues = [];
366
+ const step = (maxValue - minValue) / (tickCount - 1);
367
+ for (let i = 0; i < tickCount; i++) {
368
+ tickValues.push(minValue + i * step);
369
+ }
370
+ // Create ticks and labels
371
+ tickValues.forEach((tick) => {
372
+ // Calculate angle for this tick
373
+ const angle = this.scaleValueToAngle(tick, minValue, maxValue);
374
+ const radians = angle - Math.PI / 2; // Adjust for starting at bottom (-90 degrees)
375
+ // Calculate positions
376
+ const x1 = Math.cos(radians) * (radius + 5);
377
+ const y1 = Math.sin(radians) * (radius + 5);
378
+ const x2 = Math.cos(radians) * (radius + tickLength);
379
+ const y2 = Math.sin(radians) * (radius + tickLength);
380
+ // Calculate label position
381
+ const labelX = Math.cos(radians) * (radius + labelOffset);
382
+ const labelY = Math.sin(radians) * (radius + labelOffset);
383
+ // Draw tick line
384
+ tickGroup
385
+ .append('line')
386
+ .attr('x1', x1)
387
+ .attr('y1', y1)
388
+ .attr('x2', x2)
389
+ .attr('y2', y2)
390
+ .attr('stroke', '#94a3b8')
391
+ .attr('stroke-width', 2);
392
+ // Add tick label
393
+ tickGroup
394
+ .append('text')
395
+ .attr('x', labelX)
396
+ .attr('y', labelY)
397
+ .attr('text-anchor', 'middle')
398
+ .attr('dominant-baseline', 'middle')
399
+ .attr('fill', '#64748b')
400
+ .attr('class', 'gauge-min-max')
401
+ .style('font-size', '12px')
402
+ .text(tick.toLocaleString());
403
+ });
404
+ }
405
+ /**
406
+ * Draws the value and label text in the center
407
+ */
408
+ drawValueDisplay(chartGroup, value, label, radius, valueColor, valueFontSize, labelFontSize) {
409
+ const valueGroup = chartGroup.append('g').attr('class', 'value-display');
410
+ // Value display - moved closer to the dial
411
+ valueGroup
412
+ .append('text')
413
+ .attr('x', 0)
414
+ .attr('y', radius * 0.3)
415
+ .attr('text-anchor', 'middle')
416
+ .attr('dominant-baseline', 'middle')
417
+ .attr('fill', valueColor)
418
+ .attr('class', 'gauge-value')
419
+ .style('font-size', `${valueFontSize}px`)
420
+ .text(value.toLocaleString());
421
+ // Label display (if provided), positioned closer to the value
422
+ if (label) {
423
+ valueGroup
424
+ .append('text')
425
+ .attr('x', 0)
426
+ .attr('y', radius * 0.45)
427
+ .attr('text-anchor', 'middle')
428
+ .attr('dominant-baseline', 'middle')
429
+ .attr('fill', '#64748b')
430
+ .attr('class', 'gauge-label')
431
+ .style('font-size', `${labelFontSize}px`)
432
+ .text(label);
433
+ }
434
+ }
435
+ /**
436
+ * Draws the dial/needle pointing to the current value with animation
437
+ */
438
+ drawDial(chartGroup, radius, value, minValue, maxValue, valueColor, animationDuration) {
439
+ // Clamp value to min/max range
440
+ const clampedValue = Math.max(minValue, Math.min(maxValue, value));
441
+ // Calculate angle for needle based on value
442
+ const angle = this.scaleValueToAngle(clampedValue, minValue, maxValue);
443
+ const angleInDegrees = this.radiansToDegrees(angle - Math.PI / 2);
444
+ // Create a group for the needle
445
+ const dialGroup = chartGroup.append('g').attr('class', 'dial');
446
+ // Draw the needle with initial position at minimum value
447
+ const needlePath = dialGroup
448
+ .append('path')
449
+ .attr('d', this.createNeedlePath(radius))
450
+ .attr('fill', valueColor)
451
+ .attr('class', 'gauge-needle')
452
+ .attr('transform', `rotate(${this.radiansToDegrees(-Math.PI)})`); // Start at -180 degrees (left)
453
+ // Add a center circle
454
+ dialGroup
455
+ .append('circle')
456
+ .attr('cx', 0)
457
+ .attr('cy', 0)
458
+ .attr('r', radius * 0.08)
459
+ .attr('fill', valueColor)
460
+ .attr('stroke', '#fff')
461
+ .attr('stroke-width', 2);
462
+ // Add a smaller white center
463
+ dialGroup
464
+ .append('circle')
465
+ .attr('cx', 0)
466
+ .attr('cy', 0)
467
+ .attr('r', radius * 0.03)
468
+ .attr('fill', '#fff');
469
+ // Get easing function
470
+ const easingFunction = this.getEasingFunction(this.options()?.animationEasing);
471
+ // Animate the needle from min to current value
472
+ needlePath
473
+ .transition()
474
+ .duration(animationDuration)
475
+ .ease(easingFunction)
476
+ .attr('transform', `rotate(${angleInDegrees})`);
477
+ }
478
+ /**
479
+ * Gets the appropriate D3 easing function based on the option string
480
+ */
481
+ getEasingFunction(easing) {
482
+ switch (easing) {
483
+ case 'linear':
484
+ return this.d3.easeLinear;
485
+ case 'ease':
486
+ return this.d3.easePolyInOut;
487
+ case 'ease-in':
488
+ return this.d3.easePolyIn;
489
+ case 'ease-out':
490
+ return this.d3.easePolyOut;
491
+ case 'ease-in-out':
492
+ return this.d3.easePolyInOut;
493
+ case 'cubic':
494
+ return this.d3.easeCubic;
495
+ case 'cubic-in':
496
+ return this.d3.easeCubicIn;
497
+ case 'cubic-out':
498
+ return this.d3.easeCubicOut;
499
+ case 'cubic-in-out':
500
+ return this.d3.easeCubicInOut;
501
+ default:
502
+ return this.d3.easeCubicOut; // Default easing
503
+ }
504
+ }
505
+ /**
506
+ * Creates a path string for the needle
507
+ */
508
+ createNeedlePath(radius) {
509
+ const needleLength = radius * 0.8;
510
+ const needleWidth = radius * 0.06;
511
+ return `M 0,${needleWidth / 2} L ${needleLength},0 L 0,${-needleWidth / 2} Z`;
512
+ }
513
+ /**
514
+ * Scales a value to an angle (in radians)
515
+ */
516
+ scaleValueToAngle(value, min, max) {
517
+ const valueRange = max - min;
518
+ const angleRange = Math.PI;
519
+ return ((value - min) / valueRange) * angleRange - Math.PI / 2;
520
+ }
521
+ /**
522
+ * Scales a value to an angle for color arcs (in radians)
523
+ */
524
+ scaleValueToColorAngle(value, min, max) {
525
+ const valueRange = max - min;
526
+ const angleRange = Math.PI; // 180 degrees in radians
527
+ return ((value - min) / valueRange) * angleRange - Math.PI / 2;
528
+ }
529
+ /**
530
+ * Converts radians to degrees
531
+ */
532
+ radiansToDegrees(radians) {
533
+ return radians * (180 / Math.PI);
534
+ }
535
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: AXGaugeChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
536
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.2.9", type: AXGaugeChartComponent, isStandalone: true, selector: "ax-gauge-chart", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ax-gauge-chart\" #chartContainer></div>\n", styles: [":host{display:block;width:100%;height:100%;min-height:200px}.ax-gauge-chart{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:visible}svg{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}.gauge-arc{transition:stroke-dashoffset 1s ease}.gauge-label{font-family:var(--ax-font-family, system-ui, sans-serif);font-weight:600}.gauge-value{font-family:var(--ax-font-family, system-ui, sans-serif);font-weight:700}.gauge-min-max{font-family:var(--ax-font-family, system-ui, sans-serif);opacity:.7}.gauge-needle{transform-origin:center;transition:transform 1s cubic-bezier(.34,1.56,.64,1)}.value-display{text-anchor:middle;dominant-baseline:middle}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
537
+ }
538
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.9", ngImport: i0, type: AXGaugeChartComponent, decorators: [{
539
+ type: Component,
540
+ args: [{ selector: 'ax-gauge-chart', standalone: true, imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-gauge-chart\" #chartContainer></div>\n", styles: [":host{display:block;width:100%;height:100%;min-height:200px}.ax-gauge-chart{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:visible}svg{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}.gauge-arc{transition:stroke-dashoffset 1s ease}.gauge-label{font-family:var(--ax-font-family, system-ui, sans-serif);font-weight:600}.gauge-value{font-family:var(--ax-font-family, system-ui, sans-serif);font-weight:700}.gauge-min-max{font-family:var(--ax-font-family, system-ui, sans-serif);opacity:.7}.gauge-needle{transform-origin:center;transition:transform 1s cubic-bezier(.34,1.56,.64,1)}.value-display{text-anchor:middle;dominant-baseline:middle}\n"] }]
541
+ }], ctorParameters: () => [] });
542
+
543
+ /**
544
+ * Generated bundle index. Do not edit.
545
+ */
546
+
547
+ export { AXGaugeChartComponent, AXGaugeChartDefaultConfig, AX_GAUGE_CHART_CONFIG, gaugeChartConfig };
548
+ //# sourceMappingURL=acorex-charts-gauge-chart.mjs.map