drizzle-cube 0.6.0 → 0.6.1
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/dist/adapters/express/index.cjs +1 -1
- package/dist/adapters/{adapters/express → express}/index.d.ts +3 -3
- package/dist/adapters/express/index.js +1 -1
- package/dist/adapters/fastify/index.cjs +1 -1
- package/dist/adapters/{adapters/fastify → fastify}/index.d.ts +3 -3
- package/dist/adapters/fastify/index.js +1 -1
- package/dist/adapters/{adapters/hono → hono}/agent-handler.d.ts +3 -3
- package/dist/adapters/hono/index.cjs +1 -1
- package/dist/adapters/{adapters/hono → hono}/index.d.ts +4 -4
- package/dist/adapters/hono/index.js +1 -1
- package/dist/adapters/{adapters/hono → hono}/mcp-handler.d.ts +2 -2
- package/dist/adapters/{adapters/locale.d.ts → locale.d.ts} +1 -1
- package/dist/adapters/{adapters/mcp-tools-handlers.d.ts → mcp-tools-handlers.d.ts} +2 -2
- package/dist/adapters/mcp-tools.cjs +1 -1
- package/dist/adapters/{adapters/mcp-tools.d.ts → mcp-tools.d.ts} +5 -5
- package/dist/adapters/mcp-tools.js +1 -1
- package/dist/adapters/mcp-transport-B7ddET3M.cjs +40 -0
- package/dist/adapters/mcp-transport-Dfuj4j4q.js +591 -0
- package/dist/adapters/{adapters/mcp-transport.d.ts → mcp-transport.d.ts} +3 -3
- package/dist/adapters/nextjs/index.cjs +1 -1
- package/dist/adapters/{adapters/nextjs → nextjs}/index.d.ts +3 -3
- package/dist/adapters/nextjs/index.js +1 -1
- package/dist/adapters/{adapters/nextjs → nextjs}/mcp-handler.d.ts +2 -2
- package/dist/adapters/{adapters/types.d.ts → types.d.ts} +1 -1
- package/dist/adapters/{adapters/utils.d.ts → utils.d.ts} +16 -16
- package/dist/cli/index.cjs +2 -2
- package/dist/client/adapters/adapterRegistry.d.ts +2 -2
- package/dist/client/adapters/flowModeAdapter.d.ts +2 -2
- package/dist/client/adapters/funnelModeAdapter.d.ts +2 -2
- package/dist/client/adapters/index.d.ts +9 -9
- package/dist/client/adapters/modeAdapter.d.ts +1 -1
- package/dist/client/adapters/queryModeAdapter.d.ts +3 -3
- package/dist/client/adapters/retentionModeAdapter.d.ts +2 -2
- package/dist/client/charts/ChartLoader.d.ts +2 -2
- package/dist/client/charts/chartComponentRegistry.d.ts +1 -1
- package/dist/client/charts/chartConfigHelpers.d.ts +1 -1
- package/dist/client/charts/chartConfigRegistry.d.ts +1 -1
- package/dist/client/charts/chartPlugin.d.ts +3 -3
- package/dist/client/charts/lazyChartConfigRegistry.d.ts +2 -2
- package/dist/client/charts.d.ts +8 -8
- package/dist/client/chunks/DashboardEditModal-okVfH8ZK.js.map +1 -1
- package/dist/client/chunks/RetentionCombinedChart-BgbDhsPz.js.map +1 -1
- package/dist/client/chunks/RetentionHeatmap-DjXZaTPq.js.map +1 -1
- package/dist/client/chunks/analysis-builder-DB88FojM.js.map +1 -1
- package/dist/client/chunks/analysis-builder-shared-BVK4TYfR.js.map +1 -1
- package/dist/client/chunks/chart-activity-grid-DX0SJbxs.js.map +1 -1
- package/dist/client/chunks/chart-area-of01_62R.js.map +1 -1
- package/dist/client/chunks/chart-bar-BqelQE_I.js.map +1 -1
- package/dist/client/chunks/chart-box-plot-kkBixZ27.js.map +1 -1
- package/dist/client/chunks/chart-bubble-B2he1--4.js.map +1 -1
- package/dist/client/chunks/chart-candlestick-DZp19Tzh.js.map +1 -1
- package/dist/client/chunks/chart-config-activity-grid-D_UX4NHC.js.map +1 -1
- package/dist/client/chunks/chart-config-area-BNigHUy8.js.map +1 -1
- package/dist/client/chunks/chart-config-bar-DSO_LRTx.js.map +1 -1
- package/dist/client/chunks/chart-config-box-plot-oW8axV2q.js.map +1 -1
- package/dist/client/chunks/chart-config-bubble-gIoqVyjZ.js.map +1 -1
- package/dist/client/chunks/chart-config-candlestick-N6DchAA3.js.map +1 -1
- package/dist/client/chunks/chart-config-data-table-d7VBY-y_.js.map +1 -1
- package/dist/client/chunks/chart-config-funnel-DEYMcxsD.js.map +1 -1
- package/dist/client/chunks/chart-config-gauge-Dq-_H9UN.js.map +1 -1
- package/dist/client/chunks/chart-config-heat-map-CfRnRNcw.js.map +1 -1
- package/dist/client/chunks/chart-config-kpi-delta-DSQbvqu_.js.map +1 -1
- package/dist/client/chunks/chart-config-kpi-number-EFSWY1We.js.map +1 -1
- package/dist/client/chunks/chart-config-kpi-text-KM3V5X2u.js.map +1 -1
- package/dist/client/chunks/chart-config-line-D6jG8PCH.js.map +1 -1
- package/dist/client/chunks/chart-config-markdown-BtRIe8JN.js.map +1 -1
- package/dist/client/chunks/chart-config-measure-profile-DxRGa-zf.js.map +1 -1
- package/dist/client/chunks/chart-config-pie-BhzW-fdn.js.map +1 -1
- package/dist/client/chunks/chart-config-radar-D7REP1q_.js.map +1 -1
- package/dist/client/chunks/chart-config-radial-bar-Byx6qOPU.js.map +1 -1
- package/dist/client/chunks/chart-config-sankey-BTnWA7EW.js.map +1 -1
- package/dist/client/chunks/chart-config-scatter-DSYTjwRb.js.map +1 -1
- package/dist/client/chunks/chart-config-sunburst-Bwjtdf7X.js.map +1 -1
- package/dist/client/chunks/chart-config-tree-map-DVrvf3yQ.js.map +1 -1
- package/dist/client/chunks/chart-config-waterfall-DmFeQdIk.js.map +1 -1
- package/dist/client/chunks/chart-data-table-CKauQXme.js.map +1 -1
- package/dist/client/chunks/chart-funnel-CToKSBaL.js.map +1 -1
- package/dist/client/chunks/chart-gauge-DTVezSk7.js.map +1 -1
- package/dist/client/chunks/chart-heat-map-DTuUl0BR.js.map +1 -1
- package/dist/client/chunks/chart-kpi-delta-Dn-jjO-2.js.map +1 -1
- package/dist/client/chunks/chart-kpi-number-P_WBiO5S.js.map +1 -1
- package/dist/client/chunks/chart-kpi-text-C-Y4eb5H.js.map +1 -1
- package/dist/client/chunks/chart-line-DKgrQA6c.js.map +1 -1
- package/dist/client/chunks/chart-markdown-CV75S_zL.js.map +1 -1
- package/dist/client/chunks/chart-measure-profile-Ket8fJyf.js.map +1 -1
- package/dist/client/chunks/chart-pie-ByieAX5H.js.map +1 -1
- package/dist/client/chunks/chart-radar-CiSwHWp4.js.map +1 -1
- package/dist/client/chunks/chart-radial-bar-BF6Mhl6f.js.map +1 -1
- package/dist/client/chunks/chart-sankey-DjrvKw6K.js.map +1 -1
- package/dist/client/chunks/chart-scatter-Dd9WFqnS.js.map +1 -1
- package/dist/client/chunks/chart-sunburst-leGpuDj7.js.map +1 -1
- package/dist/client/chunks/chart-tree-map-Ceq5sLZA.js.map +1 -1
- package/dist/client/chunks/chart-waterfall-DyvQReN5.js.map +1 -1
- package/dist/client/chunks/charts-core-DaXSt1Dd.js.map +1 -1
- package/dist/client/chunks/charts-loader-DdTeCeNo.js.map +1 -1
- package/dist/client/chunks/retention-ChW9jYdy.js.map +1 -1
- package/dist/client/chunks/schema-visualization-Cb_E9_Gd.js.map +1 -1
- package/dist/client/chunks/useDirtyStateTracking-MbpxH_v6.js.map +1 -1
- package/dist/client/chunks/useExplainAI-evnZkjCY.js.map +1 -1
- package/dist/client/chunks/utils-3P6z1vz5.js.map +1 -1
- package/dist/client/client/BatchCoordinator.d.ts +1 -1
- package/dist/client/client/CubeClient.d.ts +1 -1
- package/dist/client/components/AIAssistant/index.d.ts +3 -3
- package/dist/client/components/AIAssistant/utils.d.ts +1 -1
- package/dist/client/components/AgenticNotebook/ChatMessage.d.ts +1 -1
- package/dist/client/components/AgenticNotebook/NotebookCanvas.d.ts +1 -1
- package/dist/client/components/AgenticNotebook/NotebookMarkdownBlock.d.ts +1 -1
- package/dist/client/components/AgenticNotebook/NotebookPortletBlock.d.ts +2 -2
- package/dist/client/components/AgenticNotebook/chatMessageParts.d.ts +1 -1
- package/dist/client/components/AgenticNotebook/index.d.ts +2 -2
- package/dist/client/components/AgenticNotebook/useAgentChatController.d.ts +1 -1
- package/dist/client/components/AgenticNotebook/useNotebookAutosave.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/AnalysisAxisDropZone.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/AnalysisChartConfigPanel.d.ts +4 -4
- package/dist/client/components/AnalysisBuilder/AnalysisDisplayConfigPanel.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/AnalysisFilterGroup.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/AnalysisFilterItem.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/AnalysisFilterSection.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/AnalysisModeErrorBoundary.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/AnalysisQueryPanel.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/AnalysisQueryPanelParts.d.ts +3 -3
- package/dist/client/components/AnalysisBuilder/AnalysisResultsPanel.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/AnalysisTypeSelector.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/BreakdownItemCard.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/BreakdownRow.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/BreakdownSection.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/DisplayOptionControl.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/ExecutionPlanPanel.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/ExecutionPlanPanelParts.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/ExplainAIPanel.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/FieldDetailPanel.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/FieldSearchItem.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/FieldSearchModal.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/FieldSearchResults.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/FilterConfigModal.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/FilterValueInput.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/FlowConfigPanel.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/FlowDepthControls.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/FlowModeContent.d.ts +3 -3
- package/dist/client/components/AnalysisBuilder/FlowVisualizationPicker.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/FunnelBindingKeySelector.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/FunnelConfigPanel.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/FunnelModeContent.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/FunnelStepCard.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/FunnelStepList.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/LegacyBooleanOptions.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/MetricItemCard.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/MetricRow.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/MetricsSection.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/RetentionConfigPanel.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/RetentionModeContent.d.ts +3 -3
- package/dist/client/components/AnalysisBuilder/filterConfigModalUtils.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/hooks/useAnalysisBuilderImperativeHandle.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/hooks/useFieldSearchKeyboard.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/index.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/types.d.ts +28 -28
- package/dist/client/components/AnalysisBuilder/utils/axisConfigUtils.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/utils/executionPlanMarkdown.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/utils/fieldUtils.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/utils/filterUtils.d.ts +1 -1
- package/dist/client/components/AnalysisBuilder/utils/index.d.ts +6 -6
- package/dist/client/components/AnalysisBuilder/utils/queryUtils.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/utils/recentFieldsUtils.d.ts +2 -2
- package/dist/client/components/AnalysisBuilder/utils/shareStateUtils.d.ts +16 -16
- package/dist/client/components/AnalysisBuilder/utils/storageUtils.d.ts +2 -2
- package/dist/client/components/AnalysisBuilderLazy.d.ts +1 -1
- package/dist/client/components/AnalyticsDashboard.d.ts +1 -1
- package/dist/client/components/AnalyticsPortlet.d.ts +1 -1
- package/dist/client/components/ChartTypeSelector.d.ts +2 -2
- package/dist/client/components/DashboardFilterPanel.d.ts +1 -1
- package/dist/client/components/DashboardFilters/CompactFilterBar.d.ts +1 -1
- package/dist/client/components/DashboardFilters/CompactFilterBarParts.d.ts +1 -1
- package/dist/client/components/DashboardFilters/DashboardFilterConfigModal.d.ts +2 -2
- package/dist/client/components/DashboardFilters/DashboardFilterConfigModalParts.d.ts +2 -2
- package/dist/client/components/DashboardFilters/DashboardFilterItem.d.ts +2 -2
- package/dist/client/components/DashboardFilters/DashboardFilterValueInput.d.ts +2 -2
- package/dist/client/components/DashboardFilters/EditModeFilterList.d.ts +1 -1
- package/dist/client/components/DashboardFilters/FilterChip.d.ts +1 -1
- package/dist/client/components/DashboardFilters/FilterEditModal.d.ts +2 -2
- package/dist/client/components/DashboardFilters/FilterValuePopover.d.ts +1 -1
- package/dist/client/components/DashboardFilters/ReadOnlyFilterList.d.ts +2 -2
- package/dist/client/components/DashboardFilters/dashboardFilterConfigModalUtils.d.ts +2 -2
- package/dist/client/components/DashboardFilters/useCompactFilterBar.d.ts +1 -1
- package/dist/client/components/DashboardFilters/useDashboardFilterConfigModal.d.ts +6 -6
- package/dist/client/components/DashboardFilters/useDateRangeState.d.ts +2 -2
- package/dist/client/components/DashboardFilters/useFilterValueFetch.d.ts +1 -1
- package/dist/client/components/DashboardGrid.d.ts +2 -2
- package/dist/client/components/DashboardPortletCard.d.ts +1 -1
- package/dist/client/components/DebugModal.d.ts +2 -2
- package/dist/client/components/DrillBreadcrumb.d.ts +1 -1
- package/dist/client/components/DrillMenu.d.ts +1 -1
- package/dist/client/components/FloatingEditToolbar.d.ts +1 -1
- package/dist/client/components/MobileStackedLayout.d.ts +2 -2
- package/dist/client/components/PortletAnalysisModal.d.ts +1 -1
- package/dist/client/components/PortletContainer.d.ts +1 -1
- package/dist/client/components/PortletFilterConfigModal.d.ts +1 -1
- package/dist/client/components/RowManagedLayout.d.ts +1 -1
- package/dist/client/components/SchemaVisualization/CubeNode.d.ts +1 -1
- package/dist/client/components/SchemaVisualization/FieldDetailPanel.d.ts +1 -1
- package/dist/client/components/SchemaVisualization/RelationshipEdge.d.ts +1 -1
- package/dist/client/components/SchemaVisualization/SchemaVisualizationLazy.d.ts +1 -1
- package/dist/client/components/TextPortletModal.d.ts +2 -2
- package/dist/client/components/analyticsPortlet/PortletChart.d.ts +4 -4
- package/dist/client/components/analyticsPortlet/PortletChartView.d.ts +5 -5
- package/dist/client/components/analyticsPortlet/PortletStates.d.ts +2 -2
- package/dist/client/components/analyticsPortlet/parsePortletQuery.d.ts +3 -3
- package/dist/client/components/analyticsPortlet/portletRenderState.d.ts +3 -3
- package/dist/client/components/analyticsPortlet/usePortletDebugData.d.ts +4 -4
- package/dist/client/components/analyticsPortlet/usePortletDrillState.d.ts +2 -2
- package/dist/client/components/analyticsPortlet/usePortletQueryResults.d.ts +4 -4
- package/dist/client/components/charts/ActivityGridChart.config.d.ts +1 -1
- package/dist/client/components/charts/ActivityGridChart.d.ts +1 -1
- package/dist/client/components/charts/ActivityGridChart.helpers.d.ts +1 -1
- package/dist/client/components/charts/ActivityGridChart.render.d.ts +3 -3
- package/dist/client/components/charts/AreaChart.config.d.ts +1 -1
- package/dist/client/components/charts/AreaChart.d.ts +1 -1
- package/dist/client/components/charts/AxisFormatControls.d.ts +1 -1
- package/dist/client/components/charts/BarChart.config.d.ts +1 -1
- package/dist/client/components/charts/BarChart.d.ts +1 -1
- package/dist/client/components/charts/BarChart.helpers.d.ts +1 -1
- package/dist/client/components/charts/BarSeries.d.ts +2 -2
- package/dist/client/components/charts/BoxPlotChart.config.d.ts +1 -1
- package/dist/client/components/charts/BoxPlotChart.d.ts +1 -1
- package/dist/client/components/charts/BubbleChart.config.d.ts +1 -1
- package/dist/client/components/charts/BubbleChart.d.ts +1 -1
- package/dist/client/components/charts/BubbleChart.helpers.d.ts +1 -1
- package/dist/client/components/charts/BubbleChart.render.d.ts +2 -2
- package/dist/client/components/charts/CandlestickChart.config.d.ts +1 -1
- package/dist/client/components/charts/CandlestickChart.d.ts +1 -1
- package/dist/client/components/charts/DataTable.config.d.ts +1 -1
- package/dist/client/components/charts/DataTable.d.ts +1 -1
- package/dist/client/components/charts/FunnelChart.config.d.ts +1 -1
- package/dist/client/components/charts/FunnelChart.d.ts +1 -1
- package/dist/client/components/charts/FunnelChart.helpers.d.ts +2 -2
- package/dist/client/components/charts/FunnelViews.d.ts +2 -2
- package/dist/client/components/charts/GaugeChart.config.d.ts +1 -1
- package/dist/client/components/charts/GaugeChart.d.ts +1 -1
- package/dist/client/components/charts/HeatMapCanvas.d.ts +1 -1
- package/dist/client/components/charts/HeatMapChart.config.d.ts +1 -1
- package/dist/client/components/charts/HeatMapChart.d.ts +1 -1
- package/dist/client/components/charts/HeatMapChart.helpers.d.ts +1 -1
- package/dist/client/components/charts/KpiDelta.config.d.ts +1 -1
- package/dist/client/components/charts/KpiDelta.d.ts +1 -1
- package/dist/client/components/charts/KpiDelta.helpers.d.ts +1 -1
- package/dist/client/components/charts/KpiNumber.config.d.ts +1 -1
- package/dist/client/components/charts/KpiNumber.d.ts +1 -1
- package/dist/client/components/charts/KpiNumber.helpers.d.ts +1 -1
- package/dist/client/components/charts/KpiText.config.d.ts +1 -1
- package/dist/client/components/charts/KpiText.d.ts +1 -1
- package/dist/client/components/charts/LineChart.config.d.ts +1 -1
- package/dist/client/components/charts/LineChart.d.ts +1 -1
- package/dist/client/components/charts/MarkdownChart.config.d.ts +1 -1
- package/dist/client/components/charts/MarkdownChart.d.ts +1 -1
- package/dist/client/components/charts/MeasureProfileChart.config.d.ts +1 -1
- package/dist/client/components/charts/MeasureProfileChart.d.ts +1 -1
- package/dist/client/components/charts/PieChart.config.d.ts +1 -1
- package/dist/client/components/charts/PieChart.d.ts +1 -1
- package/dist/client/components/charts/RadarChart.config.d.ts +1 -1
- package/dist/client/components/charts/RadarChart.d.ts +1 -1
- package/dist/client/components/charts/RadialBarChart.config.d.ts +1 -1
- package/dist/client/components/charts/RadialBarChart.d.ts +1 -1
- package/dist/client/components/charts/RetentionCombinedChart.config.d.ts +1 -1
- package/dist/client/components/charts/RetentionCombinedChart.d.ts +1 -1
- package/dist/client/components/charts/RetentionHeatmap.config.d.ts +1 -1
- package/dist/client/components/charts/RetentionHeatmap.d.ts +1 -1
- package/dist/client/components/charts/SankeyChart.config.d.ts +1 -1
- package/dist/client/components/charts/SankeyChart.d.ts +1 -1
- package/dist/client/components/charts/ScatterChart.config.d.ts +1 -1
- package/dist/client/components/charts/ScatterChart.d.ts +1 -1
- package/dist/client/components/charts/ScatterChart.helpers.d.ts +2 -2
- package/dist/client/components/charts/ScatterSeries.d.ts +2 -2
- package/dist/client/components/charts/ScatterTooltip.d.ts +2 -2
- package/dist/client/components/charts/SunburstChart.config.d.ts +1 -1
- package/dist/client/components/charts/SunburstChart.d.ts +1 -1
- package/dist/client/components/charts/TreeMapChart.config.d.ts +1 -1
- package/dist/client/components/charts/TreeMapChart.d.ts +1 -1
- package/dist/client/components/charts/TreeMapChart.helpers.d.ts +1 -1
- package/dist/client/components/charts/TreeMapContent.d.ts +3 -3
- package/dist/client/components/charts/TreeMapLegend.d.ts +2 -2
- package/dist/client/components/charts/WaterfallChart.config.d.ts +1 -1
- package/dist/client/components/charts/WaterfallChart.d.ts +1 -1
- package/dist/client/components/charts/cartesianChartHelpers.d.ts +1 -1
- package/dist/client/components/charts/chartAxisResolution.d.ts +1 -1
- package/dist/client/components/charts/chartScaffolding.d.ts +1 -1
- package/dist/client/components/charts/gaugeChartHelpers.d.ts +1 -1
- package/dist/client/components/charts/index.d.ts +21 -21
- package/dist/client/components/charts/kpiTextHelpers.d.ts +1 -1
- package/dist/client/components/charts/radialBarChartHelpers.d.ts +1 -1
- package/dist/client/components/dashboard/DashboardContext.d.ts +6 -6
- package/dist/client/components/dashboard/DashboardCoordinator.d.ts +1 -1
- package/dist/client/components/dashboard/DashboardProvider.d.ts +1 -1
- package/dist/client/components/dashboard/dashboardGridUtils.d.ts +1 -1
- package/dist/client/components/dashboard/index.d.ts +7 -7
- package/dist/client/components/dashboardPortletCard/FilterFieldChip.d.ts +1 -1
- package/dist/client/components/dashboardPortletCard/PortletCardHeader.d.ts +2 -2
- package/dist/client/components/dashboardPortletCard/cardStyles.d.ts +1 -1
- package/dist/client/components/dashboardPortletCard/filterField.d.ts +1 -1
- package/dist/client/components/dashboardPortletCard/propsEqual.d.ts +2 -2
- package/dist/client/components/dashboardPortletCard/usePortletCardActions.d.ts +1 -1
- package/dist/client/components/shared/DateRangeFilter.d.ts +1 -1
- package/dist/client/components/shared/FilterBuilder.d.ts +1 -1
- package/dist/client/components/shared/FilterGroup.d.ts +1 -1
- package/dist/client/components/shared/FilterItem.d.ts +1 -1
- package/dist/client/components/shared/FilterValueSelector.d.ts +1 -1
- package/dist/client/components/shared/dateRangeUtils.d.ts +1 -1
- package/dist/client/components/shared/filterItem/FilterDateRangeSelector.d.ts +1 -1
- package/dist/client/components/shared/filterItem/FilterFieldDropdown.d.ts +1 -1
- package/dist/client/components/shared/filterItem/dateRangeSync.d.ts +1 -1
- package/dist/client/components/shared/filterItem/fieldVisuals.d.ts +1 -1
- package/dist/client/components/shared/filterValueSelector/useFilterValueSelectorState.d.ts +2 -2
- package/dist/client/components/shared/queryFieldUtils.d.ts +2 -2
- package/dist/client/components/shared/types.d.ts +8 -8
- package/dist/client/components/shared/utils.d.ts +7 -7
- package/dist/client/components.d.ts +11 -11
- package/dist/client/hooks/agentChatStream.d.ts +1 -1
- package/dist/client/hooks/dashboard/layoutUtils.d.ts +1 -1
- package/dist/client/hooks/dashboard/useDashboardController.d.ts +2 -2
- package/dist/client/hooks/dashboard/useGridLayoutEngine.d.ts +1 -1
- package/dist/client/hooks/dashboard/useRowLayoutEngine.d.ts +1 -1
- package/dist/client/hooks/drillNavigation.d.ts +2 -2
- package/dist/client/hooks/queries/index.d.ts +9 -9
- package/dist/client/hooks/queries/useCubeLoadQuery.d.ts +2 -2
- package/dist/client/hooks/queries/useCubeMetaQuery.d.ts +2 -2
- package/dist/client/hooks/queries/useDryRunQuery.d.ts +2 -2
- package/dist/client/hooks/queries/useExplainAI.d.ts +1 -1
- package/dist/client/hooks/queries/useExplainQuery.d.ts +1 -1
- package/dist/client/hooks/queries/useFlowQuery.d.ts +1 -1
- package/dist/client/hooks/queries/useFunnelQuery.d.ts +1 -1
- package/dist/client/hooks/queries/useMultiCubeLoadQuery.d.ts +1 -1
- package/dist/client/hooks/queries/useRetentionQuery.d.ts +1 -1
- package/dist/client/hooks/useAgentChat.d.ts +1 -1
- package/dist/client/hooks/useAnalysisAI.d.ts +4 -4
- package/dist/client/hooks/useAnalysisBuilderHook.d.ts +27 -27
- package/dist/client/hooks/useAnalysisChartDefaults.d.ts +4 -4
- package/dist/client/hooks/useAnalysisCombinedFields.d.ts +2 -2
- package/dist/client/hooks/useAnalysisInitialization.d.ts +1 -1
- package/dist/client/hooks/useAnalysisQueryBuilder.d.ts +3 -3
- package/dist/client/hooks/useAnalysisQueryExecution.d.ts +7 -7
- package/dist/client/hooks/useAnalysisShare.d.ts +1 -1
- package/dist/client/hooks/useAnalysisUIState.d.ts +2 -2
- package/dist/client/hooks/useDashboardHook.d.ts +2 -2
- package/dist/client/hooks/useDataBrowser.d.ts +5 -5
- package/dist/client/hooks/useDrillInteraction.d.ts +1 -1
- package/dist/client/hooks/useTheme.d.ts +1 -1
- package/dist/client/hooks/useTranslation.d.ts +1 -1
- package/dist/client/hooks.d.ts +11 -11
- package/dist/client/icons/defaultIcons.d.ts +1 -1
- package/dist/client/icons/index.d.ts +3 -3
- package/dist/client/icons/registry.d.ts +1 -1
- package/dist/client/index.d.ts +74 -74
- package/dist/client/index.js.map +1 -1
- package/dist/client/providers/CubeApiProvider.d.ts +3 -3
- package/dist/client/providers/CubeFeaturesProvider.d.ts +1 -1
- package/dist/client/providers/CubeMetaContext.d.ts +1 -1
- package/dist/client/providers/CubeProvider.d.ts +7 -7
- package/dist/client/providers/I18nProvider.d.ts +1 -1
- package/dist/client/providers.d.ts +4 -4
- package/dist/client/schema.d.ts +2 -2
- package/dist/client/shared/chartConfigBuilders.d.ts +2 -2
- package/dist/client/shared/chartDefaults.d.ts +4 -4
- package/dist/client/shared/components/QueryAnalysisPanel.d.ts +1 -1
- package/dist/client/shared/components/QueryAnalysisPanel.sections.d.ts +1 -1
- package/dist/client/shared/index.d.ts +7 -7
- package/dist/client/shared/queryTransforms.d.ts +1 -1
- package/dist/client/shared/types.d.ts +1 -1
- package/dist/client/shared/utils.d.ts +3 -3
- package/dist/client/stores/analysisBuilderStore.d.ts +9 -9
- package/dist/client/stores/dashboardStore.d.ts +3 -3
- package/dist/client/stores/dataBrowserStore.d.ts +1 -1
- package/dist/client/stores/notebookStore.d.ts +1 -1
- package/dist/client/stores/optionsToAnalysisConfig.d.ts +4 -4
- package/dist/client/stores/slices/coreSlice.d.ts +3 -3
- package/dist/client/stores/slices/flowSlice.d.ts +3 -3
- package/dist/client/stores/slices/funnelSlice.d.ts +3 -3
- package/dist/client/stores/slices/index.d.ts +7 -7
- package/dist/client/stores/slices/querySlice.d.ts +3 -3
- package/dist/client/stores/slices/retentionSlice.d.ts +3 -3
- package/dist/client/stores/slices/uiSlice.d.ts +2 -2
- package/dist/client/types/analysisConfig.d.ts +4 -4
- package/dist/client/types/drill.d.ts +5 -5
- package/dist/client/types/flow.d.ts +2 -2
- package/dist/client/types/funnel.d.ts +1 -1
- package/dist/client/types/retention.d.ts +2 -2
- package/dist/client/types.d.ts +12 -12
- package/dist/client/utils/axisValueFormatting.d.ts +1 -1
- package/dist/client/utils/chartUtils.d.ts +1 -1
- package/dist/client/utils/configMigration.d.ts +2 -2
- package/dist/client/utils/drillQueryBuilder.d.ts +2 -2
- package/dist/client/utils/exportXlsx.d.ts +2 -2
- package/dist/client/utils/filterUtils.d.ts +1 -1
- package/dist/client/utils/funnelExecution.d.ts +2 -2
- package/dist/client/utils/funnelValidation.d.ts +2 -2
- package/dist/client/utils/index.d.ts +10 -10
- package/dist/client/utils/joinReachability.d.ts +1 -1
- package/dist/client/utils/multiQueryUtils.d.ts +1 -1
- package/dist/client/utils/multiQueryValidation.d.ts +1 -1
- package/dist/client/utils/pivotUtils.d.ts +1 -1
- package/dist/client/utils/shareUtils.d.ts +1 -1
- package/dist/client/utils/thumbnail.d.ts +1 -1
- package/dist/client/utils.d.ts +2 -2
- package/dist/mcp-app/mcp-app.html +33 -33
- package/dist/server/{server/adapters → adapters}/base-adapter.d.ts +1 -1
- package/dist/server/{server/adapters → adapters}/databend-adapter.d.ts +2 -2
- package/dist/server/{server/adapters → adapters}/duckdb-adapter.d.ts +2 -2
- package/dist/server/{server/adapters → adapters}/mysql-adapter.d.ts +2 -2
- package/dist/server/{server/adapters → adapters}/postgres-adapter.d.ts +2 -2
- package/dist/server/{server/adapters → adapters}/singlestore-adapter.d.ts +1 -1
- package/dist/server/{server/adapters → adapters}/snowflake-adapter.d.ts +2 -2
- package/dist/server/{server/adapters → adapters}/sqlite-adapter.d.ts +2 -2
- package/dist/server/{server/adapters → adapters}/window-function-builder.d.ts +1 -1
- package/dist/server/{server/agent → agent}/handler-steps.d.ts +3 -3
- package/dist/server/{server/agent → agent}/handler.d.ts +3 -3
- package/dist/server/agent/index.d.ts +11 -0
- package/dist/server/{server/agent → agent}/providers/anthropic.d.ts +1 -1
- package/dist/server/{server/agent → agent}/providers/factory.d.ts +1 -1
- package/dist/server/{server/agent → agent}/providers/google.d.ts +1 -1
- package/dist/server/agent/providers/index.d.ts +6 -0
- package/dist/server/{server/agent → agent}/providers/openai.d.ts +1 -1
- package/dist/server/{server/agent → agent}/system-prompt.d.ts +1 -1
- package/dist/server/{server/agent → agent}/tools.d.ts +4 -4
- package/dist/server/{server/agent → agent}/types.d.ts +1 -1
- package/dist/server/{server/ai → ai}/discovery-helpers.d.ts +1 -1
- package/dist/server/{server/ai → ai}/discovery.d.ts +2 -2
- package/dist/server/{server/ai → ai}/index.d.ts +6 -6
- package/dist/server/{server/ai → ai}/suggestion-helpers.d.ts +1 -1
- package/dist/server/{server/ai → ai}/suggestion.d.ts +2 -2
- package/dist/server/{server/ai → ai}/validation-helpers.d.ts +4 -4
- package/dist/server/{server/ai → ai}/validation.d.ts +2 -2
- package/dist/server/{server/builders → builders}/analysis-utils.d.ts +1 -1
- package/dist/server/{server/builders → builders}/comparison-query-builder.d.ts +2 -2
- package/dist/server/{server/builders → builders}/cte-builder.d.ts +2 -2
- package/dist/server/{server/builders → builders}/date-time-builder.d.ts +2 -2
- package/dist/server/{server/builders → builders}/date-time-helpers.d.ts +1 -1
- package/dist/server/{server/builders → builders}/filter-builder.d.ts +3 -3
- package/dist/server/{server/builders → builders}/filter-operators.d.ts +3 -3
- package/dist/server/{server/builders → builders}/flow-query-builder.d.ts +4 -4
- package/dist/server/{server/builders → builders}/funnel-query-builder.d.ts +3 -3
- package/dist/server/{server/builders → builders}/group-by-builder.d.ts +2 -2
- package/dist/server/builders/index.d.ts +13 -0
- package/dist/server/{server/builders → builders}/measure-builder.d.ts +3 -3
- package/dist/server/{server/builders → builders}/retention-query-builder.d.ts +3 -3
- package/dist/server/cache-providers/index.d.ts +5 -0
- package/dist/server/{server/cache-providers → cache-providers}/memory.d.ts +1 -1
- package/dist/server/{server/cache-utils.d.ts → cache-utils.d.ts} +1 -1
- package/dist/server/{server/compiler-metadata.d.ts → compiler-metadata.d.ts} +1 -1
- package/dist/server/{server/compiler.d.ts → compiler.d.ts} +3 -3
- package/dist/server/{server/cube-utils.d.ts → cube-utils.d.ts} +1 -1
- package/dist/server/{server/database-utils.d.ts → database-utils.d.ts} +1 -1
- package/dist/server/{server/execution → execution}/annotation-builder.d.ts +1 -1
- package/dist/server/{server/execution → execution}/filter-cache-preloader.d.ts +3 -3
- package/dist/server/{server/execution → execution}/mode-router.d.ts +5 -5
- package/dist/server/{server/execution → execution}/query-result-cache.d.ts +1 -1
- package/dist/server/{server/execution → execution}/result-post-processor.d.ts +2 -2
- package/dist/server/{server/executor.d.ts → executor.d.ts} +3 -3
- package/dist/server/{server/executors → executors}/base-executor.d.ts +2 -2
- package/dist/server/{server/executors → executors}/databend-executor.d.ts +2 -2
- package/dist/server/{server/executors → executors}/duckdb-executor.d.ts +2 -2
- package/dist/server/{server/executors → executors}/index.d.ts +9 -9
- package/dist/server/{server/executors → executors}/mysql-executor.d.ts +2 -2
- package/dist/server/{server/executors → executors}/postgres-executor.d.ts +2 -2
- package/dist/server/{server/executors → executors}/singlestore-executor.d.ts +2 -2
- package/dist/server/{server/executors → executors}/snowflake-executor.d.ts +2 -2
- package/dist/server/{server/executors → executors}/sqlite-executor.d.ts +2 -2
- package/dist/server/{server/explain → explain}/databend-parser.d.ts +1 -1
- package/dist/server/{server/explain → explain}/duckdb-parser.d.ts +1 -1
- package/dist/server/{server/explain → explain}/explain-tree.d.ts +1 -1
- package/dist/server/explain/index.d.ts +9 -0
- package/dist/server/{server/explain → explain}/mysql-parser.d.ts +1 -1
- package/dist/server/{server/explain → explain}/postgres-parser.d.ts +1 -1
- package/dist/server/{server/explain → explain}/snowflake-parser.d.ts +1 -1
- package/dist/server/{server/explain → explain}/sqlite-parser.d.ts +1 -1
- package/dist/server/{server/filter-cache.d.ts → filter-cache.d.ts} +1 -1
- package/dist/server/{server/gap-filler.d.ts → gap-filler.d.ts} +2 -2
- package/dist/server/index.d.ts +38 -2
- package/dist/server/{server/logical-plan → logical-plan}/cte-planner-helpers.d.ts +1 -1
- package/dist/server/{server/logical-plan → logical-plan}/cte-planner.d.ts +4 -4
- package/dist/server/{server/logical-plan → logical-plan}/filter-propagation.d.ts +1 -1
- package/dist/server/{server/logical-plan → logical-plan}/index.d.ts +11 -11
- package/dist/server/{server/logical-plan → logical-plan}/join-planner.d.ts +3 -3
- package/dist/server/{server/logical-plan → logical-plan}/logical-plan-builder.d.ts +5 -5
- package/dist/server/{server/logical-plan → logical-plan}/logical-planner.d.ts +2 -2
- package/dist/server/{server/logical-plan → logical-plan}/optimiser.d.ts +1 -1
- package/dist/server/{server/logical-plan → logical-plan}/plan-analysis-reporter.d.ts +2 -2
- package/dist/server/{server/logical-plan → logical-plan}/planner-utils.d.ts +2 -2
- package/dist/server/{server/logical-plan → logical-plan}/types.d.ts +3 -3
- package/dist/server/{server/measure-classification.d.ts → measure-classification.d.ts} +1 -1
- package/dist/server/{server/physical-plan → physical-plan}/drizzle-plan-builder.d.ts +5 -5
- package/dist/server/{server/physical-plan → physical-plan}/drizzle-sql-builder.d.ts +3 -3
- package/dist/server/physical-plan/index.d.ts +2 -0
- package/dist/server/{server/physical-plan → physical-plan}/processors/cte-processor.d.ts +2 -2
- package/dist/server/physical-plan/processors/index.d.ts +8 -0
- package/dist/server/{server/physical-plan → physical-plan}/processors/joins-processor.d.ts +2 -2
- package/dist/server/{server/physical-plan → physical-plan}/processors/keys-dedup-processor.d.ts +2 -2
- package/dist/server/{server/physical-plan → physical-plan}/processors/multi-fact-processor.d.ts +2 -2
- package/dist/server/{server/physical-plan → physical-plan}/processors/predicates-processor.d.ts +2 -2
- package/dist/server/{server/physical-plan → physical-plan}/processors/selection-processor.d.ts +2 -2
- package/dist/server/{server/physical-plan → physical-plan}/processors/shared.d.ts +4 -4
- package/dist/server/{server/physical-plan → physical-plan}/processors/window-processor.d.ts +2 -2
- package/dist/server/{server/query-handlers.d.ts → query-handlers.d.ts} +3 -3
- package/dist/server/{server/query-modes.d.ts → query-modes.d.ts} +1 -1
- package/dist/server/{server/query-validator.d.ts → query-validator.d.ts} +1 -1
- package/dist/server/{server/resolvers → resolvers}/calculated-measure-resolver.d.ts +1 -1
- package/dist/server/{server/resolvers → resolvers}/index.d.ts +2 -2
- package/dist/server/{server/resolvers → resolvers}/join-path-resolver.d.ts +1 -1
- package/dist/server/{server/template-substitution.d.ts → template-substitution.d.ts} +1 -1
- package/dist/server/{server/types → types}/cache.d.ts +1 -1
- package/dist/server/{server/types → types}/cube.d.ts +4 -4
- package/dist/server/{server/types → types}/executor.d.ts +2 -2
- package/dist/server/{server/types → types}/flow.d.ts +2 -2
- package/dist/server/{server/types → types}/funnel.d.ts +1 -1
- package/dist/server/types/index.d.ts +11 -0
- package/dist/server/{server/types → types}/metadata.d.ts +1 -1
- package/dist/server/{server/types → types}/query.d.ts +4 -4
- package/dist/server/{server/types → types}/retention.d.ts +1 -1
- package/dist/server/{server/types → types}/utils.d.ts +1 -1
- package/package.json +12 -4
- package/dist/adapters/mcp-transport-Chiaj4ll.cjs +0 -40
- package/dist/adapters/mcp-transport-H96VG3WY.js +0 -591
- package/dist/server/server/agent/index.d.ts +0 -11
- package/dist/server/server/agent/providers/index.d.ts +0 -6
- package/dist/server/server/builders/index.d.ts +0 -13
- package/dist/server/server/cache-providers/index.d.ts +0 -5
- package/dist/server/server/explain/index.d.ts +0 -9
- package/dist/server/server/index.d.ts +0 -38
- package/dist/server/server/physical-plan/index.d.ts +0 -2
- package/dist/server/server/physical-plan/processors/index.d.ts +0 -8
- package/dist/server/server/types/index.d.ts +0 -11
- /package/dist/server/{server/agent → agent}/chart-validation.d.ts +0 -0
- /package/dist/server/{server/agent → agent}/providers/types.d.ts +0 -0
- /package/dist/server/{server/ai → ai}/mcp-prompts.d.ts +0 -0
- /package/dist/server/{server/ai → ai}/query-schema.d.ts +0 -0
- /package/dist/server/{server/ai → ai}/schemas.d.ts +0 -0
- /package/dist/server/{server/executors → executors}/explain-utils.d.ts +0 -0
- /package/dist/server/{server/prompts → prompts}/explain-analysis-prompt.d.ts +0 -0
- /package/dist/server/{server/prompts → prompts}/index.d.ts +0 -0
- /package/dist/server/{server/prompts → prompts}/single-step-prompt.d.ts +0 -0
- /package/dist/server/{server/prompts → prompts}/step0-validation-prompt.d.ts +0 -0
- /package/dist/server/{server/prompts → prompts}/step1-shape-prompt.d.ts +0 -0
- /package/dist/server/{server/prompts → prompts}/step2-complete-prompt.d.ts +0 -0
- /package/dist/server/{server/prompts → prompts}/types.d.ts +0 -0
- /package/dist/server/{server/sql-format.d.ts → sql-format.d.ts} +0 -0
- /package/dist/server/{server/types → types}/analysis.d.ts +0 -0
- /package/dist/server/{server/types → types}/core.d.ts +0 -0
- /package/dist/server/{server/types → types}/validation.d.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-area-of01_62R.js","names":[],"sources":["../../../src/client/components/LoadingIndicator.tsx","../../../src/client/utils/comparisonUtils.ts","../../../src/client/components/charts/ChartContainer.tsx","../../../src/client/components/charts/ChartTooltip.tsx","../../../src/client/components/charts/AngledXAxisTick.tsx","../../../src/client/components/charts/ChartStates.tsx","../../../src/client/components/charts/chartAxisResolution.ts","../../../src/client/utils/targetUtils.ts","../../../src/client/components/charts/chartScaffolding.tsx","../../../src/client/components/charts/cartesianChartHelpers.tsx","../../../src/client/components/charts/AreaChart.tsx"],"sourcesContent":["/**\n * LoadingIndicator Component\n *\n * A centralized, theme-aware loading spinner that uses CSS variables\n * for consistent styling across all drizzle-cube components.\n *\n * Can be overridden at the Dashboard or Portlet level by passing a\n * custom loadingComponent prop.\n */\n\nexport interface LoadingIndicatorProps {\n /** Size variant: 'xs' (12px), 'sm' (24px), 'md' (32px), 'lg' (48px) */\n size?: 'xs' | 'sm' | 'md' | 'lg'\n /** Additional CSS classes */\n className?: string\n}\n\nconst sizeClasses = {\n xs: 'dc:h-3 dc:w-3',\n sm: 'dc:h-6 dc:w-6',\n md: 'dc:h-8 dc:w-8',\n lg: 'dc:h-12 dc:w-12'\n}\n\nexport default function LoadingIndicator({\n size = 'md',\n className = ''\n}: LoadingIndicatorProps) {\n return (\n <div\n className={`dc:animate-spin dc:rounded-full dc:border-b-2 ${sizeClasses[size]} ${className}`}\n style={{ borderBottomColor: 'var(--dc-primary)' }}\n role=\"status\"\n aria-label=\"Loading\"\n />\n )\n}\n","/**\n * Comparison Data Utilities\n * Handles period-over-period comparison data transformation for charts\n *\n * These utilities detect comparison query results (with __periodIndex metadata)\n * and transform them for visualization in either 'separate' or 'overlay' mode.\n */\n\n/**\n * Check if data contains comparison period metadata\n */\nexport function isComparisonData(data: any[]): boolean {\n return data.length > 0 && '__periodIndex' in data[0]\n}\n\n/**\n * Get unique period labels from comparison data\n */\nexport function getPeriodLabels(data: any[]): string[] {\n if (!isComparisonData(data)) return []\n\n const labels = new Set<string>()\n for (const row of data) {\n if (row.__period) {\n labels.add(row.__period)\n }\n }\n return Array.from(labels)\n}\n\n/**\n * Get period indices from comparison data\n */\nexport function getPeriodIndices(data: any[]): number[] {\n if (!isComparisonData(data)) return []\n\n const indices = new Set<number>()\n for (const row of data) {\n if (typeof row.__periodIndex === 'number') {\n indices.add(row.__periodIndex)\n }\n }\n return Array.from(indices).sort((a, b) => a - b)\n}\n\n/**\n * Generate a short label for a period (used in legends)\n * Uses simple \"Current\" / \"Prior\" labels for clarity\n */\nexport function generatePeriodShortLabel(_periodLabel: string, index: number): string {\n return index === 0 ? 'Current' : 'Prior'\n}\n\n/**\n * Transform comparison data for 'separate' mode\n * Returns data with each row having its original measure values\n * Series are created per period (e.g., \"totalLinesOfCode (Current)\", \"totalLinesOfCode (Prior)\")\n */\nexport function transformForSeparateMode(\n data: any[],\n measures: string[],\n _timeDimensionKey: string\n): { data: any[], seriesKeys: string[] } {\n if (!isComparisonData(data)) {\n return { data, seriesKeys: measures }\n }\n\n const periodLabels = getPeriodLabels(data)\n const seriesKeys: string[] = []\n\n // Create series key for each measure + period combination\n for (const measure of measures) {\n for (let i = 0; i < periodLabels.length; i++) {\n const shortLabel = generatePeriodShortLabel(periodLabels[i], i)\n const key = `${measure} (${shortLabel})`\n seriesKeys.push(key)\n }\n }\n\n // Data already has period info, just return as-is for separate mode\n // The chart will group by time dimension and show multiple lines\n return { data, seriesKeys }\n}\n\n/**\n * Detect dimension fields in comparison data\n * Returns fields that are not measures, time dimension, or metadata fields\n */\nfunction detectDimensionFields(\n data: any[],\n measures: string[],\n timeDimensionKey: string\n): string[] {\n if (data.length === 0) return []\n\n const metadataFields = ['__period', '__periodIndex', '__periodDayIndex']\n const firstRow = data[0]\n const dimensions: string[] = []\n\n for (const key of Object.keys(firstRow)) {\n if (\n !measures.includes(key) &&\n key !== timeDimensionKey &&\n !metadataFields.includes(key)\n ) {\n dimensions.push(key)\n }\n }\n\n return dimensions\n}\n\n/**\n * Get unique dimension values from data for a set of dimension fields\n */\nfunction getUniqueDimensionValues(\n data: any[],\n dimensionFields: string[]\n): Map<string, Set<any>> {\n const values = new Map<string, Set<any>>()\n\n for (const field of dimensionFields) {\n values.set(field, new Set())\n }\n\n for (const row of data) {\n for (const field of dimensionFields) {\n if (row[field] !== undefined && row[field] !== null) {\n values.get(field)!.add(row[field])\n }\n }\n }\n\n return values\n}\n\n/**\n * Transform comparison data for 'overlay' mode\n * Pivots data so each row represents a day-of-period index,\n * with separate columns for each period's values.\n *\n * When dimensions are present, creates series keys that include\n * dimension values: \"Engineering - Total Lines of Code (Current)\"\n *\n * Input (without dimensions):\n * [\n * { date: '2024-01-01', value: 100, __periodIndex: 0, __periodDayIndex: 0 },\n * { date: '2024-01-02', value: 110, __periodIndex: 0, __periodDayIndex: 1 },\n * { date: '2023-01-01', value: 80, __periodIndex: 1, __periodDayIndex: 0 },\n * { date: '2023-01-02', value: 85, __periodIndex: 1, __periodDayIndex: 1 },\n * ]\n *\n * Output (without dimensions):\n * [\n * { __periodDayIndex: 0, 'value (Current)': 100, 'value (Prior)': 80 },\n * { __periodDayIndex: 1, 'value (Current)': 110, 'value (Prior)': 85 },\n * ]\n *\n * Input (with dimensions):\n * [\n * { date: '2024-01-01', 'Dept.name': 'Engineering', value: 100, __periodIndex: 0, __periodDayIndex: 0 },\n * { date: '2024-01-01', 'Dept.name': 'Sales', value: 50, __periodIndex: 0, __periodDayIndex: 0 },\n * { date: '2023-01-01', 'Dept.name': 'Engineering', value: 80, __periodIndex: 1, __periodDayIndex: 0 },\n * { date: '2023-01-01', 'Dept.name': 'Sales', value: 40, __periodIndex: 1, __periodDayIndex: 0 },\n * ]\n *\n * Output (with dimensions):\n * [\n * {\n * __periodDayIndex: 0,\n * 'Engineering - value (Current)': 100,\n * 'Engineering - value (Prior)': 80,\n * 'Sales - value (Current)': 50,\n * 'Sales - value (Prior)': 40,\n * },\n * ]\n */\nexport function transformForOverlayMode(\n data: any[],\n measures: string[],\n timeDimensionKey: string,\n getFieldLabel?: (fieldName: string) => string\n): { data: any[], seriesKeys: string[], xAxisKey: string } {\n if (!isComparisonData(data)) {\n return { data, seriesKeys: measures, xAxisKey: timeDimensionKey }\n }\n\n const periodLabels = getPeriodLabels(data)\n const periodIndices = getPeriodIndices(data)\n\n // Detect dimension fields in the data\n const dimensionFields = detectDimensionFields(data, measures, timeDimensionKey)\n const hasDimensions = dimensionFields.length > 0\n\n // Get unique dimension values for generating series keys\n const uniqueDimensionValues = hasDimensions\n ? getUniqueDimensionValues(data, dimensionFields)\n : null\n\n const transformedData = pivotByDayIndex(data, measures, timeDimensionKey, {\n dimensionFields,\n hasDimensions,\n periodLabels,\n getFieldLabel\n })\n\n const seriesKeys = buildOverlaySeriesKeys(measures, periodLabels, periodIndices, {\n dimensionFields,\n uniqueDimensionValues,\n getFieldLabel\n })\n\n return {\n data: transformedData,\n seriesKeys,\n xAxisKey: '__periodDayIndex'\n }\n}\n\n/**\n * Build the `dimensionValue / dimensionValue` prefix for a comparison row,\n * applying field labels when available. Empty when no dimensions are present.\n */\nfunction buildDimensionPrefix(\n row: any,\n dimensionFields: string[],\n getFieldLabel?: (fieldName: string) => string\n): string {\n if (dimensionFields.length === 0) return ''\n const dimensionValues = dimensionFields.map(field => {\n const value = row[field]\n return getFieldLabel ? getFieldLabel(String(value)) : String(value)\n })\n return dimensionValues.join(' / ')\n}\n\n/**\n * Pivot comparison rows so each output row is one `__periodDayIndex`, with a\n * column per measure/dimension/period combination.\n */\nfunction pivotByDayIndex(\n data: any[],\n measures: string[],\n timeDimensionKey: string,\n opts: {\n dimensionFields: string[]\n hasDimensions: boolean\n periodLabels: string[]\n getFieldLabel?: (fieldName: string) => string\n }\n): any[] {\n const { dimensionFields, hasDimensions, periodLabels, getFieldLabel } = opts\n const groupedByDayIndex = new Map<number, Record<string, any>>()\n\n for (const row of data) {\n const dayIndex = row.__periodDayIndex\n const periodIndex = row.__periodIndex\n\n if (!groupedByDayIndex.has(dayIndex)) {\n groupedByDayIndex.set(dayIndex, {\n __periodDayIndex: dayIndex,\n // Store the date from the first period (index 0) for reference\n __displayDate: periodIndex === 0 ? row[timeDimensionKey] : undefined\n })\n }\n\n const groupedRow = groupedByDayIndex.get(dayIndex)!\n\n // If this is the first period and we don't have a display date yet, add it\n if (!groupedRow.__displayDate && row[timeDimensionKey]) {\n groupedRow.__displayDate = row[timeDimensionKey]\n }\n\n // Build dimension prefix if dimensions are present\n const dimensionPrefix = hasDimensions\n ? buildDimensionPrefix(row, dimensionFields, getFieldLabel)\n : ''\n\n // Add measure values with optional dimension prefix and period suffix\n const shortLabel = generatePeriodShortLabel(periodLabels[periodIndex] || '', periodIndex)\n for (const measure of measures) {\n const displayName = getFieldLabel ? getFieldLabel(measure) : measure\n const key = dimensionPrefix\n ? `${dimensionPrefix} - ${displayName} (${shortLabel})`\n : `${displayName} (${shortLabel})`\n groupedRow[key] = row[measure]\n }\n }\n\n // Convert to array and sort by day index\n return Array.from(groupedByDayIndex.values())\n .sort((a, b) => a.__periodDayIndex - b.__periodDayIndex)\n}\n\n/**\n * Generate the overlay-mode series keys (matching the data column names) for\n * each measure/dimension-combination/period.\n */\nfunction buildOverlaySeriesKeys(\n measures: string[],\n periodLabels: string[],\n periodIndices: number[],\n opts: {\n dimensionFields: string[]\n uniqueDimensionValues: Map<string, Set<any>> | null\n getFieldLabel?: (fieldName: string) => string\n }\n): string[] {\n const { dimensionFields, uniqueDimensionValues, getFieldLabel } = opts\n const seriesKeys: string[] = []\n\n // When dimensions are present, prefix each series key with the combination;\n // otherwise use a single empty prefix to share the measure/period loop.\n const dimensionCombinations = uniqueDimensionValues\n ? generateDimensionCombinations(dimensionFields, uniqueDimensionValues, getFieldLabel)\n : ['']\n\n for (const dimCombo of dimensionCombinations) {\n for (const measure of measures) {\n const displayName = getFieldLabel ? getFieldLabel(measure) : measure\n for (let i = 0; i < periodIndices.length; i++) {\n const shortLabel = generatePeriodShortLabel(periodLabels[i] || '', i)\n seriesKeys.push(\n dimCombo\n ? `${dimCombo} - ${displayName} (${shortLabel})`\n : `${displayName} (${shortLabel})`\n )\n }\n }\n }\n\n return seriesKeys\n}\n\n/**\n * Generate all combinations of dimension values as formatted strings\n */\nfunction generateDimensionCombinations(\n dimensionFields: string[],\n uniqueValues: Map<string, Set<any>>,\n getFieldLabel?: (fieldName: string) => string\n): string[] {\n if (dimensionFields.length === 0) return []\n\n // For single dimension, just return the values\n if (dimensionFields.length === 1) {\n const field = dimensionFields[0]\n const values = Array.from(uniqueValues.get(field) || [])\n return values.map(v => getFieldLabel ? getFieldLabel(String(v)) : String(v))\n }\n\n // For multiple dimensions, generate cartesian product\n const result: string[] = []\n const fieldValues = dimensionFields.map(field =>\n Array.from(uniqueValues.get(field) || [])\n )\n\n function generateCombos(index: number, current: string[]): void {\n if (index === fieldValues.length) {\n const formatted = current.map(v =>\n getFieldLabel ? getFieldLabel(String(v)) : String(v)\n )\n result.push(formatted.join(' / '))\n return\n }\n\n for (const value of fieldValues[index]) {\n generateCombos(index + 1, [...current, value])\n }\n }\n\n generateCombos(0, [])\n return result\n}\n\n/**\n * Format the period day index for display on X-axis\n * Can show as \"Day 1\", \"Day 2\", etc., or as the original date\n */\nexport function formatPeriodDayIndex(\n dayIndex: number,\n displayDate?: string | Date,\n options?: {\n showDayNumber?: boolean\n dateFormat?: 'short' | 'long'\n }\n): string {\n if (options?.showDayNumber) {\n return `Day ${dayIndex + 1}`\n }\n\n if (displayDate) {\n const date = typeof displayDate === 'string' ? new Date(displayDate) : displayDate\n if (!isNaN(date.getTime())) {\n return date.toLocaleDateString('en-US', {\n month: options?.dateFormat === 'long' ? 'long' : 'short',\n day: 'numeric'\n })\n }\n }\n\n return `${dayIndex + 1}`\n}\n\n/**\n * Check if a series key represents a prior period (not the first period)\n * Used for applying different styling to prior period lines\n */\nexport function isPriorPeriodSeries(seriesKey: string, periodLabels: string[]): boolean {\n if (periodLabels.length < 2) return false\n\n // Check if the series key contains any label other than the first period's\n for (let i = 1; i < periodLabels.length; i++) {\n const shortLabel = generatePeriodShortLabel(periodLabels[i], i)\n if (seriesKey.includes(`(${shortLabel})`)) {\n return true\n }\n }\n\n // Also check for \"(Prior\" pattern\n return seriesKey.includes('(Prior')\n}\n\n/**\n * Get the stroke dash array for prior period styling\n */\nexport function getPriorPeriodStrokeDashArray(\n style: 'solid' | 'dashed' | 'dotted' = 'dashed'\n): string | undefined {\n switch (style) {\n case 'solid':\n return undefined\n case 'dashed':\n return '5 5'\n case 'dotted':\n return '2 2'\n default:\n return '5 5'\n }\n}\n","import { ReactElement, useState, useRef, useLayoutEffect, startTransition } from 'react'\nimport { ResponsiveContainer } from 'recharts'\nimport { useTranslation } from '../../hooks/useTranslation'\nimport LoadingIndicator from '../LoadingIndicator'\n\ninterface ChartContainerProps {\n children: ReactElement\n height?: string | number\n}\n\nexport default function ChartContainer({ children, height = \"100%\" }: ChartContainerProps) {\n const { t } = useTranslation()\n // Track if container is ready to render ResponsiveContainer\n // We need to wait for the container to be in the DOM with valid dimensions\n const containerRef = useRef<HTMLDivElement>(null)\n const [isReady, setIsReady] = useState(false)\n const [containerSize, setContainerSize] = useState({ width: 0, height: 0 })\n const sizeRef = useRef({ width: 0, height: 0 })\n const readyRef = useRef(false)\n const pendingSizeRef = useRef<{ width: number; height: number } | null>(null)\n const frameRef = useRef<number | null>(null)\n\n // Use useLayoutEffect to measure before paint\n useLayoutEffect(() => {\n let mounted = true\n let resizeObserver: ResizeObserver | null = null\n\n const flushPendingSize = () => {\n frameRef.current = null\n if (!mounted) return\n const pending = pendingSizeRef.current\n pendingSizeRef.current = null\n if (!pending) return\n\n const nextWidth = Math.round(pending.width)\n const nextHeight = Math.round(pending.height)\n if (nextWidth <= 0 || nextHeight <= 0) return\n\n const sizeChanged =\n sizeRef.current.width !== nextWidth || sizeRef.current.height !== nextHeight\n const shouldBecomeReady = !readyRef.current\n\n if (!sizeChanged && !shouldBecomeReady) return\n\n sizeRef.current = { width: nextWidth, height: nextHeight }\n readyRef.current = true\n\n // Resize-driven chart updates can come in bursts (one per portlet).\n // Transitioning these avoids blocking urgent input and coalesces visual work.\n startTransition(() => {\n if (sizeChanged) {\n setContainerSize({ width: nextWidth, height: nextHeight })\n }\n if (shouldBecomeReady) {\n setIsReady(true)\n }\n })\n }\n\n const scheduleSizeUpdate = (width: number, height: number) => {\n pendingSizeRef.current = { width, height }\n if (frameRef.current === null) {\n frameRef.current = requestAnimationFrame(flushPendingSize)\n }\n }\n\n const measureAndSetReady = () => {\n if (!mounted || !containerRef.current) return\n\n const rect = containerRef.current.getBoundingClientRect()\n // Check both clientWidth/Height AND getBoundingClientRect for robustness\n const width = Math.max(containerRef.current.clientWidth, rect.width)\n const measuredHeight = Math.max(containerRef.current.clientHeight, rect.height)\n\n if (width > 0 && measuredHeight > 0) {\n scheduleSizeUpdate(width, measuredHeight)\n }\n }\n\n // Set up ResizeObserver to detect when container gets valid dimensions\n resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width, height: entryHeight } = entry.contentRect\n if (width > 0 && entryHeight > 0) {\n scheduleSizeUpdate(width, entryHeight)\n }\n }\n })\n\n if (containerRef.current) {\n resizeObserver.observe(containerRef.current)\n // Also try immediate measurement\n measureAndSetReady()\n }\n\n return () => {\n mounted = false\n if (frameRef.current !== null) {\n cancelAnimationFrame(frameRef.current)\n frameRef.current = null\n }\n resizeObserver?.disconnect()\n }\n }, [])\n\n try {\n if (height === \"100%\") {\n // For 100% height, make the container fill the available flex space with proper sizing\n // user-select: none prevents browser selection box from appearing when clicking/dragging on charts\n return (\n <div\n ref={containerRef}\n className=\"dc:w-full dc:h-full dc:flex-1 dc:flex dc:flex-col dc:relative\"\n style={{ minHeight: '250px', minWidth: '100px', overflow: 'hidden', userSelect: 'none' }}\n >\n {isReady && containerSize.width > 0 && containerSize.height > 0 ? (\n <ResponsiveContainer\n width={containerSize.width}\n height={containerSize.height - 16}\n debounce={100}\n style={{ marginTop: '16px' }}\n >\n {children}\n </ResponsiveContainer>\n ) : (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\">\n <LoadingIndicator size=\"sm\" />\n </div>\n )}\n </div>\n )\n }\n\n // For specific heights, use them directly\n // user-select: none prevents browser selection box from appearing when clicking/dragging on charts\n const containerStyle = {\n height: typeof height === 'number' ? `${height}px` : height,\n width: '100%',\n minHeight: '200px',\n minWidth: '100px',\n userSelect: 'none' as const\n }\n\n return (\n <div\n ref={containerRef}\n className=\"dc:w-full dc:flex dc:flex-col dc:relative\"\n style={{ ...containerStyle, overflow: 'hidden' }}\n >\n {isReady && containerSize.width > 0 && containerSize.height > 0 ? (\n <ResponsiveContainer\n width={containerSize.width}\n height={containerSize.height - 16}\n debounce={100}\n style={{ marginTop: '16px' }}\n >\n {children}\n </ResponsiveContainer>\n ) : (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\">\n <LoadingIndicator size=\"sm\" />\n </div>\n )}\n </div>\n )\n } catch (error) {\n // ChartContainer ResponsiveContainer error\n\n return (\n <div\n className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full dc:h-full dc:p-4 dc:text-center dc:border dc:border-dashed dc:rounded-lg\"\n style={{ height, borderColor: 'var(--dc-border)', backgroundColor: 'var(--dc-surface)' }}\n >\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1 text-dc-text-muted\">{t('chart.runtime.unableToDisplay')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {error instanceof Error ? error.message : 'Failed to create responsive container'}\n </div>\n </div>\n )\n }\n}\n","import React from 'react'\nimport { Tooltip } from 'recharts'\nimport { formatNumericValue } from '../../utils/chartUtils'\n\ninterface ChartTooltipProps {\n formatter?: (value: any, name: any, props: any) => [React.ReactText, React.ReactText]\n labelFormatter?: (label: any, payload?: readonly any[]) => React.ReactText\n}\n\n// Default formatter that rounds numeric values to at most 2 decimal places\nconst defaultFormatter = (value: any, name: any): [React.ReactText, React.ReactText] => {\n if (value === null || value === undefined) {\n return ['No data', name]\n }\n return [formatNumericValue(value), name]\n}\n\nexport default function ChartTooltip({ formatter, labelFormatter }: ChartTooltipProps) {\n return (\n <Tooltip\n formatter={formatter || defaultFormatter}\n labelFormatter={labelFormatter}\n contentStyle={{\n backgroundColor: 'white',\n border: '1px solid #e5e7eb',\n borderRadius: '0.5rem',\n fontSize: '0.875rem',\n color: '#1f2937',\n boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',\n padding: '8px 12px'\n }}\n />\n )\n}","import React from 'react'\n\ninterface AngledXAxisTickProps {\n x?: number\n y?: number\n payload?: {\n value: string | number\n }\n tickFormatter?: (value: string | number, index: number) => string\n index?: number\n visibleTicksCount?: number\n // Allow any additional props recharts might pass\n [key: string]: unknown\n}\n\n/**\n * Custom XAxis tick component for properly aligned angled labels.\n * Fixes alignment issues in recharts 3.7+ where angled labels don't\n * center properly under data points.\n */\nconst AngledXAxisTick: React.FC<AngledXAxisTickProps> = ({\n x = 0,\n y = 0,\n payload,\n tickFormatter,\n index = 0\n}) => {\n if (!payload) return null\n\n const displayValue = tickFormatter\n ? tickFormatter(payload.value, index)\n : String(payload.value)\n\n return (\n <g transform={`translate(${x},${y})`}>\n <text\n x={0}\n y={0}\n dy={16}\n textAnchor=\"end\"\n fill=\"currentColor\"\n fontSize={12}\n transform=\"rotate(-45)\"\n >\n {displayValue}\n </text>\n </g>\n )\n}\n\nexport default AngledXAxisTick\n","import React from 'react'\nimport { useTranslation } from '../../hooks/useTranslation'\n\n/**\n * Shared guard/state components for chart rendering.\n *\n * Cartesian charts (bar, line, area, pie, …) all duplicated the same\n * centred flex blocks for their empty / config-error / render-error states,\n * differing only in the hint text. These components own the duplicated wrapper\n * JSX; each chart supplies its own already-resolved `hint` (a translation key\n * resolved via `t()`, or a literal string) so per-chart messaging is preserved.\n */\n\ninterface ChartStateProps {\n height?: string | number\n /** Already-resolved hint text (caller resolves its own i18n key) */\n hint?: React.ReactNode\n}\n\n/**\n * Empty / no-data state (muted styling). `titleKey` defaults to the generic\n * \"No data available\" heading but can be overridden (e.g. for the post-transform\n * \"No valid data\" state).\n */\nexport function ChartEmptyState({\n height,\n hint,\n titleKey = 'chart.runtime.noData'\n}: ChartStateProps & { titleKey?: string }) {\n const { t } = useTranslation()\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">{t(titleKey)}</div>\n {hint != null && <div className=\"dc:text-xs text-dc-text-secondary\">{hint}</div>}\n </div>\n </div>\n )\n}\n\n/**\n * Invalid-configuration state (warning styling). Title is always\n * \"Configuration Error\"; the chart supplies a hint describing what is missing.\n */\nexport function ChartConfigError({ height, hint }: ChartStateProps) {\n const { t } = useTranslation()\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">{t('chart.runtime.configError')}</div>\n {hint != null && <div className=\"dc:text-xs\">{hint}</div>}\n </div>\n </div>\n )\n}\n\n/**\n * Render-error state (error styling), used in chart `catch` blocks.\n */\nexport function ChartRenderError({\n height,\n chartType,\n error\n}: {\n height?: string | number\n /** Display name used in the \"{chartType} Error\" heading */\n chartType: string\n error: unknown\n}) {\n const { t } = useTranslation()\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full text-dc-error dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">{t('chart.runtime.chartError', { chartType })}</div>\n <div className=\"dc:text-xs dc:mb-2\">{error instanceof Error ? error.message : t('chart.runtime.unknownError')}</div>\n <div className=\"dc:text-xs text-dc-text-muted\">{t('chart.runtime.checkConfig')}</div>\n </div>\n </div>\n )\n}\n","import type { ChartAxisConfig } from '../../types'\n\n/**\n * Discriminated error code returned by {@link resolveChartAxisFields} when the\n * chart configuration is invalid. Each chart maps these codes to its own i18n\n * hint key (e.g. pie charts surface a pie-specific message for `axisInvalid`).\n */\nexport type ChartAxisErrorCode = 'axisInvalid' | 'axisFields'\n\n/**\n * Result of resolving X/Y/series fields from a {@link ChartAxisConfig}.\n * When `errorCode` is non-null the field values are not usable and the chart\n * should render a configuration-error state instead.\n */\nexport interface ResolvedChartAxisFields {\n /** Resolved X-axis field (category), or undefined when the config is invalid */\n xAxisField?: string\n /** Resolved Y-axis (value/measure) fields */\n yAxisFields: string[]\n /** Optional series/grouping fields (only present in the new config format) */\n seriesFields: string[]\n /** Non-null when the configuration cannot produce a renderable axis setup */\n errorCode: ChartAxisErrorCode | null\n}\n\n/**\n * Pure resolver for the X/Y/series field extraction that Cartesian charts\n * (bar, line, area, pie) all duplicated inline.\n *\n * Supports both the new format (`xAxis` / `yAxis` / `series` arrays) and the\n * legacy format (`x` / `y`). Returns a discriminated `errorCode` rather than\n * JSX so each chart can map it to its own i18n hint key and render its own\n * guard component.\n */\nexport function resolveChartAxisFields(chartConfig?: ChartAxisConfig): ResolvedChartAxisFields {\n let xAxisField: string | undefined\n let yAxisFields: string[] = []\n let seriesFields: string[] = []\n let errorCode: ChartAxisErrorCode | null = null\n\n if (chartConfig?.xAxis && chartConfig?.yAxis) {\n // New format\n xAxisField = Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis\n yAxisFields = Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis : [chartConfig.yAxis]\n seriesFields = chartConfig.series || []\n } else if (chartConfig?.x && chartConfig?.y) {\n // Legacy format\n xAxisField = chartConfig.x\n yAxisFields = Array.isArray(chartConfig.y) ? chartConfig.y : [chartConfig.y]\n } else {\n errorCode = 'axisInvalid'\n }\n\n if (!errorCode && (!xAxisField || yAxisFields.length === 0)) {\n errorCode = 'axisFields'\n }\n\n return { xAxisField, yAxisFields, seriesFields, errorCode }\n}\n","/**\n * Utility functions for handling target values in charts\n */\n\n/**\n * Parse target values from string format\n * @param targetString - String containing target values (e.g., \"100\" or \"50,75,100\")\n * @returns Array of numeric target values\n */\nexport function parseTargetValues(targetString: string): number[] {\n if (!targetString || typeof targetString !== 'string') {\n return []\n }\n\n const trimmed = targetString.trim()\n if (!trimmed) {\n return []\n }\n\n try {\n // Split by comma and parse each value\n const values = trimmed\n .split(',')\n .map(val => val.trim())\n .filter(val => val !== '')\n .map(val => {\n const num = parseFloat(val)\n if (isNaN(num)) {\n throw new Error(`Invalid numeric value: ${val}`)\n }\n return num\n })\n\n return values.length > 0 ? values : []\n } catch (error) {\n console.warn('Failed to parse target values:', error)\n return []\n }\n}\n\n/**\n * Spread target values across data points\n * @param targets - Array of target values\n * @param dataLength - Number of data points to spread across\n * @returns Array of target values for each data point\n */\nexport function spreadTargetValues(targets: number[], dataLength: number): number[] {\n if (targets.length === 0 || dataLength <= 0) {\n return []\n }\n\n // If only one target value, repeat for all data points\n if (targets.length === 1) {\n return new Array(dataLength).fill(targets[0])\n }\n\n // If we have multiple targets, spread them evenly across data points\n const result: number[] = []\n const baseGroupSize = Math.floor(dataLength / targets.length)\n const remainder = dataLength % targets.length\n\n let currentIndex = 0\n \n for (let i = 0; i < targets.length; i++) {\n // Calculate group size for this target\n // First 'remainder' groups get an extra item\n const groupSize = baseGroupSize + (i < remainder ? 1 : 0)\n \n // Fill this group with the current target value\n for (let j = 0; j < groupSize; j++) {\n result[currentIndex++] = targets[i]\n }\n }\n\n return result\n}\n\n/**\n * Calculate variance between actual and target values\n * @param actual - Actual value\n * @param target - Target value\n * @returns Variance as percentage\n */\nexport function calculateVariance(actual: number, target: number): number {\n if (target === 0) {\n return actual === 0 ? 0 : (actual > 0 ? 100 : -100)\n }\n return ((actual - target) / target) * 100\n}\n\n/**\n * Format variance as percentage string with appropriate sign and color indication\n * @param variance - Variance percentage\n * @param decimals - Number of decimal places (default: 1)\n * @returns Formatted variance string (e.g., \"+12.5%\" or \"-8.3%\")\n */\nexport function formatVariance(variance: number, decimals: number = 1): string {\n const sign = variance >= 0 ? '+' : ''\n return `${sign}${variance.toFixed(decimals)}%`\n}\n\n/**\n * Get unique target values for reference lines\n * @param targets - Array of target values (may contain duplicates)\n * @returns Array of unique target values\n */\nexport function getUniqueTargets(targets: number[]): number[] {\n return [...new Set(targets)].sort((a, b) => a - b)\n}","import { Fragment } from 'react'\nimport { YAxis, Line, Legend } from 'recharts'\nimport { CHART_MARGINS } from '../../utils/chartConstants'\nimport { formatAxisValue } from '../../utils/chartUtils'\nimport { parseTargetValues, spreadTargetValues } from '../../utils/targetUtils'\nimport type { AxisFormatConfig } from '../../types'\n\n/**\n * Shared scaffolding for the Cartesian recharts charts (bar, line, area).\n *\n * These charts duplicated the same dual-Y-axis derivation, chart margins,\n * target-line overlay, and the left/right `<YAxis>` JSX. The pure helpers and\n * recharts render-helpers here own that shared setup. Render-helpers return\n * recharts elements (inside a fragment) and are embedded directly in the chart\n * children — recharts flattens fragments when discovering axes/series, exactly\n * as it already does for the inline target-line fragment.\n */\n\nexport interface DualAxisInfo {\n /** True when at least one measure is assigned to the right axis */\n hasRightAxis: boolean\n /** Measure fields rendered against the left axis */\n leftAxisFields: string[]\n /** Measure fields rendered against the right axis */\n rightAxisFields: string[]\n}\n\n/** Derive left/right axis membership from per-measure axis assignments. */\nexport function getDualAxisInfo(\n yAxisFields: string[],\n yAxisAssignment: Record<string, 'left' | 'right'>\n): DualAxisInfo {\n const hasRightAxis = yAxisFields.some((field) => yAxisAssignment[field] === 'right')\n const leftAxisFields = yAxisFields.filter((f) => (yAxisAssignment[f] || 'left') === 'left')\n const rightAxisFields = yAxisFields.filter((f) => yAxisAssignment[f] === 'right')\n return { hasRightAxis, leftAxisFields, rightAxisFields }\n}\n\n/** Chart margins with extra room for the Y-axis label(s). */\nexport function getYAxisChartMargins(hasRightAxis: boolean) {\n return {\n ...CHART_MARGINS,\n left: 40, // Space for left Y-axis label\n right: hasRightAxis ? 40 : 20 // Extra space for right Y-axis label if needed\n }\n}\n\n/**\n * Apply target values (single or comma-separated spread) onto chart data,\n * returning the spread targets plus data enhanced with a `__target` key.\n */\nexport function withTargetData<T extends Record<string, any>>(\n chartData: T[],\n target: string | undefined\n): { spreadTargets: number[]; enhancedChartData: T[] } {\n const targetValues = parseTargetValues(target || '')\n const spreadTargets = spreadTargetValues(targetValues, chartData.length)\n const enhancedChartData = spreadTargets.length > 0\n ? chartData.map((dataPoint, index) => ({ ...dataPoint, __target: spreadTargets[index] || null }))\n : chartData\n return { spreadTargets, enhancedChartData }\n}\n\n/**\n * Render the left + optional right `<YAxis>`. Returns recharts elements in a\n * fragment for embedding directly in the chart children. Positional args keep\n * the (otherwise identical) call sites in each chart down to a single line.\n *\n * @param isPercentStack when true (area/bar percent stacking) the left axis shows 0–100%\n */\nexport function renderDualYAxes(\n { hasRightAxis, leftAxisFields, rightAxisFields }: DualAxisInfo,\n getFieldLabel: (field: string) => string,\n leftYAxisFormat?: AxisFormatConfig,\n rightYAxisFormat?: AxisFormatConfig,\n isPercentStack = false\n) {\n return (\n <Fragment>\n <YAxis\n yAxisId=\"left\"\n orientation=\"left\"\n tick={{ fontSize: 12 }}\n tickFormatter={\n isPercentStack\n ? (v) => `${(v * 100).toFixed(0)}%`\n : leftYAxisFormat\n ? (value) => formatAxisValue(value, leftYAxisFormat)\n : undefined\n }\n domain={isPercentStack ? [0, 1] : undefined}\n label={\n isPercentStack\n ? undefined\n : leftAxisFields.length > 0\n ? {\n value: leftYAxisFormat?.label || getFieldLabel(leftAxisFields[0]),\n angle: -90,\n position: 'left',\n style: { textAnchor: 'middle', fontSize: '12px' }\n }\n : undefined\n }\n />\n {hasRightAxis && (\n <YAxis\n yAxisId=\"right\"\n orientation=\"right\"\n tick={{ fontSize: 12 }}\n tickFormatter={rightYAxisFormat ? (value) => formatAxisValue(value, rightYAxisFormat) : undefined}\n label={\n rightAxisFields.length > 0\n ? {\n value: rightYAxisFormat?.label || getFieldLabel(rightAxisFields[0]),\n angle: 90,\n position: 'right',\n style: { textAnchor: 'middle', fontSize: '12px' }\n }\n : undefined\n }\n />\n )}\n </Fragment>\n )\n}\n\n/**\n * Render the target overlay: a white backing line plus a purple dashed line\n * on top (shared by bar, line, area). Returns null when there are no targets.\n */\nexport function renderChartTargetLines(spreadTargets: number[]) {\n if (spreadTargets.length === 0) return null\n return (\n <Fragment>\n {/* White background line */}\n <Line\n type=\"monotone\"\n dataKey=\"__target\"\n yAxisId=\"left\"\n stroke=\"#ffffff\"\n strokeWidth={2}\n dot={false}\n activeDot={false}\n connectNulls={false}\n />\n {/* Grey dashed line on top */}\n <Line\n type=\"monotone\"\n dataKey=\"__target\"\n yAxisId=\"left\"\n name=\"Target\"\n stroke=\"#8B5CF6\"\n strokeWidth={2}\n strokeDasharray=\"2 3\"\n dot={false}\n activeDot={false}\n connectNulls={false}\n />\n </Fragment>\n )\n}\n\ninterface CartesianTooltipOptions {\n leftYAxisFormat?: AxisFormatConfig\n rightYAxisFormat?: AxisFormatConfig\n yAxisAssignment: Record<string, 'left' | 'right'>\n /** Maps a series key (display label) back to its original measure field. */\n resolveField: (name: string) => string | undefined\n /** When true, values render as percentages (area/bar percent stacking). */\n isPercentStack?: boolean\n}\n\n/**\n * Build the value/name tooltip formatter shared by the Cartesian charts.\n * Handles null values, the target series, percent stacking, and per-series\n * left/right axis format selection.\n */\nexport function makeCartesianTooltipFormatter({\n leftYAxisFormat,\n rightYAxisFormat,\n yAxisAssignment,\n resolveField,\n isPercentStack = false\n}: CartesianTooltipOptions) {\n return (value: any, name: any): [any, any] => {\n if (value === null || value === undefined) {\n return ['No data', name]\n }\n if (name === 'Target') {\n // Use left Y-axis format for target values\n return [formatAxisValue(value, leftYAxisFormat), 'Target Value']\n }\n if (isPercentStack && typeof value === 'number') {\n return [`${(value * 100).toFixed(1)}%`, name]\n }\n // Determine which axis format to use based on series name\n const originalField = resolveField(name)\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n const formatConfig = axisId === 'right' ? rightYAxisFormat : leftYAxisFormat\n return [formatAxisValue(value, formatConfig), name]\n }\n}\n\n/**\n * Render the hover-aware bottom legend shared by the Cartesian charts.\n * Returns null when the legend is hidden. `iconType` / `paddingTop` differ\n * per chart (rect+10 for area, rect+25 for bar, line+25 for line).\n */\nexport function renderHoverLegend({\n show,\n iconType,\n paddingTop,\n onHover,\n onLeave\n}: {\n show: boolean\n iconType: 'rect' | 'line' | 'circle'\n paddingTop: number\n onHover: (dataKey: string) => void\n onLeave: () => void\n}) {\n if (!show) return null\n return (\n <Legend\n wrapperStyle={{ fontSize: '12px', paddingTop: `${paddingTop}px` }}\n iconType={iconType}\n iconSize={8}\n layout=\"horizontal\"\n align=\"center\"\n verticalAlign=\"bottom\"\n onMouseEnter={(o) => onHover(String(o.dataKey || ''))}\n onMouseLeave={onLeave}\n />\n )\n}\n","import React from 'react'\nimport { Line, Area } from 'recharts'\nimport { formatTimeValue, getFieldGranularity, transformChartDataWithSeries } from '../../utils/chartUtils'\nimport { CHART_COLORS } from '../../utils/chartConstants'\nimport {\n isComparisonData,\n getPeriodLabels,\n transformForOverlayMode,\n isPriorPeriodSeries,\n getPriorPeriodStrokeDashArray\n} from '../../utils/comparisonUtils'\nimport type { ColorPalette } from '../../types'\n\n/**\n * Co-located helpers shared by the Cartesian time-series charts (Line, Area).\n *\n * These extract the heavier inline logic that previously lived inside the chart\n * components — the clickable/drill dot renderer, the comparison-mode tick and\n * label formatters, and the series-key → measure-field resolution — so the\n * component bodies stay focused on chart composition. Pure extraction: no\n * behaviour change.\n */\n\nexport interface DataPointClickPayload {\n dataPoint: any\n clickedField: string\n xValue: any\n position: { x: number; y: number }\n nativeEvent: React.MouseEvent\n}\n\ninterface DrillDotOptions {\n /** Recharts dot props (cx, cy, payload, key). */\n props: any\n color: string\n drillEnabled?: boolean\n originalField?: string\n seriesKey: string\n onDataPointClick?: (payload: DataPointClickPayload) => void\n}\n\n/**\n * Render a single data-point dot. When drill is enabled and a click handler is\n * present, renders a larger clickable dot with a surface-coloured backing\n * circle to mask grid lines; otherwise a small plain dot. Returns null when the\n * point has no coordinates.\n *\n * `renderPlain` controls whether a small non-interactive dot is drawn when\n * drill is disabled (Line draws one; Area returns nothing).\n */\nexport function renderDrillDot({\n props,\n color,\n drillEnabled,\n originalField,\n seriesKey,\n onDataPointClick,\n renderPlain\n}: DrillDotOptions & { renderPlain: boolean }): React.ReactElement | null {\n const { cx, cy, payload, key } = props\n if (cx === undefined || cy === undefined) return null\n\n const handleClick = (event: React.MouseEvent) => {\n event.stopPropagation()\n event.preventDefault()\n if (onDataPointClick) {\n onDataPointClick({\n dataPoint: payload,\n clickedField: originalField || seriesKey,\n xValue: payload.name,\n position: { x: event.clientX, y: event.clientY },\n nativeEvent: event\n })\n }\n }\n\n if (drillEnabled && onDataPointClick) {\n return (\n <g key={key}>\n {/* Background to mask grid lines - uses theme surface color */}\n <circle cx={cx} cy={cy} r={6} fill=\"var(--dc-surface)\" style={{ pointerEvents: 'none' }} />\n {/* Visible dot with click handler */}\n <circle\n cx={cx}\n cy={cy}\n r={4}\n fill=\"var(--dc-surface)\"\n stroke={color}\n strokeWidth={2}\n cursor=\"pointer\"\n onClick={(e: React.MouseEvent<SVGCircleElement>) => {\n handleClick(e as unknown as React.MouseEvent)\n }}\n />\n </g>\n )\n }\n\n if (!renderPlain) return null\n\n return <circle key={key} cx={cx} cy={cy} r={3} fill={color} />\n}\n\n/**\n * Format a comparison-data X-axis value: shows the current period's date,\n * formatted to the query granularity, falling back to \"Period N\".\n */\nfunction formatComparisonDate(\n displayDate: unknown,\n queryObject: any,\n xAxisField: string,\n fallbackIndex: number\n): string {\n if (displayDate) {\n const granularity = getFieldGranularity(queryObject, xAxisField)\n return formatTimeValue(displayDate as any, granularity)\n }\n return `Period ${fallbackIndex + 1}`\n}\n\n/**\n * Build the comparison-mode X-axis tick formatter for time-series charts.\n * Returns undefined when not in comparison mode (no custom formatting).\n */\nexport function makeComparisonTickFormatter(\n hasComparisonData: boolean,\n chartData: any[],\n queryObject: any,\n xAxisField: string\n): ((value: string | number, index: number) => string) | undefined {\n if (!hasComparisonData) return undefined\n return (value: string | number, index: number) => {\n const row = chartData[index]\n return formatComparisonDate(row?.__displayDate, queryObject, xAxisField, Number(value))\n }\n}\n\n/**\n * Build the comparison-mode tooltip label formatter for time-series charts.\n * Returns undefined when not in comparison mode (no custom formatting).\n */\nexport function makeComparisonLabelFormatter(\n hasComparisonData: boolean,\n queryObject: any,\n xAxisField: string\n): ((label: any, payload: any) => string) | undefined {\n if (!hasComparisonData) return undefined\n return (label: any, payload: any) => {\n const row = payload && payload.length > 0 ? payload[0]?.payload : undefined\n return formatComparisonDate(row?.__displayDate, queryObject, xAxisField, Number(label))\n }\n}\n\n/**\n * Resolve effective stacking for the Area chart from `stackType` (new) /\n * `stacked` (legacy), disabling stacking when a right axis is present (areas on\n * different axes can't be stacked).\n */\nexport function resolveAreaStacking(\n displayConfig: { stackType?: string; stacked?: boolean } | undefined,\n hasRightAxis: boolean\n): { effectiveShouldStack: boolean; effectiveIsPercentStack: boolean; stackOffset: 'expand' | undefined } {\n const stackType = displayConfig?.stackType ?? (displayConfig?.stacked ? 'normal' : 'none')\n const effectiveShouldStack = stackType !== 'none' && !hasRightAxis\n const effectiveIsPercentStack = stackType === 'percent' && !hasRightAxis\n return {\n effectiveShouldStack,\n effectiveIsPercentStack,\n stackOffset: effectiveIsPercentStack ? 'expand' : undefined\n }\n}\n\n/** Build a map from series display label to original measure field name. */\nexport function buildSeriesKeyToFieldMap(\n yAxisFields: string[],\n getFieldLabel: (field: string) => string\n): Record<string, string> {\n const mapping: Record<string, string> = {}\n yAxisFields.forEach((field) => {\n mapping[getFieldLabel(field)] = field\n })\n return mapping\n}\n\n/**\n * Resolve a series key back to its original measure field, handling comparison\n * suffixes like \"(Current)\" / \"(Prior)\" and dimension prefixes (\"Dim - Label\").\n */\nexport function makeSeriesKeyResolver(\n seriesKeyToField: Record<string, string>\n): (seriesKey: string) => string | undefined {\n return (seriesKey: string): string | undefined => {\n if (seriesKeyToField[seriesKey]) return seriesKeyToField[seriesKey]\n // Guard against excessive input length to prevent ReDoS\n if (seriesKey.length > 1000) return undefined\n const withoutSuffix = seriesKey.replace(/\\s*\\((Current|Prior)\\)$/, '')\n const parts = withoutSuffix.split(' - ')\n const measureLabel = parts[parts.length - 1]\n return seriesKeyToField[measureLabel]\n }\n}\n\n/** Pick a series colour by index, preferring the palette then the defaults. */\nexport function getSeriesColor(colorPalette: ColorPalette | undefined, index: number): string {\n const colors = colorPalette?.colors\n return (colors && colors[index % colors.length]) || CHART_COLORS[index % CHART_COLORS.length]\n}\n\ninterface LineSeriesOptions {\n seriesKeys: string[]\n colorPalette?: ColorPalette\n resolveField: (seriesKey: string) => string | undefined\n yAxisAssignment: Record<string, 'left' | 'right'>\n hoveredLegend: string | null\n connectNulls: boolean\n drillEnabled?: boolean\n onDataPointClick?: (payload: DataPointClickPayload) => void\n /** Comparison styling (omit/false → standard series). */\n hasComparisonData?: boolean\n periodLabels?: string[]\n priorPeriodStyle?: 'solid' | 'dashed' | 'dotted'\n priorPeriodOpacity?: number\n}\n\n/** Render the `<Line>` element for one resolved series (Line chart). */\nfunction renderOneLineSeries(seriesKey: string, index: number, opts: LineSeriesOptions): React.ReactElement {\n const {\n colorPalette, resolveField, yAxisAssignment, hoveredLegend, connectNulls,\n drillEnabled, onDataPointClick, hasComparisonData, periodLabels = [],\n priorPeriodStyle = 'dashed', priorPeriodOpacity = 0.5\n } = opts\n\n const originalField = resolveField(seriesKey)\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n\n const isPriorPeriod = Boolean(hasComparisonData) && isPriorPeriodSeries(seriesKey, periodLabels)\n const strokeDashArray = isPriorPeriod ? getPriorPeriodStrokeDashArray(priorPeriodStyle) : undefined\n const opacity = isPriorPeriod ? priorPeriodOpacity : 1\n const lineColor = getSeriesColor(colorPalette, index)\n\n let strokeOpacity = opacity\n if (hoveredLegend) strokeOpacity = hoveredLegend === seriesKey ? 1 : 0.3\n\n return (\n <Line\n key={seriesKey}\n type=\"monotone\"\n dataKey={seriesKey}\n yAxisId={axisId}\n stroke={lineColor}\n strokeWidth={isPriorPeriod ? 1.5 : 2}\n strokeDasharray={strokeDashArray}\n dot={isPriorPeriod ? false : (props: any) => renderDrillDot({\n props, color: lineColor, drillEnabled, originalField, seriesKey, onDataPointClick, renderPlain: true\n })}\n activeDot={false}\n strokeOpacity={strokeOpacity}\n connectNulls={connectNulls}\n />\n )\n}\n\n/** Render all line series for the Line chart. */\nexport function renderLineSeries(opts: LineSeriesOptions): React.ReactElement[] {\n return opts.seriesKeys.map((seriesKey, index) => renderOneLineSeries(seriesKey, index, opts))\n}\n\ninterface AreaSeriesOptions {\n seriesKeys: string[]\n colorPalette?: ColorPalette\n seriesKeyToField: Record<string, string>\n yAxisAssignment: Record<string, 'left' | 'right'>\n hoveredLegend: string | null\n connectNulls: boolean\n shouldStack: boolean\n drillEnabled?: boolean\n onDataPointClick?: (payload: DataPointClickPayload) => void\n}\n\n/** Render the `<Area>` element for one series (Area chart). */\nfunction renderOneAreaSeries(seriesKey: string, index: number, opts: AreaSeriesOptions): React.ReactElement {\n const {\n colorPalette, seriesKeyToField, yAxisAssignment, hoveredLegend, connectNulls,\n shouldStack, drillEnabled, onDataPointClick\n } = opts\n\n const originalField = seriesKeyToField[seriesKey]\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n const areaColor = getSeriesColor(colorPalette, index)\n\n let fillOpacity = 0.3\n let strokeOpacity = 1\n if (hoveredLegend) {\n const active = hoveredLegend === seriesKey\n fillOpacity = active ? 0.6 : 0.1\n strokeOpacity = active ? 1 : 0.3\n }\n\n return (\n <Area\n key={seriesKey}\n type=\"monotone\"\n dataKey={seriesKey}\n yAxisId={axisId}\n stackId={shouldStack ? 'stack' : undefined}\n stroke={areaColor}\n fill={areaColor}\n fillOpacity={fillOpacity}\n strokeWidth={2}\n strokeOpacity={strokeOpacity}\n connectNulls={connectNulls}\n dot={(props: any) => renderDrillDot({\n props, color: areaColor, drillEnabled, originalField, seriesKey, onDataPointClick, renderPlain: false\n })}\n activeDot={false}\n />\n )\n}\n\n/** Render all area series for the Area chart. */\nexport function renderAreaSeries(opts: AreaSeriesOptions): React.ReactElement[] {\n return opts.seriesKeys.map((seriesKey, index) => renderOneAreaSeries(seriesKey, index, opts))\n}\n\nexport interface TimeSeriesShape {\n chartData: any[]\n seriesKeys: string[]\n effectiveXAxisKey: string\n hasComparisonData: boolean\n periodLabels: string[]\n}\n\n/**\n * Shape raw rows for a Cartesian time-series chart. In comparison mode this\n * uses the overlay transform (aligned by period day index); otherwise the\n * standard series transform. Centralises the branch so the chart body stays\n * flat.\n */\nexport function buildTimeSeriesData(args: {\n data: any[]\n xAxisField: string\n yAxisFields: string[]\n seriesFields: string[]\n queryObject: any\n getFieldLabel: (field: string) => string\n}): TimeSeriesShape {\n const { data, xAxisField, yAxisFields, seriesFields, queryObject, getFieldLabel } = args\n const hasComparisonData = isComparisonData(data)\n\n if (hasComparisonData) {\n const overlay = transformForOverlayMode(data, yAxisFields, xAxisField, getFieldLabel)\n return {\n chartData: overlay.data,\n seriesKeys: overlay.seriesKeys,\n effectiveXAxisKey: '__periodDayIndex',\n hasComparisonData,\n periodLabels: getPeriodLabels(data)\n }\n }\n\n const standard = transformChartDataWithSeries(data, xAxisField, yAxisFields, queryObject, seriesFields, getFieldLabel)\n return {\n chartData: standard.data,\n seriesKeys: standard.seriesKeys,\n effectiveXAxisKey: 'name',\n hasComparisonData,\n periodLabels: []\n }\n}\n","import React, { useState, useMemo } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation'\nimport { ComposedChart, XAxis, CartesianGrid } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport AngledXAxisTick from './AngledXAxisTick'\nimport { ChartEmptyState, ChartConfigError, ChartRenderError } from './ChartStates'\nimport { resolveChartAxisFields } from './chartAxisResolution'\nimport {\n getDualAxisInfo,\n getYAxisChartMargins,\n withTargetData,\n renderDualYAxes,\n renderChartTargetLines,\n makeCartesianTooltipFormatter,\n renderHoverLegend\n} from './chartScaffolding'\nimport { buildSeriesKeyToFieldMap, renderAreaSeries, resolveAreaStacking } from './cartesianChartHelpers'\nimport { transformChartDataWithSeries } from '../../utils/chartUtils'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport type { ChartProps } from '../../types'\n\nconst AreaChart = React.memo(function AreaChart({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n onDataPointClick,\n drillEnabled\n}: ChartProps) {\n const { t } = useTranslation()\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel()\n\n // Resolve + validate axis fields (hooks-first to satisfy React rules)\n const { xAxisField, yAxisFields, seriesFields, errorCode } = useMemo(\n () => resolveChartAxisFields(chartConfig),\n [chartConfig]\n )\n\n // Dual Y-axis support: extract yAxisAssignment from chartConfig\n const yAxisAssignment = useMemo(() =>\n chartConfig?.yAxisAssignment || {},\n [chartConfig?.yAxisAssignment]\n )\n\n // Use shared function to transform data and handle series\n // (empty arrays when config is invalid — early returns happen after hooks)\n const { data: chartData, seriesKeys } = useMemo(() => {\n if (errorCode || !data || data.length === 0 || !xAxisField) {\n return { data: [], seriesKeys: [] }\n }\n return transformChartDataWithSeries(\n data,\n xAxisField,\n yAxisFields,\n queryObject,\n seriesFields,\n getFieldLabel\n )\n }, [data, xAxisField, yAxisFields, queryObject, seriesFields, getFieldLabel, errorCode])\n\n try {\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showGrid: displayConfig?.showGrid ?? true,\n showTooltip: displayConfig?.showTooltip ?? true,\n connectNulls: displayConfig?.connectNulls ?? false\n }\n\n const showAllXLabels = displayConfig?.showAllXLabels ?? true\n\n // Extract axis format configs\n const leftYAxisFormat = displayConfig?.leftYAxisFormat\n const rightYAxisFormat = displayConfig?.rightYAxisFormat\n\n if (!data || data.length === 0) {\n return <ChartEmptyState height={height} hint={t('chart.runtime.noDataHint.area')} />\n }\n\n if (errorCode) {\n return <ChartConfigError height={height} hint={t(`chart.runtime.configErrorHint.${errorCode}`)} />\n }\n\n // Build mapping from series key (label) to original field name\n const seriesKeyToField = buildSeriesKeyToFieldMap(yAxisFields, getFieldLabel)\n\n // Dual Y-axis derivation + margins (shared scaffolding)\n const axisInfo = getDualAxisInfo(yAxisFields, yAxisAssignment)\n const { hasRightAxis } = axisInfo\n\n // Resolve effective stacking (disabled when dual Y-axis present)\n const { effectiveShouldStack, effectiveIsPercentStack, stackOffset } =\n resolveAreaStacking(displayConfig, hasRightAxis)\n\n // Determine if legend will be shown\n const showLegend = safeDisplayConfig.showLegend\n\n // Use custom chart margins with extra space for Y-axis labels\n const chartMargins = getYAxisChartMargins(hasRightAxis)\n\n // Process target values and add to chart data\n const { spreadTargets, enhancedChartData } = withTargetData(chartData, displayConfig?.target)\n\n // Validate transformed data\n if (!chartData || chartData.length === 0) {\n return (\n <ChartEmptyState\n height={height}\n titleKey=\"chart.runtime.noValidData\"\n hint=\"No valid data points for area chart after transformation\"\n />\n )\n }\n\n return (\n <ChartContainer height={height}>\n <ComposedChart data={enhancedChartData} margin={chartMargins} stackOffset={stackOffset} accessibilityLayer={false}>\n {safeDisplayConfig.showGrid && <CartesianGrid strokeDasharray=\"3 3\" style={{ pointerEvents: 'none' }} />}\n <XAxis dataKey=\"name\" type=\"category\" tick={<AngledXAxisTick />} height={60} interval={showAllXLabels ? 0 : undefined} />\n {renderDualYAxes(axisInfo, getFieldLabel, leftYAxisFormat, rightYAxisFormat, effectiveIsPercentStack)}\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={makeCartesianTooltipFormatter({\n leftYAxisFormat,\n rightYAxisFormat,\n yAxisAssignment,\n resolveField: (name) => seriesKeyToField[name],\n isPercentStack: effectiveIsPercentStack\n })}\n />\n )}\n {renderHoverLegend({\n show: showLegend,\n iconType: 'rect',\n paddingTop: 10,\n onHover: setHoveredLegend,\n onLeave: () => setHoveredLegend(null)\n })}\n {renderAreaSeries({\n seriesKeys,\n colorPalette,\n seriesKeyToField,\n yAxisAssignment,\n hoveredLegend,\n connectNulls: safeDisplayConfig.connectNulls,\n shouldStack: effectiveShouldStack,\n drillEnabled,\n onDataPointClick\n })}\n {renderChartTargetLines(spreadTargets)}\n </ComposedChart>\n </ChartContainer>\n )\n } catch (error) {\n return <ChartRenderError height={height} chartType=\"Area Chart\" error={error} />\n }\n})\n\nexport default AreaChart\n"],"mappings":";;;;;;AAiBA,IAAM,IAAc;CAClB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,SAAwB,EAAiB,EACvC,UAAO,MACP,eAAY,MACY;CACxB,OACE,kBAAC,OAAD;EACE,WAAW,iDAAiD,EAAY,GAAM,GAAG;EACjF,OAAO,EAAE,mBAAmB,oBAAoB;EAChD,MAAK;EACL,cAAW;CACZ,CAAA;AAEL;;;ACzBA,SAAgB,EAAiB,GAAsB;CACrD,OAAO,EAAK,SAAS,KAAK,mBAAmB,EAAK;AACpD;AAKA,SAAgB,EAAgB,GAAuB;CACrD,IAAI,CAAC,EAAiB,CAAI,GAAG,OAAO,CAAC;CAErC,IAAM,oBAAS,IAAI,IAAY;CAC/B,KAAK,IAAM,KAAO,GAChB,AAAI,EAAI,YACN,EAAO,IAAI,EAAI,QAAQ;CAG3B,OAAO,MAAM,KAAK,CAAM;AAC1B;AAKA,SAAgB,EAAiB,GAAuB;CACtD,IAAI,CAAC,EAAiB,CAAI,GAAG,OAAO,CAAC;CAErC,IAAM,oBAAU,IAAI,IAAY;CAChC,KAAK,IAAM,KAAO,GAChB,AAAI,OAAO,EAAI,iBAAkB,YAC/B,EAAQ,IAAI,EAAI,aAAa;CAGjC,OAAO,MAAM,KAAK,CAAO,EAAE,MAAM,GAAG,MAAM,IAAI,CAAC;AACjD;AAMA,SAAgB,EAAyB,GAAsB,GAAuB;CACpF,OAAO,MAAU,IAAI,YAAY;AACnC;AAOA,SAAgB,GACd,GACA,GACA,GACuC;CACvC,IAAI,CAAC,EAAiB,CAAI,GACxB,OAAO;EAAE;EAAM,YAAY;CAAS;CAGtC,IAAM,IAAe,EAAgB,CAAI,GACnC,IAAuB,CAAC;CAG9B,KAAK,IAAM,KAAW,GACpB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAa,QAAQ,KAAK;EAE5C,IAAM,IAAM,GAAG,EAAQ,IADJ,EAAyB,EAAa,IAAI,CAClC,EAAW;EACtC,EAAW,KAAK,CAAG;CACrB;CAKF,OAAO;EAAE;EAAM;CAAW;AAC5B;AAMA,SAAS,GACP,GACA,GACA,GACU;CACV,IAAI,EAAK,WAAW,GAAG,OAAO,CAAC;CAE/B,IAAM,IAAiB;EAAC;EAAY;EAAiB;CAAkB,GACjE,IAAW,EAAK,IAChB,IAAuB,CAAC;CAE9B,KAAK,IAAM,KAAO,OAAO,KAAK,CAAQ,GACpC,AACE,CAAC,EAAS,SAAS,CAAG,KACtB,MAAQ,KACR,CAAC,EAAe,SAAS,CAAG,KAE5B,EAAW,KAAK,CAAG;CAIvB,OAAO;AACT;AAKA,SAAS,GACP,GACA,GACuB;CACvB,IAAM,oBAAS,IAAI,IAAsB;CAEzC,KAAK,IAAM,KAAS,GAClB,EAAO,IAAI,mBAAO,IAAI,IAAI,CAAC;CAG7B,KAAK,IAAM,KAAO,GAChB,KAAK,IAAM,KAAS,GAClB,AAAI,EAAI,OAAW,KAAA,KAAa,EAAI,OAAW,QAC7C,EAAO,IAAI,CAAK,EAAG,IAAI,EAAI,EAAM;CAKvC,OAAO;AACT;AA2CA,SAAgB,EACd,GACA,GACA,GACA,GACyD;CACzD,IAAI,CAAC,EAAiB,CAAI,GACxB,OAAO;EAAE;EAAM,YAAY;EAAU,UAAU;CAAiB;CAGlE,IAAM,IAAe,EAAgB,CAAI,GACnC,IAAgB,EAAiB,CAAI,GAGrC,IAAkB,GAAsB,GAAM,GAAU,CAAgB,GACxE,IAAgB,EAAgB,SAAS,GAGzC,IAAwB,IAC1B,GAAyB,GAAM,CAAe,IAC9C;CAeJ,OAAO;EACL,MAdsB,GAAgB,GAAM,GAAU,GAAkB;GACxE;GACA;GACA;GACA;EACF,CASQ;EACN,YARiB,GAAuB,GAAU,GAAc,GAAe;GAC/E;GACA;GACA;EACF,CAIE;EACA,UAAU;CACZ;AACF;AAMA,SAAS,GACP,GACA,GACA,GACQ;CAMR,OALI,EAAgB,WAAW,IAAU,KACjB,EAAgB,KAAI,MAAS;EACnD,IAAM,IAAQ,EAAI;EAClB,OAAO,IAAgB,EAAc,OAAO,CAAK,CAAC,IAAI,OAAO,CAAK;CACpE,CACO,EAAgB,KAAK,KAAK;AACnC;AAMA,SAAS,GACP,GACA,GACA,GACA,GAMO;CACP,IAAM,EAAE,oBAAiB,kBAAe,iBAAc,qBAAkB,GAClE,oBAAoB,IAAI,IAAiC;CAE/D,KAAK,IAAM,KAAO,GAAM;EACtB,IAAM,IAAW,EAAI,kBACf,IAAc,EAAI;EAExB,AAAK,EAAkB,IAAI,CAAQ,KACjC,EAAkB,IAAI,GAAU;GAC9B,kBAAkB;GAElB,eAAe,MAAgB,IAAI,EAAI,KAAoB,KAAA;EAC7D,CAAC;EAGH,IAAM,IAAa,EAAkB,IAAI,CAAQ;EAGjD,AAAI,CAAC,EAAW,iBAAiB,EAAI,OACnC,EAAW,gBAAgB,EAAI;EAIjC,IAAM,IAAkB,IACpB,GAAqB,GAAK,GAAiB,CAAa,IACxD,IAGE,IAAa,EAAyB,EAAa,MAAgB,IAAI,CAAW;EACxF,KAAK,IAAM,KAAW,GAAU;GAC9B,IAAM,IAAc,IAAgB,EAAc,CAAO,IAAI,GACvD,IAAM,IACR,GAAG,EAAgB,KAAK,EAAY,IAAI,EAAW,KACnD,GAAG,EAAY,IAAI,EAAW;GAClC,EAAW,KAAO,EAAI;EACxB;CACF;CAGA,OAAO,MAAM,KAAK,EAAkB,OAAO,CAAC,EACzC,MAAM,GAAG,MAAM,EAAE,mBAAmB,EAAE,gBAAgB;AAC3D;AAMA,SAAS,GACP,GACA,GACA,GACA,GAKU;CACV,IAAM,EAAE,oBAAiB,0BAAuB,qBAAkB,GAC5D,IAAuB,CAAC,GAIxB,IAAwB,IAC1B,GAA8B,GAAiB,GAAuB,CAAa,IACnF,CAAC,EAAE;CAEP,KAAK,IAAM,KAAY,GACrB,KAAK,IAAM,KAAW,GAAU;EAC9B,IAAM,IAAc,IAAgB,EAAc,CAAO,IAAI;EAC7D,KAAK,IAAI,IAAI,GAAG,IAAI,EAAc,QAAQ,KAAK;GAC7C,IAAM,IAAa,EAAyB,EAAa,MAAM,IAAI,CAAC;GACpE,EAAW,KACT,IACI,GAAG,EAAS,KAAK,EAAY,IAAI,EAAW,KAC5C,GAAG,EAAY,IAAI,EAAW,EACpC;EACF;CACF;CAGF,OAAO;AACT;AAKA,SAAS,GACP,GACA,GACA,GACU;CACV,IAAI,EAAgB,WAAW,GAAG,OAAO,CAAC;CAG1C,IAAI,EAAgB,WAAW,GAG7B,OADe,MAAM,KAAK,EAAa,IADzB,EAAgB,EACkB,KAAK,CAAC,CAC/C,EAAO,KAAI,MAAK,IAAgB,EAAc,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC;CAI7E,IAAM,IAAmB,CAAC,GACpB,IAAc,EAAgB,KAAI,MACtC,MAAM,KAAK,EAAa,IAAI,CAAK,KAAK,CAAC,CAAC,CAC1C;CAEA,SAAS,EAAe,GAAe,GAAyB;EAC9D,IAAI,MAAU,EAAY,QAAQ;GAChC,IAAM,IAAY,EAAQ,KAAI,MAC5B,IAAgB,EAAc,OAAO,CAAC,CAAC,IAAI,OAAO,CAAC,CACrD;GACA,EAAO,KAAK,EAAU,KAAK,KAAK,CAAC;GACjC;EACF;EAEA,KAAK,IAAM,KAAS,EAAY,IAC9B,EAAe,IAAQ,GAAG,CAAC,GAAG,GAAS,CAAK,CAAC;CAEjD;CAGA,OADA,EAAe,GAAG,CAAC,CAAC,GACb;AACT;AAMA,SAAgB,GACd,GACA,GACA,GAIQ;CACR,IAAI,GAAS,eACX,OAAO,OAAO,IAAW;CAG3B,IAAI,GAAa;EACf,IAAM,IAAO,OAAO,KAAgB,WAAW,IAAI,KAAK,CAAW,IAAI;EACvE,IAAI,CAAC,MAAM,EAAK,QAAQ,CAAC,GACvB,OAAO,EAAK,mBAAmB,SAAS;GACtC,OAAO,GAAS,eAAe,SAAS,SAAS;GACjD,KAAK;EACP,CAAC;CAEL;CAEA,OAAO,GAAG,IAAW;AACvB;AAMA,SAAgB,EAAoB,GAAmB,GAAiC;CACtF,IAAI,EAAa,SAAS,GAAG,OAAO;CAGpC,KAAK,IAAI,IAAI,GAAG,IAAI,EAAa,QAAQ,KAAK;EAC5C,IAAM,IAAa,EAAyB,EAAa,IAAI,CAAC;EAC9D,IAAI,EAAU,SAAS,IAAI,EAAW,EAAE,GACtC,OAAO;CAEX;CAGA,OAAO,EAAU,SAAS,QAAQ;AACpC;AAKA,SAAgB,EACd,IAAuC,UACnB;CACpB,QAAQ,GAAR;EACE,KAAK,SACH;EACF,KAAK,UACH,OAAO;EACT,KAAK,UACH,OAAO;EACT,SACE,OAAO;CACX;AACF;;;AC7aA,SAAwB,EAAe,EAAE,aAAU,YAAS,UAA+B;CACzF,IAAM,EAAE,SAAM,EAAe,GAGvB,IAAe,EAAuB,IAAI,GAC1C,CAAC,GAAS,KAAc,EAAS,EAAK,GACtC,CAAC,GAAe,KAAoB,EAAS;EAAE,OAAO;EAAG,QAAQ;CAAE,CAAC,GACpE,IAAU,EAAO;EAAE,OAAO;EAAG,QAAQ;CAAE,CAAC,GACxC,IAAW,EAAO,EAAK,GACvB,IAAiB,EAAiD,IAAI,GACtE,IAAW,EAAsB,IAAI;CAG3C,QAAsB;EACpB,IAAI,IAAU,IACV,IAAwC,MAEtC,UAAyB;GAE7B,IADA,EAAS,UAAU,MACf,CAAC,GAAS;GACd,IAAM,IAAU,EAAe;GAE/B,IADA,EAAe,UAAU,MACrB,CAAC,GAAS;GAEd,IAAM,IAAY,KAAK,MAAM,EAAQ,KAAK,GACpC,IAAa,KAAK,MAAM,EAAQ,MAAM;GAC5C,IAAI,KAAa,KAAK,KAAc,GAAG;GAEvC,IAAM,IACJ,EAAQ,QAAQ,UAAU,KAAa,EAAQ,QAAQ,WAAW,GAC9D,IAAoB,CAAC,EAAS;GAEhC,CAAC,KAAe,CAAC,MAErB,EAAQ,UAAU;IAAE,OAAO;IAAW,QAAQ;GAAW,GACzD,EAAS,UAAU,IAInB,QAAsB;IAIpB,AAHI,KACF,EAAiB;KAAE,OAAO;KAAW,QAAQ;IAAW,CAAC,GAEvD,KACF,EAAW,EAAI;GAEnB,CAAC;EACH,GAEM,KAAsB,GAAe,MAAmB;GAE5D,AADA,EAAe,UAAU;IAAE;IAAO;GAAO,GACrC,EAAS,YAAY,SACvB,EAAS,UAAU,sBAAsB,CAAgB;EAE7D;EA+BA,OAfA,IAAiB,IAAI,gBAAgB,MAAY;GAC/C,KAAK,IAAM,KAAS,GAAS;IAC3B,IAAM,EAAE,UAAO,QAAQ,MAAgB,EAAM;IAC7C,AAAI,IAAQ,KAAK,IAAc,KAC7B,EAAmB,GAAO,CAAW;GAEzC;EACF,CAAC,GAEG,EAAa,YACf,EAAe,QAAQ,EAAa,OAAO,UAxBZ;GAC/B,IAAI,CAAC,KAAW,CAAC,EAAa,SAAS;GAEvC,IAAM,IAAO,EAAa,QAAQ,sBAAsB,GAElD,IAAQ,KAAK,IAAI,EAAa,QAAQ,aAAa,EAAK,KAAK,GAC7D,IAAiB,KAAK,IAAI,EAAa,QAAQ,cAAc,EAAK,MAAM;GAE9E,AAAI,IAAQ,KAAK,IAAiB,KAChC,EAAmB,GAAO,CAAc;EAE5C,GAeqB,UAGR;GAMX,AALA,IAAU,IACN,EAAS,YAAY,SACvB,qBAAqB,EAAS,OAAO,GACrC,EAAS,UAAU,OAErB,GAAgB,WAAW;EAC7B;CACF,GAAG,CAAC,CAAC;CAEL,IAAI;EAsCF,OArCI,MAAW,SAIX,kBAAC,OAAD;GACE,KAAK;GACL,WAAU;GACV,OAAO;IAAE,WAAW;IAAS,UAAU;IAAS,UAAU;IAAU,YAAY;GAAO;aAEtF,KAAW,EAAc,QAAQ,KAAK,EAAc,SAAS,IAC5D,kBAAC,GAAD;IACE,OAAO,EAAc;IACrB,QAAQ,EAAc,SAAS;IAC/B,UAAU;IACV,OAAO,EAAE,WAAW,OAAO;IAE1B;GACkB,CAAA,IAErB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD,EAAkB,MAAK,KAAM,CAAA;GAC1B,CAAA;EAEJ,CAAA,IAeP,kBAAC,OAAD;GACE,KAAK;GACL,WAAU;GACV,OAAO;IAXT,QAAQ,OAAO,KAAW,WAAW,GAAG,EAAO,MAAM;IACrD,OAAO;IACP,WAAW;IACX,UAAU;IACV,YAAY;IAOkB,UAAU;GAAS;aAE9C,KAAW,EAAc,QAAQ,KAAK,EAAc,SAAS,IAC5D,kBAAC,GAAD;IACE,OAAO,EAAc;IACrB,QAAQ,EAAc,SAAS;IAC/B,UAAU;IACV,OAAO,EAAE,WAAW,OAAO;IAE1B;GACkB,CAAA,IAErB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD,EAAkB,MAAK,KAAM,CAAA;GAC1B,CAAA;EAEJ,CAAA;CAET,SAAS,GAAO;EAGd,OACE,kBAAC,OAAD;GACE,WAAU;GACV,OAAO;IAAE;IAAQ,aAAa;IAAoB,iBAAiB;GAAoB;aAFzF,CAIE,kBAAC,OAAD;IAAK,WAAU;cAA0D,EAAE,+BAA+B;GAAO,CAAA,GACjH,kBAAC,OAAD;IAAK,WAAU;cACZ,aAAiB,QAAQ,EAAM,UAAU;GACvC,CAAA,CACF;;CAET;AACF;;;AC1KA,IAAM,MAAoB,GAAY,MAChC,KAAU,OACL,CAAC,WAAW,CAAI,IAElB,CAAC,EAAmB,CAAK,GAAG,CAAI;AAGzC,SAAwB,EAAa,EAAE,cAAW,qBAAqC;CACrF,OACE,kBAAC,GAAD;EACE,WAAW,KAAa;EACR;EAChB,cAAc;GACZ,iBAAiB;GACjB,QAAQ;GACR,cAAc;GACd,UAAU;GACV,OAAO;GACP,WAAW;GACX,SAAS;EACX;CACD,CAAA;AAEL;;;ACbA,IAAM,KAAmD,EACvD,OAAI,GACJ,OAAI,GACJ,YACA,kBACA,WAAQ,QACJ;CACJ,IAAI,CAAC,GAAS,OAAO;CAErB,IAAM,IAAe,IACjB,EAAc,EAAQ,OAAO,CAAK,IAClC,OAAO,EAAQ,KAAK;CAExB,OACE,kBAAC,KAAD;EAAG,WAAW,aAAa,EAAE,GAAG,EAAE;YAChC,kBAAC,QAAD;GACE,GAAG;GACH,GAAG;GACH,IAAI;GACJ,YAAW;GACX,MAAK;GACL,UAAU;GACV,WAAU;aAET;EACG,CAAA;CACL,CAAA;AAEP;;;ACxBA,SAAgB,EAAgB,EAC9B,WACA,SACA,cAAW,0BAC+B;CAC1C,IAAM,EAAE,SAAM,EAAe;CAC7B,OACE,kBAAC,OAAD;EAAK,WAAU;EAAyE,OAAO,EAAE,UAAO;YACtG,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAuC,EAAE,CAAQ;GAAO,CAAA,GACtE,KAAQ,QAAQ,kBAAC,OAAD;IAAK,WAAU;cAAqC;GAAU,CAAA,CAC5E;;CACF,CAAA;AAET;AAMA,SAAgB,EAAiB,EAAE,WAAQ,WAAyB;CAClE,IAAM,EAAE,SAAM,EAAe;CAC7B,OACE,kBAAC,OAAD;EAAK,WAAU;EAAsE,OAAO,EAAE,UAAO;YACnG,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAuC,EAAE,2BAA2B;GAAO,CAAA,GACzF,KAAQ,QAAQ,kBAAC,OAAD;IAAK,WAAU;cAAc;GAAU,CAAA,CACrD;;CACF,CAAA;AAET;AAKA,SAAgB,EAAiB,EAC/B,WACA,cACA,YAMC;CACD,IAAM,EAAE,SAAM,EAAe;CAC7B,OACE,kBAAC,OAAD;EAAK,WAAU;EAAuF,OAAO,EAAE,UAAO;YACpH,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAuC,EAAE,4BAA4B,EAAE,aAAU,CAAC;IAAO,CAAA;IACxG,kBAAC,OAAD;KAAK,WAAU;eAAsB,aAAiB,QAAQ,EAAM,UAAU,EAAE,4BAA4B;IAAO,CAAA;IACnH,kBAAC,OAAD;KAAK,WAAU;eAAiC,EAAE,2BAA2B;IAAO,CAAA;GACjF;;CACF,CAAA;AAET;;;AC7CA,SAAgB,EAAuB,GAAwD;CAC7F,IAAI,GACA,IAAwB,CAAC,GACzB,IAAyB,CAAC,GAC1B,IAAuC;CAmB3C,OAjBI,GAAa,SAAS,GAAa,SAErC,IAAa,MAAM,QAAQ,EAAY,KAAK,IAAI,EAAY,MAAM,KAAK,EAAY,OACnF,IAAc,MAAM,QAAQ,EAAY,KAAK,IAAI,EAAY,QAAQ,CAAC,EAAY,KAAK,GACvF,IAAe,EAAY,UAAU,CAAC,KAC7B,GAAa,KAAK,GAAa,KAExC,IAAa,EAAY,GACzB,IAAc,MAAM,QAAQ,EAAY,CAAC,IAAI,EAAY,IAAI,CAAC,EAAY,CAAC,KAE3E,IAAY,eAGV,CAAC,MAAc,CAAC,KAAc,EAAY,WAAW,OACvD,IAAY,eAGP;EAAE;EAAY;EAAa;EAAc;CAAU;AAC5D;;;ACjDA,SAAgB,EAAkB,GAAgC;CAChE,IAAI,CAAC,KAAgB,OAAO,KAAiB,UAC3C,OAAO,CAAC;CAGV,IAAM,IAAU,EAAa,KAAK;CAClC,IAAI,CAAC,GACH,OAAO,CAAC;CAGV,IAAI;EAEF,IAAM,IAAS,EACZ,MAAM,GAAG,EACT,KAAI,MAAO,EAAI,KAAK,CAAC,EACrB,QAAO,MAAO,MAAQ,EAAE,EACxB,KAAI,MAAO;GACV,IAAM,IAAM,WAAW,CAAG;GAC1B,IAAI,MAAM,CAAG,GACX,MAAU,MAAM,0BAA0B,GAAK;GAEjD,OAAO;EACT,CAAC;EAEH,OAAO,EAAO,SAAS,IAAI,IAAS,CAAC;CACvC,SAAS,GAAO;EAEd,OADA,QAAQ,KAAK,kCAAkC,CAAK,GAC7C,CAAC;CACV;AACF;AAQA,SAAgB,GAAmB,GAAmB,GAA8B;CAClF,IAAI,EAAQ,WAAW,KAAK,KAAc,GACxC,OAAO,CAAC;CAIV,IAAI,EAAQ,WAAW,GACrB,OAAW,MAAM,CAAU,EAAE,KAAK,EAAQ,EAAE;CAI9C,IAAM,IAAmB,CAAC,GACpB,IAAgB,KAAK,MAAM,IAAa,EAAQ,MAAM,GACtD,IAAY,IAAa,EAAQ,QAEnC,IAAe;CAEnB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAQ,QAAQ,KAAK;EAGvC,IAAM,IAAY,IAAiB,MAAI;EAGvC,KAAK,IAAI,IAAI,GAAG,IAAI,GAAW,KAC7B,EAAO,OAAkB,EAAQ;CAErC;CAEA,OAAO;AACT;AAQA,SAAgB,GAAkB,GAAgB,GAAwB;CAIxE,OAHI,MAAW,IACN,MAAW,IAAI,IAAK,IAAS,IAAI,MAAM,QAEvC,IAAS,KAAU,IAAU;AACxC;AAQA,SAAgB,GAAe,GAAkB,IAAmB,GAAW;CAE7E,OAAO,GADM,KAAY,IAAI,MAAM,KAClB,EAAS,QAAQ,CAAQ,EAAE;AAC9C;;;ACvEA,SAAgB,EACd,GACA,GACc;CAId,OAAO;EAAE,cAHY,EAAY,MAAM,MAAU,EAAgB,OAAW,OAGnE;EAAc,gBAFA,EAAY,QAAQ,OAAO,EAAgB,MAAM,YAAY,MAE7D;EAAgB,iBADf,EAAY,QAAQ,MAAM,EAAgB,OAAO,OAClC;CAAgB;AACzD;AAGA,SAAgB,EAAqB,GAAuB;CAC1D,OAAO;EACL,GAAG;EACH,MAAM;EACN,OAAO,IAAe,KAAK;CAC7B;AACF;AAMA,SAAgB,EACd,GACA,GACqD;CAErD,IAAM,IAAgB,GADD,EAAkB,KAAU,EACR,GAAc,EAAU,MAAM;CAIvE,OAAO;EAAE;EAAe,mBAHE,EAAc,SAAS,IAC7C,EAAU,KAAK,GAAW,OAAW;GAAE,GAAG;GAAW,UAAU,EAAc,MAAU;EAAK,EAAE,IAC9F;CACsC;AAC5C;AASA,SAAgB,EACd,EAAE,iBAAc,mBAAgB,sBAChC,GACA,GACA,GACA,IAAiB,IACjB;CACA,OACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;EACE,SAAQ;EACR,aAAY;EACZ,MAAM,EAAE,UAAU,GAAG;EACrB,eACE,KACK,MAAM,IAAI,IAAI,KAAK,QAAQ,CAAC,EAAE,KAC/B,KACG,MAAU,EAAgB,GAAO,CAAe,IACjD,KAAA;EAER,QAAQ,IAAiB,CAAC,GAAG,CAAC,IAAI,KAAA;EAClC,OACE,IACI,KAAA,IACA,EAAe,SAAS,IACtB;GACE,OAAO,GAAiB,SAAS,EAAc,EAAe,EAAE;GAChE,OAAO;GACP,UAAU;GACV,OAAO;IAAE,YAAY;IAAU,UAAU;GAAO;EAClD,IACA,KAAA;CAET,CAAA,GACA,KACC,kBAAC,GAAD;EACE,SAAQ;EACR,aAAY;EACZ,MAAM,EAAE,UAAU,GAAG;EACrB,eAAe,KAAoB,MAAU,EAAgB,GAAO,CAAgB,IAAI,KAAA;EACxF,OACE,EAAgB,SAAS,IACrB;GACE,OAAO,GAAkB,SAAS,EAAc,EAAgB,EAAE;GAClE,OAAO;GACP,UAAU;GACV,OAAO;IAAE,YAAY;IAAU,UAAU;GAAO;EAClD,IACA,KAAA;CAEP,CAAA,CAEK,EAAA,CAAA;AAEd;AAMA,SAAgB,EAAuB,GAAyB;CAE9D,OADI,EAAc,WAAW,IAAU,OAErC,kBAAC,GAAD,EAAA,UAAA,CAEE,kBAAC,GAAD;EACE,MAAK;EACL,SAAQ;EACR,SAAQ;EACR,QAAO;EACP,aAAa;EACb,KAAK;EACL,WAAW;EACX,cAAc;CACf,CAAA,GAED,kBAAC,GAAD;EACE,MAAK;EACL,SAAQ;EACR,SAAQ;EACR,MAAK;EACL,QAAO;EACP,aAAa;EACb,iBAAgB;EAChB,KAAK;EACL,WAAW;EACX,cAAc;CACf,CAAA,CACO,EAAA,CAAA;AAEd;AAiBA,SAAgB,EAA8B,EAC5C,oBACA,qBACA,oBACA,iBACA,oBAAiB,MACS;CAC1B,QAAQ,GAAY,MAA0B;EAC5C,IAAI,KAAU,MACZ,OAAO,CAAC,WAAW,CAAI;EAEzB,IAAI,MAAS,UAEX,OAAO,CAAC,EAAgB,GAAO,CAAe,GAAG,cAAc;EAEjE,IAAI,KAAkB,OAAO,KAAU,UACrC,OAAO,CAAC,IAAI,IAAQ,KAAK,QAAQ,CAAC,EAAE,IAAI,CAAI;EAG9C,IAAM,IAAgB,EAAa,CAAI;EAGvC,OAAO,CAAC,EAAgB,IAFT,KAAiB,EAAgB,OAAmB,UAAU,UAAU,WACvD,UAAU,IAAmB,CAClB,GAAG,CAAI;CACpD;AACF;AAOA,SAAgB,EAAkB,EAChC,SACA,aACA,eACA,YACA,cAOC;CAED,OADK,IAEH,kBAAC,GAAD;EACE,cAAc;GAAE,UAAU;GAAQ,YAAY,GAAG,EAAW;EAAI;EACtD;EACV,UAAU;EACV,QAAO;EACP,OAAM;EACN,eAAc;EACd,eAAe,MAAM,EAAQ,OAAO,EAAE,WAAW,EAAE,CAAC;EACpD,cAAc;CACf,CAAA,IAXe;AAapB;;;ACxLA,SAAgB,EAAe,EAC7B,UACA,UACA,iBACA,kBACA,cACA,qBACA,kBACwE;CACxE,IAAM,EAAE,OAAI,OAAI,YAAS,WAAQ;CACjC,IAAI,MAAO,KAAA,KAAa,MAAO,KAAA,GAAW,OAAO;CAEjD,IAAM,KAAe,MAA4B;EAG/C,AAFA,EAAM,gBAAgB,GACtB,EAAM,eAAe,GACjB,KACF,EAAiB;GACf,WAAW;GACX,cAAc,KAAiB;GAC/B,QAAQ,EAAQ;GAChB,UAAU;IAAE,GAAG,EAAM;IAAS,GAAG,EAAM;GAAQ;GAC/C,aAAa;EACf,CAAC;CAEL;CA0BA,OAxBI,KAAgB,IAEhB,kBAAC,KAAD,EAAA,UAAA,CAEE,kBAAC,UAAD;EAAY;EAAQ;EAAI,GAAG;EAAG,MAAK;EAAoB,OAAO,EAAE,eAAe,OAAO;CAAI,CAAA,GAE1F,kBAAC,UAAD;EACM;EACA;EACJ,GAAG;EACH,MAAK;EACL,QAAQ;EACR,aAAa;EACb,QAAO;EACP,UAAU,MAA0C;GAClD,EAAY,CAAgC;EAC9C;CACD,CAAA,CACA,EAAA,GAhBK,CAgBL,IAIF,IAEE,kBAAC,UAAD;EAAsB;EAAQ;EAAI,GAAG;EAAG,MAAM;CAAQ,GAAzC,CAAyC,IAFpC;AAG3B;AAMA,SAAS,EACP,GACA,GACA,GACA,GACQ;CAKR,OAJI,IAEK,EAAgB,GADH,EAAoB,GAAa,CACV,CAAW,IAEjD,UAAU,IAAgB;AACnC;AAMA,SAAgB,GACd,GACA,GACA,GACA,GACiE;CAC5D,OACL,QAAQ,GAAwB,MAAkB;EAChD,IAAM,IAAM,EAAU;EACtB,OAAO,EAAqB,GAAK,eAAe,GAAa,GAAY,OAAO,CAAK,CAAC;CACxF;AACF;AAMA,SAAgB,GACd,GACA,GACA,GACoD;CAC/C,OACL,QAAQ,GAAY,MAEX,GADK,KAAW,EAAQ,SAAS,IAAI,EAAQ,IAAI,UAAU,KAAA,IACjC,eAAe,GAAa,GAAY,OAAO,CAAK,CAAC;AAE1F;AAOA,SAAgB,GACd,GACA,GACwG;CACxG,IAAM,IAAY,GAAe,cAAc,GAAe,UAAU,WAAW,SAC7E,IAAuB,MAAc,UAAU,CAAC,GAChD,IAA0B,MAAc,aAAa,CAAC;CAC5D,OAAO;EACL;EACA;EACA,aAAa,IAA0B,WAAW,KAAA;CACpD;AACF;AAGA,SAAgB,EACd,GACA,GACwB;CACxB,IAAM,IAAkC,CAAC;CAIzC,OAHA,EAAY,SAAS,MAAU;EAC7B,EAAQ,EAAc,CAAK,KAAK;CAClC,CAAC,GACM;AACT;AAMA,SAAgB,GACd,GAC2C;CAC3C,QAAQ,MAA0C;EAChD,IAAI,EAAiB,IAAY,OAAO,EAAiB;EAEzD,IAAI,EAAU,SAAS,KAAM;EAE7B,IAAM,IADgB,EAAU,QAAQ,2BAA2B,EACrD,EAAc,MAAM,KAAK;EAEvC,OAAO,EADc,EAAM,EAAM,SAAS;CAE5C;AACF;AAGA,SAAgB,GAAe,GAAwC,GAAuB;CAC5F,IAAM,IAAS,GAAc;CAC7B,OAAQ,KAAU,EAAO,IAAQ,EAAO,WAAY,EAAa,IAAQ,EAAa;AACxF;AAmBA,SAAS,GAAoB,GAAmB,GAAe,GAA6C;CAC1G,IAAM,EACJ,iBAAc,iBAAc,oBAAiB,kBAAe,iBAC5D,iBAAc,qBAAkB,sBAAmB,kBAAe,CAAC,GACnE,sBAAmB,UAAU,wBAAqB,OAChD,GAEE,IAAgB,EAAa,CAAS,GACtC,IAAS,KAAiB,EAAgB,OAAmB,UAAU,UAAU,QAEjF,IAAgB,EAAQ,KAAsB,EAAoB,GAAW,CAAY,GACzF,IAAkB,IAAgB,EAA8B,CAAgB,IAAI,KAAA,GACpF,IAAU,IAAgB,IAAqB,GAC/C,IAAY,GAAe,GAAc,CAAK,GAEhD,IAAgB;CAGpB,OAFI,MAAe,IAAgB,MAAkB,IAAY,IAAI,KAGnE,kBAAC,GAAD;EAEE,MAAK;EACL,SAAS;EACT,SAAS;EACT,QAAQ;EACR,aAAa,IAAgB,MAAM;EACnC,iBAAiB;EACjB,KAAK,IAAgB,MAAS,MAAe,EAAe;GAC1D;GAAO,OAAO;GAAW;GAAc;GAAe;GAAW;GAAkB,aAAa;EAClG,CAAC;EACD,WAAW;EACI;EACD;CACf,GAbM,CAaN;AAEL;AAGA,SAAgB,GAAiB,GAA+C;CAC9E,OAAO,EAAK,WAAW,KAAK,GAAW,MAAU,GAAoB,GAAW,GAAO,CAAI,CAAC;AAC9F;AAeA,SAAS,GAAoB,GAAmB,GAAe,GAA6C;CAC1G,IAAM,EACJ,iBAAc,qBAAkB,oBAAiB,kBAAe,iBAChE,gBAAa,iBAAc,wBACzB,GAEE,IAAgB,EAAiB,IACjC,IAAS,KAAiB,EAAgB,OAAmB,UAAU,UAAU,QACjF,IAAY,GAAe,GAAc,CAAK,GAEhD,IAAc,IACd,IAAgB;CACpB,IAAI,GAAe;EACjB,IAAM,IAAS,MAAkB;EAEjC,AADA,IAAc,IAAS,KAAM,IAC7B,IAAgB,IAAS,IAAI;CAC/B;CAEA,OACE,kBAAC,GAAD;EAEE,MAAK;EACL,SAAS;EACT,SAAS;EACT,SAAS,IAAc,UAAU,KAAA;EACjC,QAAQ;EACR,MAAM;EACO;EACb,aAAa;EACE;EACD;EACd,MAAM,MAAe,EAAe;GAClC;GAAO,OAAO;GAAW;GAAc;GAAe;GAAW;GAAkB,aAAa;EAClG,CAAC;EACD,WAAW;CACZ,GAfM,CAeN;AAEL;AAGA,SAAgB,GAAiB,GAA+C;CAC9E,OAAO,EAAK,WAAW,KAAK,GAAW,MAAU,GAAoB,GAAW,GAAO,CAAI,CAAC;AAC9F;AAgBA,SAAgB,GAAoB,GAOhB;CAClB,IAAM,EAAE,SAAM,eAAY,gBAAa,iBAAc,gBAAa,qBAAkB,GAC9E,IAAoB,EAAiB,CAAI;CAE/C,IAAI,GAAmB;EACrB,IAAM,IAAU,EAAwB,GAAM,GAAa,GAAY,CAAa;EACpF,OAAO;GACL,WAAW,EAAQ;GACnB,YAAY,EAAQ;GACpB,mBAAmB;GACnB;GACA,cAAc,EAAgB,CAAI;EACpC;CACF;CAEA,IAAM,IAAW,EAA6B,GAAM,GAAY,GAAa,GAAa,GAAc,CAAa;CACrH,OAAO;EACL,WAAW,EAAS;EACpB,YAAY,EAAS;EACrB,mBAAmB;EACnB;EACA,cAAc,CAAC;CACjB;AACF;;;mDC1VM,KAAY,EAAM,KAAK,SAAmB,EAC9C,SACA,gBACA,mBAAgB,CAAC,GACjB,gBACA,YAAS,QACT,iBACA,qBACA,mBACa;CACb,IAAM,EAAE,SAAM,EAAe,GACvB,CAAC,GAAe,KAAoB,EAAwB,IAAI,GAEhE,IAAgB,EAAkB,GAGlC,EAAE,eAAY,gBAAa,iBAAc,iBAAc,QACrD,EAAuB,CAAW,GACxC,CAAC,CAAW,CACd,GAGM,IAAkB,QACtB,GAAa,mBAAmB,CAAC,GACjC,CAAC,GAAa,eAAe,CAC/B,GAIM,EAAE,MAAM,GAAW,kBAAe,QAClC,KAAa,CAAC,KAAQ,EAAK,WAAW,KAAK,CAAC,IACvC;EAAE,MAAM,CAAC;EAAG,YAAY,CAAC;CAAE,IAE7B,EACL,GACA,GACA,GACA,GACA,GACA,CACF,GACC;EAAC;EAAM;EAAY;EAAa;EAAa;EAAc;EAAe;CAAS,CAAC;CAEvF,IAAI;EACF,IAAM,IAAoB;GACxB,YAAY,GAAe,cAAc;GACzC,UAAU,GAAe,YAAY;GACrC,aAAa,GAAe,eAAe;GAC3C,cAAc,GAAe,gBAAgB;EAC/C,GAEM,IAAiB,GAAe,kBAAkB,IAGlD,IAAkB,GAAe,iBACjC,IAAmB,GAAe;EAExC,IAAI,CAAC,KAAQ,EAAK,WAAW,GAC3B,OAAO,kBAAC,GAAD;GAAyB;GAAQ,MAAM,EAAE,+BAA+B;EAAI,CAAA;EAGrF,IAAI,GACF,OAAO,kBAAC,GAAD;GAA0B;GAAQ,MAAM,EAAE,iCAAiC,GAAW;EAAI,CAAA;EAInG,IAAM,IAAmB,EAAyB,GAAa,CAAa,GAGtE,IAAW,EAAgB,GAAa,CAAe,GACvD,EAAE,oBAAiB,GAGnB,EAAE,yBAAsB,4BAAyB,mBACrD,GAAoB,GAAe,CAAY,GAG3C,IAAa,EAAkB,YAG/B,IAAe,EAAqB,CAAY,GAGhD,EAAE,kBAAe,yBAAsB,EAAe,GAAW,GAAe,MAAM;EAa5F,OAVI,CAAC,KAAa,EAAU,WAAW,IAEnC,kBAAC,GAAD;GACU;GACR,UAAS;GACT,MAAK;EACN,CAAA,IAKH,kBAAC,GAAD;GAAwB;aACtB,kBAAC,GAAD;IAAe,MAAM;IAAmB,QAAQ;IAA2B;IAAa,oBAAoB;cAA5G;KACG,EAAkB,YAAY,kBAAC,GAAD;MAAe,iBAAgB;MAAM,OAAO,EAAE,eAAe,OAAO;KAAI,CAAA;KACvG,kBAAC,IAAD;MAAO,SAAQ;MAAO,MAAK;MAAW,MAAM,kBAAC,GAAD,CAAkB,CAAA;MAAG,QAAQ;MAAI,UAAU,IAAiB,IAAI,KAAA;KAAY,CAAA;KACvH,EAAgB,GAAU,GAAe,GAAiB,GAAkB,CAAuB;KACnG,EAAkB,eACjB,kBAAC,GAAD,EACE,WAAW,EAA8B;MACvC;MACA;MACA;MACA,eAAe,MAAS,EAAiB;MACzC,gBAAgB;KAClB,CAAC,EACF,CAAA;KAEF,EAAkB;MACjB,MAAM;MACN,UAAU;MACV,YAAY;MACZ,SAAS;MACT,eAAe,EAAiB,IAAI;KACtC,CAAC;KACA,GAAiB;MAChB;MACA;MACA;MACA;MACA;MACA,cAAc,EAAkB;MAChC,aAAa;MACb;MACA;KACF,CAAC;KACA,EAAuB,CAAa;IACxB;;EACD,CAAA;CAEpB,SAAS,GAAO;EACd,OAAO,kBAAC,GAAD;GAA0B;GAAQ,WAAU;GAAoB;EAAQ,CAAA;CACjF;AACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"chart-area-of01_62R.js","names":[],"sources":["../../../src/client/components/LoadingIndicator.tsx","../../../src/client/utils/comparisonUtils.ts","../../../src/client/components/charts/ChartContainer.tsx","../../../src/client/components/charts/ChartTooltip.tsx","../../../src/client/components/charts/AngledXAxisTick.tsx","../../../src/client/components/charts/ChartStates.tsx","../../../src/client/components/charts/chartAxisResolution.ts","../../../src/client/utils/targetUtils.ts","../../../src/client/components/charts/chartScaffolding.tsx","../../../src/client/components/charts/cartesianChartHelpers.tsx","../../../src/client/components/charts/AreaChart.tsx"],"sourcesContent":["/**\n * LoadingIndicator Component\n *\n * A centralized, theme-aware loading spinner that uses CSS variables\n * for consistent styling across all drizzle-cube components.\n *\n * Can be overridden at the Dashboard or Portlet level by passing a\n * custom loadingComponent prop.\n */\n\nexport interface LoadingIndicatorProps {\n /** Size variant: 'xs' (12px), 'sm' (24px), 'md' (32px), 'lg' (48px) */\n size?: 'xs' | 'sm' | 'md' | 'lg'\n /** Additional CSS classes */\n className?: string\n}\n\nconst sizeClasses = {\n xs: 'dc:h-3 dc:w-3',\n sm: 'dc:h-6 dc:w-6',\n md: 'dc:h-8 dc:w-8',\n lg: 'dc:h-12 dc:w-12'\n}\n\nexport default function LoadingIndicator({\n size = 'md',\n className = ''\n}: LoadingIndicatorProps) {\n return (\n <div\n className={`dc:animate-spin dc:rounded-full dc:border-b-2 ${sizeClasses[size]} ${className}`}\n style={{ borderBottomColor: 'var(--dc-primary)' }}\n role=\"status\"\n aria-label=\"Loading\"\n />\n )\n}\n","/**\n * Comparison Data Utilities\n * Handles period-over-period comparison data transformation for charts\n *\n * These utilities detect comparison query results (with __periodIndex metadata)\n * and transform them for visualization in either 'separate' or 'overlay' mode.\n */\n\n/**\n * Check if data contains comparison period metadata\n */\nexport function isComparisonData(data: any[]): boolean {\n return data.length > 0 && '__periodIndex' in data[0]\n}\n\n/**\n * Get unique period labels from comparison data\n */\nexport function getPeriodLabels(data: any[]): string[] {\n if (!isComparisonData(data)) return []\n\n const labels = new Set<string>()\n for (const row of data) {\n if (row.__period) {\n labels.add(row.__period)\n }\n }\n return Array.from(labels)\n}\n\n/**\n * Get period indices from comparison data\n */\nexport function getPeriodIndices(data: any[]): number[] {\n if (!isComparisonData(data)) return []\n\n const indices = new Set<number>()\n for (const row of data) {\n if (typeof row.__periodIndex === 'number') {\n indices.add(row.__periodIndex)\n }\n }\n return Array.from(indices).sort((a, b) => a - b)\n}\n\n/**\n * Generate a short label for a period (used in legends)\n * Uses simple \"Current\" / \"Prior\" labels for clarity\n */\nexport function generatePeriodShortLabel(_periodLabel: string, index: number): string {\n return index === 0 ? 'Current' : 'Prior'\n}\n\n/**\n * Transform comparison data for 'separate' mode\n * Returns data with each row having its original measure values\n * Series are created per period (e.g., \"totalLinesOfCode (Current)\", \"totalLinesOfCode (Prior)\")\n */\nexport function transformForSeparateMode(\n data: any[],\n measures: string[],\n _timeDimensionKey: string\n): { data: any[], seriesKeys: string[] } {\n if (!isComparisonData(data)) {\n return { data, seriesKeys: measures }\n }\n\n const periodLabels = getPeriodLabels(data)\n const seriesKeys: string[] = []\n\n // Create series key for each measure + period combination\n for (const measure of measures) {\n for (let i = 0; i < periodLabels.length; i++) {\n const shortLabel = generatePeriodShortLabel(periodLabels[i], i)\n const key = `${measure} (${shortLabel})`\n seriesKeys.push(key)\n }\n }\n\n // Data already has period info, just return as-is for separate mode\n // The chart will group by time dimension and show multiple lines\n return { data, seriesKeys }\n}\n\n/**\n * Detect dimension fields in comparison data\n * Returns fields that are not measures, time dimension, or metadata fields\n */\nfunction detectDimensionFields(\n data: any[],\n measures: string[],\n timeDimensionKey: string\n): string[] {\n if (data.length === 0) return []\n\n const metadataFields = ['__period', '__periodIndex', '__periodDayIndex']\n const firstRow = data[0]\n const dimensions: string[] = []\n\n for (const key of Object.keys(firstRow)) {\n if (\n !measures.includes(key) &&\n key !== timeDimensionKey &&\n !metadataFields.includes(key)\n ) {\n dimensions.push(key)\n }\n }\n\n return dimensions\n}\n\n/**\n * Get unique dimension values from data for a set of dimension fields\n */\nfunction getUniqueDimensionValues(\n data: any[],\n dimensionFields: string[]\n): Map<string, Set<any>> {\n const values = new Map<string, Set<any>>()\n\n for (const field of dimensionFields) {\n values.set(field, new Set())\n }\n\n for (const row of data) {\n for (const field of dimensionFields) {\n if (row[field] !== undefined && row[field] !== null) {\n values.get(field)!.add(row[field])\n }\n }\n }\n\n return values\n}\n\n/**\n * Transform comparison data for 'overlay' mode\n * Pivots data so each row represents a day-of-period index,\n * with separate columns for each period's values.\n *\n * When dimensions are present, creates series keys that include\n * dimension values: \"Engineering - Total Lines of Code (Current)\"\n *\n * Input (without dimensions):\n * [\n * { date: '2024-01-01', value: 100, __periodIndex: 0, __periodDayIndex: 0 },\n * { date: '2024-01-02', value: 110, __periodIndex: 0, __periodDayIndex: 1 },\n * { date: '2023-01-01', value: 80, __periodIndex: 1, __periodDayIndex: 0 },\n * { date: '2023-01-02', value: 85, __periodIndex: 1, __periodDayIndex: 1 },\n * ]\n *\n * Output (without dimensions):\n * [\n * { __periodDayIndex: 0, 'value (Current)': 100, 'value (Prior)': 80 },\n * { __periodDayIndex: 1, 'value (Current)': 110, 'value (Prior)': 85 },\n * ]\n *\n * Input (with dimensions):\n * [\n * { date: '2024-01-01', 'Dept.name': 'Engineering', value: 100, __periodIndex: 0, __periodDayIndex: 0 },\n * { date: '2024-01-01', 'Dept.name': 'Sales', value: 50, __periodIndex: 0, __periodDayIndex: 0 },\n * { date: '2023-01-01', 'Dept.name': 'Engineering', value: 80, __periodIndex: 1, __periodDayIndex: 0 },\n * { date: '2023-01-01', 'Dept.name': 'Sales', value: 40, __periodIndex: 1, __periodDayIndex: 0 },\n * ]\n *\n * Output (with dimensions):\n * [\n * {\n * __periodDayIndex: 0,\n * 'Engineering - value (Current)': 100,\n * 'Engineering - value (Prior)': 80,\n * 'Sales - value (Current)': 50,\n * 'Sales - value (Prior)': 40,\n * },\n * ]\n */\nexport function transformForOverlayMode(\n data: any[],\n measures: string[],\n timeDimensionKey: string,\n getFieldLabel?: (fieldName: string) => string\n): { data: any[], seriesKeys: string[], xAxisKey: string } {\n if (!isComparisonData(data)) {\n return { data, seriesKeys: measures, xAxisKey: timeDimensionKey }\n }\n\n const periodLabels = getPeriodLabels(data)\n const periodIndices = getPeriodIndices(data)\n\n // Detect dimension fields in the data\n const dimensionFields = detectDimensionFields(data, measures, timeDimensionKey)\n const hasDimensions = dimensionFields.length > 0\n\n // Get unique dimension values for generating series keys\n const uniqueDimensionValues = hasDimensions\n ? getUniqueDimensionValues(data, dimensionFields)\n : null\n\n const transformedData = pivotByDayIndex(data, measures, timeDimensionKey, {\n dimensionFields,\n hasDimensions,\n periodLabels,\n getFieldLabel\n })\n\n const seriesKeys = buildOverlaySeriesKeys(measures, periodLabels, periodIndices, {\n dimensionFields,\n uniqueDimensionValues,\n getFieldLabel\n })\n\n return {\n data: transformedData,\n seriesKeys,\n xAxisKey: '__periodDayIndex'\n }\n}\n\n/**\n * Build the `dimensionValue / dimensionValue` prefix for a comparison row,\n * applying field labels when available. Empty when no dimensions are present.\n */\nfunction buildDimensionPrefix(\n row: any,\n dimensionFields: string[],\n getFieldLabel?: (fieldName: string) => string\n): string {\n if (dimensionFields.length === 0) return ''\n const dimensionValues = dimensionFields.map(field => {\n const value = row[field]\n return getFieldLabel ? getFieldLabel(String(value)) : String(value)\n })\n return dimensionValues.join(' / ')\n}\n\n/**\n * Pivot comparison rows so each output row is one `__periodDayIndex`, with a\n * column per measure/dimension/period combination.\n */\nfunction pivotByDayIndex(\n data: any[],\n measures: string[],\n timeDimensionKey: string,\n opts: {\n dimensionFields: string[]\n hasDimensions: boolean\n periodLabels: string[]\n getFieldLabel?: (fieldName: string) => string\n }\n): any[] {\n const { dimensionFields, hasDimensions, periodLabels, getFieldLabel } = opts\n const groupedByDayIndex = new Map<number, Record<string, any>>()\n\n for (const row of data) {\n const dayIndex = row.__periodDayIndex\n const periodIndex = row.__periodIndex\n\n if (!groupedByDayIndex.has(dayIndex)) {\n groupedByDayIndex.set(dayIndex, {\n __periodDayIndex: dayIndex,\n // Store the date from the first period (index 0) for reference\n __displayDate: periodIndex === 0 ? row[timeDimensionKey] : undefined\n })\n }\n\n const groupedRow = groupedByDayIndex.get(dayIndex)!\n\n // If this is the first period and we don't have a display date yet, add it\n if (!groupedRow.__displayDate && row[timeDimensionKey]) {\n groupedRow.__displayDate = row[timeDimensionKey]\n }\n\n // Build dimension prefix if dimensions are present\n const dimensionPrefix = hasDimensions\n ? buildDimensionPrefix(row, dimensionFields, getFieldLabel)\n : ''\n\n // Add measure values with optional dimension prefix and period suffix\n const shortLabel = generatePeriodShortLabel(periodLabels[periodIndex] || '', periodIndex)\n for (const measure of measures) {\n const displayName = getFieldLabel ? getFieldLabel(measure) : measure\n const key = dimensionPrefix\n ? `${dimensionPrefix} - ${displayName} (${shortLabel})`\n : `${displayName} (${shortLabel})`\n groupedRow[key] = row[measure]\n }\n }\n\n // Convert to array and sort by day index\n return Array.from(groupedByDayIndex.values())\n .sort((a, b) => a.__periodDayIndex - b.__periodDayIndex)\n}\n\n/**\n * Generate the overlay-mode series keys (matching the data column names) for\n * each measure/dimension-combination/period.\n */\nfunction buildOverlaySeriesKeys(\n measures: string[],\n periodLabels: string[],\n periodIndices: number[],\n opts: {\n dimensionFields: string[]\n uniqueDimensionValues: Map<string, Set<any>> | null\n getFieldLabel?: (fieldName: string) => string\n }\n): string[] {\n const { dimensionFields, uniqueDimensionValues, getFieldLabel } = opts\n const seriesKeys: string[] = []\n\n // When dimensions are present, prefix each series key with the combination;\n // otherwise use a single empty prefix to share the measure/period loop.\n const dimensionCombinations = uniqueDimensionValues\n ? generateDimensionCombinations(dimensionFields, uniqueDimensionValues, getFieldLabel)\n : ['']\n\n for (const dimCombo of dimensionCombinations) {\n for (const measure of measures) {\n const displayName = getFieldLabel ? getFieldLabel(measure) : measure\n for (let i = 0; i < periodIndices.length; i++) {\n const shortLabel = generatePeriodShortLabel(periodLabels[i] || '', i)\n seriesKeys.push(\n dimCombo\n ? `${dimCombo} - ${displayName} (${shortLabel})`\n : `${displayName} (${shortLabel})`\n )\n }\n }\n }\n\n return seriesKeys\n}\n\n/**\n * Generate all combinations of dimension values as formatted strings\n */\nfunction generateDimensionCombinations(\n dimensionFields: string[],\n uniqueValues: Map<string, Set<any>>,\n getFieldLabel?: (fieldName: string) => string\n): string[] {\n if (dimensionFields.length === 0) return []\n\n // For single dimension, just return the values\n if (dimensionFields.length === 1) {\n const field = dimensionFields[0]\n const values = Array.from(uniqueValues.get(field) || [])\n return values.map(v => getFieldLabel ? getFieldLabel(String(v)) : String(v))\n }\n\n // For multiple dimensions, generate cartesian product\n const result: string[] = []\n const fieldValues = dimensionFields.map(field =>\n Array.from(uniqueValues.get(field) || [])\n )\n\n function generateCombos(index: number, current: string[]): void {\n if (index === fieldValues.length) {\n const formatted = current.map(v =>\n getFieldLabel ? getFieldLabel(String(v)) : String(v)\n )\n result.push(formatted.join(' / '))\n return\n }\n\n for (const value of fieldValues[index]) {\n generateCombos(index + 1, [...current, value])\n }\n }\n\n generateCombos(0, [])\n return result\n}\n\n/**\n * Format the period day index for display on X-axis\n * Can show as \"Day 1\", \"Day 2\", etc., or as the original date\n */\nexport function formatPeriodDayIndex(\n dayIndex: number,\n displayDate?: string | Date,\n options?: {\n showDayNumber?: boolean\n dateFormat?: 'short' | 'long'\n }\n): string {\n if (options?.showDayNumber) {\n return `Day ${dayIndex + 1}`\n }\n\n if (displayDate) {\n const date = typeof displayDate === 'string' ? new Date(displayDate) : displayDate\n if (!isNaN(date.getTime())) {\n return date.toLocaleDateString('en-US', {\n month: options?.dateFormat === 'long' ? 'long' : 'short',\n day: 'numeric'\n })\n }\n }\n\n return `${dayIndex + 1}`\n}\n\n/**\n * Check if a series key represents a prior period (not the first period)\n * Used for applying different styling to prior period lines\n */\nexport function isPriorPeriodSeries(seriesKey: string, periodLabels: string[]): boolean {\n if (periodLabels.length < 2) return false\n\n // Check if the series key contains any label other than the first period's\n for (let i = 1; i < periodLabels.length; i++) {\n const shortLabel = generatePeriodShortLabel(periodLabels[i], i)\n if (seriesKey.includes(`(${shortLabel})`)) {\n return true\n }\n }\n\n // Also check for \"(Prior\" pattern\n return seriesKey.includes('(Prior')\n}\n\n/**\n * Get the stroke dash array for prior period styling\n */\nexport function getPriorPeriodStrokeDashArray(\n style: 'solid' | 'dashed' | 'dotted' = 'dashed'\n): string | undefined {\n switch (style) {\n case 'solid':\n return undefined\n case 'dashed':\n return '5 5'\n case 'dotted':\n return '2 2'\n default:\n return '5 5'\n }\n}\n","import { ReactElement, useState, useRef, useLayoutEffect, startTransition } from 'react'\nimport { ResponsiveContainer } from 'recharts'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport LoadingIndicator from '../LoadingIndicator.js'\n\ninterface ChartContainerProps {\n children: ReactElement\n height?: string | number\n}\n\nexport default function ChartContainer({ children, height = \"100%\" }: ChartContainerProps) {\n const { t } = useTranslation()\n // Track if container is ready to render ResponsiveContainer\n // We need to wait for the container to be in the DOM with valid dimensions\n const containerRef = useRef<HTMLDivElement>(null)\n const [isReady, setIsReady] = useState(false)\n const [containerSize, setContainerSize] = useState({ width: 0, height: 0 })\n const sizeRef = useRef({ width: 0, height: 0 })\n const readyRef = useRef(false)\n const pendingSizeRef = useRef<{ width: number; height: number } | null>(null)\n const frameRef = useRef<number | null>(null)\n\n // Use useLayoutEffect to measure before paint\n useLayoutEffect(() => {\n let mounted = true\n let resizeObserver: ResizeObserver | null = null\n\n const flushPendingSize = () => {\n frameRef.current = null\n if (!mounted) return\n const pending = pendingSizeRef.current\n pendingSizeRef.current = null\n if (!pending) return\n\n const nextWidth = Math.round(pending.width)\n const nextHeight = Math.round(pending.height)\n if (nextWidth <= 0 || nextHeight <= 0) return\n\n const sizeChanged =\n sizeRef.current.width !== nextWidth || sizeRef.current.height !== nextHeight\n const shouldBecomeReady = !readyRef.current\n\n if (!sizeChanged && !shouldBecomeReady) return\n\n sizeRef.current = { width: nextWidth, height: nextHeight }\n readyRef.current = true\n\n // Resize-driven chart updates can come in bursts (one per portlet).\n // Transitioning these avoids blocking urgent input and coalesces visual work.\n startTransition(() => {\n if (sizeChanged) {\n setContainerSize({ width: nextWidth, height: nextHeight })\n }\n if (shouldBecomeReady) {\n setIsReady(true)\n }\n })\n }\n\n const scheduleSizeUpdate = (width: number, height: number) => {\n pendingSizeRef.current = { width, height }\n if (frameRef.current === null) {\n frameRef.current = requestAnimationFrame(flushPendingSize)\n }\n }\n\n const measureAndSetReady = () => {\n if (!mounted || !containerRef.current) return\n\n const rect = containerRef.current.getBoundingClientRect()\n // Check both clientWidth/Height AND getBoundingClientRect for robustness\n const width = Math.max(containerRef.current.clientWidth, rect.width)\n const measuredHeight = Math.max(containerRef.current.clientHeight, rect.height)\n\n if (width > 0 && measuredHeight > 0) {\n scheduleSizeUpdate(width, measuredHeight)\n }\n }\n\n // Set up ResizeObserver to detect when container gets valid dimensions\n resizeObserver = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width, height: entryHeight } = entry.contentRect\n if (width > 0 && entryHeight > 0) {\n scheduleSizeUpdate(width, entryHeight)\n }\n }\n })\n\n if (containerRef.current) {\n resizeObserver.observe(containerRef.current)\n // Also try immediate measurement\n measureAndSetReady()\n }\n\n return () => {\n mounted = false\n if (frameRef.current !== null) {\n cancelAnimationFrame(frameRef.current)\n frameRef.current = null\n }\n resizeObserver?.disconnect()\n }\n }, [])\n\n try {\n if (height === \"100%\") {\n // For 100% height, make the container fill the available flex space with proper sizing\n // user-select: none prevents browser selection box from appearing when clicking/dragging on charts\n return (\n <div\n ref={containerRef}\n className=\"dc:w-full dc:h-full dc:flex-1 dc:flex dc:flex-col dc:relative\"\n style={{ minHeight: '250px', minWidth: '100px', overflow: 'hidden', userSelect: 'none' }}\n >\n {isReady && containerSize.width > 0 && containerSize.height > 0 ? (\n <ResponsiveContainer\n width={containerSize.width}\n height={containerSize.height - 16}\n debounce={100}\n style={{ marginTop: '16px' }}\n >\n {children}\n </ResponsiveContainer>\n ) : (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\">\n <LoadingIndicator size=\"sm\" />\n </div>\n )}\n </div>\n )\n }\n\n // For specific heights, use them directly\n // user-select: none prevents browser selection box from appearing when clicking/dragging on charts\n const containerStyle = {\n height: typeof height === 'number' ? `${height}px` : height,\n width: '100%',\n minHeight: '200px',\n minWidth: '100px',\n userSelect: 'none' as const\n }\n\n return (\n <div\n ref={containerRef}\n className=\"dc:w-full dc:flex dc:flex-col dc:relative\"\n style={{ ...containerStyle, overflow: 'hidden' }}\n >\n {isReady && containerSize.width > 0 && containerSize.height > 0 ? (\n <ResponsiveContainer\n width={containerSize.width}\n height={containerSize.height - 16}\n debounce={100}\n style={{ marginTop: '16px' }}\n >\n {children}\n </ResponsiveContainer>\n ) : (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\">\n <LoadingIndicator size=\"sm\" />\n </div>\n )}\n </div>\n )\n } catch (error) {\n // ChartContainer ResponsiveContainer error\n\n return (\n <div\n className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full dc:h-full dc:p-4 dc:text-center dc:border dc:border-dashed dc:rounded-lg\"\n style={{ height, borderColor: 'var(--dc-border)', backgroundColor: 'var(--dc-surface)' }}\n >\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1 text-dc-text-muted\">{t('chart.runtime.unableToDisplay')}</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {error instanceof Error ? error.message : 'Failed to create responsive container'}\n </div>\n </div>\n )\n }\n}\n","import React from 'react'\nimport { Tooltip } from 'recharts'\nimport { formatNumericValue } from '../../utils/chartUtils.js'\n\ninterface ChartTooltipProps {\n formatter?: (value: any, name: any, props: any) => [React.ReactText, React.ReactText]\n labelFormatter?: (label: any, payload?: readonly any[]) => React.ReactText\n}\n\n// Default formatter that rounds numeric values to at most 2 decimal places\nconst defaultFormatter = (value: any, name: any): [React.ReactText, React.ReactText] => {\n if (value === null || value === undefined) {\n return ['No data', name]\n }\n return [formatNumericValue(value), name]\n}\n\nexport default function ChartTooltip({ formatter, labelFormatter }: ChartTooltipProps) {\n return (\n <Tooltip\n formatter={formatter || defaultFormatter}\n labelFormatter={labelFormatter}\n contentStyle={{\n backgroundColor: 'white',\n border: '1px solid #e5e7eb',\n borderRadius: '0.5rem',\n fontSize: '0.875rem',\n color: '#1f2937',\n boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',\n padding: '8px 12px'\n }}\n />\n )\n}","import React from 'react'\n\ninterface AngledXAxisTickProps {\n x?: number\n y?: number\n payload?: {\n value: string | number\n }\n tickFormatter?: (value: string | number, index: number) => string\n index?: number\n visibleTicksCount?: number\n // Allow any additional props recharts might pass\n [key: string]: unknown\n}\n\n/**\n * Custom XAxis tick component for properly aligned angled labels.\n * Fixes alignment issues in recharts 3.7+ where angled labels don't\n * center properly under data points.\n */\nconst AngledXAxisTick: React.FC<AngledXAxisTickProps> = ({\n x = 0,\n y = 0,\n payload,\n tickFormatter,\n index = 0\n}) => {\n if (!payload) return null\n\n const displayValue = tickFormatter\n ? tickFormatter(payload.value, index)\n : String(payload.value)\n\n return (\n <g transform={`translate(${x},${y})`}>\n <text\n x={0}\n y={0}\n dy={16}\n textAnchor=\"end\"\n fill=\"currentColor\"\n fontSize={12}\n transform=\"rotate(-45)\"\n >\n {displayValue}\n </text>\n </g>\n )\n}\n\nexport default AngledXAxisTick\n","import React from 'react'\nimport { useTranslation } from '../../hooks/useTranslation.js'\n\n/**\n * Shared guard/state components for chart rendering.\n *\n * Cartesian charts (bar, line, area, pie, …) all duplicated the same\n * centred flex blocks for their empty / config-error / render-error states,\n * differing only in the hint text. These components own the duplicated wrapper\n * JSX; each chart supplies its own already-resolved `hint` (a translation key\n * resolved via `t()`, or a literal string) so per-chart messaging is preserved.\n */\n\ninterface ChartStateProps {\n height?: string | number\n /** Already-resolved hint text (caller resolves its own i18n key) */\n hint?: React.ReactNode\n}\n\n/**\n * Empty / no-data state (muted styling). `titleKey` defaults to the generic\n * \"No data available\" heading but can be overridden (e.g. for the post-transform\n * \"No valid data\" state).\n */\nexport function ChartEmptyState({\n height,\n hint,\n titleKey = 'chart.runtime.noData'\n}: ChartStateProps & { titleKey?: string }) {\n const { t } = useTranslation()\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">{t(titleKey)}</div>\n {hint != null && <div className=\"dc:text-xs text-dc-text-secondary\">{hint}</div>}\n </div>\n </div>\n )\n}\n\n/**\n * Invalid-configuration state (warning styling). Title is always\n * \"Configuration Error\"; the chart supplies a hint describing what is missing.\n */\nexport function ChartConfigError({ height, hint }: ChartStateProps) {\n const { t } = useTranslation()\n return (\n <div className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-warning\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">{t('chart.runtime.configError')}</div>\n {hint != null && <div className=\"dc:text-xs\">{hint}</div>}\n </div>\n </div>\n )\n}\n\n/**\n * Render-error state (error styling), used in chart `catch` blocks.\n */\nexport function ChartRenderError({\n height,\n chartType,\n error\n}: {\n height?: string | number\n /** Display name used in the \"{chartType} Error\" heading */\n chartType: string\n error: unknown\n}) {\n const { t } = useTranslation()\n return (\n <div className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full text-dc-error dc:p-4\" style={{ height }}>\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">{t('chart.runtime.chartError', { chartType })}</div>\n <div className=\"dc:text-xs dc:mb-2\">{error instanceof Error ? error.message : t('chart.runtime.unknownError')}</div>\n <div className=\"dc:text-xs text-dc-text-muted\">{t('chart.runtime.checkConfig')}</div>\n </div>\n </div>\n )\n}\n","import type { ChartAxisConfig } from '../../types.js'\n\n/**\n * Discriminated error code returned by {@link resolveChartAxisFields} when the\n * chart configuration is invalid. Each chart maps these codes to its own i18n\n * hint key (e.g. pie charts surface a pie-specific message for `axisInvalid`).\n */\nexport type ChartAxisErrorCode = 'axisInvalid' | 'axisFields'\n\n/**\n * Result of resolving X/Y/series fields from a {@link ChartAxisConfig}.\n * When `errorCode` is non-null the field values are not usable and the chart\n * should render a configuration-error state instead.\n */\nexport interface ResolvedChartAxisFields {\n /** Resolved X-axis field (category), or undefined when the config is invalid */\n xAxisField?: string\n /** Resolved Y-axis (value/measure) fields */\n yAxisFields: string[]\n /** Optional series/grouping fields (only present in the new config format) */\n seriesFields: string[]\n /** Non-null when the configuration cannot produce a renderable axis setup */\n errorCode: ChartAxisErrorCode | null\n}\n\n/**\n * Pure resolver for the X/Y/series field extraction that Cartesian charts\n * (bar, line, area, pie) all duplicated inline.\n *\n * Supports both the new format (`xAxis` / `yAxis` / `series` arrays) and the\n * legacy format (`x` / `y`). Returns a discriminated `errorCode` rather than\n * JSX so each chart can map it to its own i18n hint key and render its own\n * guard component.\n */\nexport function resolveChartAxisFields(chartConfig?: ChartAxisConfig): ResolvedChartAxisFields {\n let xAxisField: string | undefined\n let yAxisFields: string[] = []\n let seriesFields: string[] = []\n let errorCode: ChartAxisErrorCode | null = null\n\n if (chartConfig?.xAxis && chartConfig?.yAxis) {\n // New format\n xAxisField = Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis\n yAxisFields = Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis : [chartConfig.yAxis]\n seriesFields = chartConfig.series || []\n } else if (chartConfig?.x && chartConfig?.y) {\n // Legacy format\n xAxisField = chartConfig.x\n yAxisFields = Array.isArray(chartConfig.y) ? chartConfig.y : [chartConfig.y]\n } else {\n errorCode = 'axisInvalid'\n }\n\n if (!errorCode && (!xAxisField || yAxisFields.length === 0)) {\n errorCode = 'axisFields'\n }\n\n return { xAxisField, yAxisFields, seriesFields, errorCode }\n}\n","/**\n * Utility functions for handling target values in charts\n */\n\n/**\n * Parse target values from string format\n * @param targetString - String containing target values (e.g., \"100\" or \"50,75,100\")\n * @returns Array of numeric target values\n */\nexport function parseTargetValues(targetString: string): number[] {\n if (!targetString || typeof targetString !== 'string') {\n return []\n }\n\n const trimmed = targetString.trim()\n if (!trimmed) {\n return []\n }\n\n try {\n // Split by comma and parse each value\n const values = trimmed\n .split(',')\n .map(val => val.trim())\n .filter(val => val !== '')\n .map(val => {\n const num = parseFloat(val)\n if (isNaN(num)) {\n throw new Error(`Invalid numeric value: ${val}`)\n }\n return num\n })\n\n return values.length > 0 ? values : []\n } catch (error) {\n console.warn('Failed to parse target values:', error)\n return []\n }\n}\n\n/**\n * Spread target values across data points\n * @param targets - Array of target values\n * @param dataLength - Number of data points to spread across\n * @returns Array of target values for each data point\n */\nexport function spreadTargetValues(targets: number[], dataLength: number): number[] {\n if (targets.length === 0 || dataLength <= 0) {\n return []\n }\n\n // If only one target value, repeat for all data points\n if (targets.length === 1) {\n return new Array(dataLength).fill(targets[0])\n }\n\n // If we have multiple targets, spread them evenly across data points\n const result: number[] = []\n const baseGroupSize = Math.floor(dataLength / targets.length)\n const remainder = dataLength % targets.length\n\n let currentIndex = 0\n \n for (let i = 0; i < targets.length; i++) {\n // Calculate group size for this target\n // First 'remainder' groups get an extra item\n const groupSize = baseGroupSize + (i < remainder ? 1 : 0)\n \n // Fill this group with the current target value\n for (let j = 0; j < groupSize; j++) {\n result[currentIndex++] = targets[i]\n }\n }\n\n return result\n}\n\n/**\n * Calculate variance between actual and target values\n * @param actual - Actual value\n * @param target - Target value\n * @returns Variance as percentage\n */\nexport function calculateVariance(actual: number, target: number): number {\n if (target === 0) {\n return actual === 0 ? 0 : (actual > 0 ? 100 : -100)\n }\n return ((actual - target) / target) * 100\n}\n\n/**\n * Format variance as percentage string with appropriate sign and color indication\n * @param variance - Variance percentage\n * @param decimals - Number of decimal places (default: 1)\n * @returns Formatted variance string (e.g., \"+12.5%\" or \"-8.3%\")\n */\nexport function formatVariance(variance: number, decimals: number = 1): string {\n const sign = variance >= 0 ? '+' : ''\n return `${sign}${variance.toFixed(decimals)}%`\n}\n\n/**\n * Get unique target values for reference lines\n * @param targets - Array of target values (may contain duplicates)\n * @returns Array of unique target values\n */\nexport function getUniqueTargets(targets: number[]): number[] {\n return [...new Set(targets)].sort((a, b) => a - b)\n}","import { Fragment } from 'react'\nimport { YAxis, Line, Legend } from 'recharts'\nimport { CHART_MARGINS } from '../../utils/chartConstants.js'\nimport { formatAxisValue } from '../../utils/chartUtils.js'\nimport { parseTargetValues, spreadTargetValues } from '../../utils/targetUtils.js'\nimport type { AxisFormatConfig } from '../../types.js'\n\n/**\n * Shared scaffolding for the Cartesian recharts charts (bar, line, area).\n *\n * These charts duplicated the same dual-Y-axis derivation, chart margins,\n * target-line overlay, and the left/right `<YAxis>` JSX. The pure helpers and\n * recharts render-helpers here own that shared setup. Render-helpers return\n * recharts elements (inside a fragment) and are embedded directly in the chart\n * children — recharts flattens fragments when discovering axes/series, exactly\n * as it already does for the inline target-line fragment.\n */\n\nexport interface DualAxisInfo {\n /** True when at least one measure is assigned to the right axis */\n hasRightAxis: boolean\n /** Measure fields rendered against the left axis */\n leftAxisFields: string[]\n /** Measure fields rendered against the right axis */\n rightAxisFields: string[]\n}\n\n/** Derive left/right axis membership from per-measure axis assignments. */\nexport function getDualAxisInfo(\n yAxisFields: string[],\n yAxisAssignment: Record<string, 'left' | 'right'>\n): DualAxisInfo {\n const hasRightAxis = yAxisFields.some((field) => yAxisAssignment[field] === 'right')\n const leftAxisFields = yAxisFields.filter((f) => (yAxisAssignment[f] || 'left') === 'left')\n const rightAxisFields = yAxisFields.filter((f) => yAxisAssignment[f] === 'right')\n return { hasRightAxis, leftAxisFields, rightAxisFields }\n}\n\n/** Chart margins with extra room for the Y-axis label(s). */\nexport function getYAxisChartMargins(hasRightAxis: boolean) {\n return {\n ...CHART_MARGINS,\n left: 40, // Space for left Y-axis label\n right: hasRightAxis ? 40 : 20 // Extra space for right Y-axis label if needed\n }\n}\n\n/**\n * Apply target values (single or comma-separated spread) onto chart data,\n * returning the spread targets plus data enhanced with a `__target` key.\n */\nexport function withTargetData<T extends Record<string, any>>(\n chartData: T[],\n target: string | undefined\n): { spreadTargets: number[]; enhancedChartData: T[] } {\n const targetValues = parseTargetValues(target || '')\n const spreadTargets = spreadTargetValues(targetValues, chartData.length)\n const enhancedChartData = spreadTargets.length > 0\n ? chartData.map((dataPoint, index) => ({ ...dataPoint, __target: spreadTargets[index] || null }))\n : chartData\n return { spreadTargets, enhancedChartData }\n}\n\n/**\n * Render the left + optional right `<YAxis>`. Returns recharts elements in a\n * fragment for embedding directly in the chart children. Positional args keep\n * the (otherwise identical) call sites in each chart down to a single line.\n *\n * @param isPercentStack when true (area/bar percent stacking) the left axis shows 0–100%\n */\nexport function renderDualYAxes(\n { hasRightAxis, leftAxisFields, rightAxisFields }: DualAxisInfo,\n getFieldLabel: (field: string) => string,\n leftYAxisFormat?: AxisFormatConfig,\n rightYAxisFormat?: AxisFormatConfig,\n isPercentStack = false\n) {\n return (\n <Fragment>\n <YAxis\n yAxisId=\"left\"\n orientation=\"left\"\n tick={{ fontSize: 12 }}\n tickFormatter={\n isPercentStack\n ? (v) => `${(v * 100).toFixed(0)}%`\n : leftYAxisFormat\n ? (value) => formatAxisValue(value, leftYAxisFormat)\n : undefined\n }\n domain={isPercentStack ? [0, 1] : undefined}\n label={\n isPercentStack\n ? undefined\n : leftAxisFields.length > 0\n ? {\n value: leftYAxisFormat?.label || getFieldLabel(leftAxisFields[0]),\n angle: -90,\n position: 'left',\n style: { textAnchor: 'middle', fontSize: '12px' }\n }\n : undefined\n }\n />\n {hasRightAxis && (\n <YAxis\n yAxisId=\"right\"\n orientation=\"right\"\n tick={{ fontSize: 12 }}\n tickFormatter={rightYAxisFormat ? (value) => formatAxisValue(value, rightYAxisFormat) : undefined}\n label={\n rightAxisFields.length > 0\n ? {\n value: rightYAxisFormat?.label || getFieldLabel(rightAxisFields[0]),\n angle: 90,\n position: 'right',\n style: { textAnchor: 'middle', fontSize: '12px' }\n }\n : undefined\n }\n />\n )}\n </Fragment>\n )\n}\n\n/**\n * Render the target overlay: a white backing line plus a purple dashed line\n * on top (shared by bar, line, area). Returns null when there are no targets.\n */\nexport function renderChartTargetLines(spreadTargets: number[]) {\n if (spreadTargets.length === 0) return null\n return (\n <Fragment>\n {/* White background line */}\n <Line\n type=\"monotone\"\n dataKey=\"__target\"\n yAxisId=\"left\"\n stroke=\"#ffffff\"\n strokeWidth={2}\n dot={false}\n activeDot={false}\n connectNulls={false}\n />\n {/* Grey dashed line on top */}\n <Line\n type=\"monotone\"\n dataKey=\"__target\"\n yAxisId=\"left\"\n name=\"Target\"\n stroke=\"#8B5CF6\"\n strokeWidth={2}\n strokeDasharray=\"2 3\"\n dot={false}\n activeDot={false}\n connectNulls={false}\n />\n </Fragment>\n )\n}\n\ninterface CartesianTooltipOptions {\n leftYAxisFormat?: AxisFormatConfig\n rightYAxisFormat?: AxisFormatConfig\n yAxisAssignment: Record<string, 'left' | 'right'>\n /** Maps a series key (display label) back to its original measure field. */\n resolveField: (name: string) => string | undefined\n /** When true, values render as percentages (area/bar percent stacking). */\n isPercentStack?: boolean\n}\n\n/**\n * Build the value/name tooltip formatter shared by the Cartesian charts.\n * Handles null values, the target series, percent stacking, and per-series\n * left/right axis format selection.\n */\nexport function makeCartesianTooltipFormatter({\n leftYAxisFormat,\n rightYAxisFormat,\n yAxisAssignment,\n resolveField,\n isPercentStack = false\n}: CartesianTooltipOptions) {\n return (value: any, name: any): [any, any] => {\n if (value === null || value === undefined) {\n return ['No data', name]\n }\n if (name === 'Target') {\n // Use left Y-axis format for target values\n return [formatAxisValue(value, leftYAxisFormat), 'Target Value']\n }\n if (isPercentStack && typeof value === 'number') {\n return [`${(value * 100).toFixed(1)}%`, name]\n }\n // Determine which axis format to use based on series name\n const originalField = resolveField(name)\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n const formatConfig = axisId === 'right' ? rightYAxisFormat : leftYAxisFormat\n return [formatAxisValue(value, formatConfig), name]\n }\n}\n\n/**\n * Render the hover-aware bottom legend shared by the Cartesian charts.\n * Returns null when the legend is hidden. `iconType` / `paddingTop` differ\n * per chart (rect+10 for area, rect+25 for bar, line+25 for line).\n */\nexport function renderHoverLegend({\n show,\n iconType,\n paddingTop,\n onHover,\n onLeave\n}: {\n show: boolean\n iconType: 'rect' | 'line' | 'circle'\n paddingTop: number\n onHover: (dataKey: string) => void\n onLeave: () => void\n}) {\n if (!show) return null\n return (\n <Legend\n wrapperStyle={{ fontSize: '12px', paddingTop: `${paddingTop}px` }}\n iconType={iconType}\n iconSize={8}\n layout=\"horizontal\"\n align=\"center\"\n verticalAlign=\"bottom\"\n onMouseEnter={(o) => onHover(String(o.dataKey || ''))}\n onMouseLeave={onLeave}\n />\n )\n}\n","import React from 'react'\nimport { Line, Area } from 'recharts'\nimport { formatTimeValue, getFieldGranularity, transformChartDataWithSeries } from '../../utils/chartUtils.js'\nimport { CHART_COLORS } from '../../utils/chartConstants.js'\nimport {\n isComparisonData,\n getPeriodLabels,\n transformForOverlayMode,\n isPriorPeriodSeries,\n getPriorPeriodStrokeDashArray\n} from '../../utils/comparisonUtils.js'\nimport type { ColorPalette } from '../../types.js'\n\n/**\n * Co-located helpers shared by the Cartesian time-series charts (Line, Area).\n *\n * These extract the heavier inline logic that previously lived inside the chart\n * components — the clickable/drill dot renderer, the comparison-mode tick and\n * label formatters, and the series-key → measure-field resolution — so the\n * component bodies stay focused on chart composition. Pure extraction: no\n * behaviour change.\n */\n\nexport interface DataPointClickPayload {\n dataPoint: any\n clickedField: string\n xValue: any\n position: { x: number; y: number }\n nativeEvent: React.MouseEvent\n}\n\ninterface DrillDotOptions {\n /** Recharts dot props (cx, cy, payload, key). */\n props: any\n color: string\n drillEnabled?: boolean\n originalField?: string\n seriesKey: string\n onDataPointClick?: (payload: DataPointClickPayload) => void\n}\n\n/**\n * Render a single data-point dot. When drill is enabled and a click handler is\n * present, renders a larger clickable dot with a surface-coloured backing\n * circle to mask grid lines; otherwise a small plain dot. Returns null when the\n * point has no coordinates.\n *\n * `renderPlain` controls whether a small non-interactive dot is drawn when\n * drill is disabled (Line draws one; Area returns nothing).\n */\nexport function renderDrillDot({\n props,\n color,\n drillEnabled,\n originalField,\n seriesKey,\n onDataPointClick,\n renderPlain\n}: DrillDotOptions & { renderPlain: boolean }): React.ReactElement | null {\n const { cx, cy, payload, key } = props\n if (cx === undefined || cy === undefined) return null\n\n const handleClick = (event: React.MouseEvent) => {\n event.stopPropagation()\n event.preventDefault()\n if (onDataPointClick) {\n onDataPointClick({\n dataPoint: payload,\n clickedField: originalField || seriesKey,\n xValue: payload.name,\n position: { x: event.clientX, y: event.clientY },\n nativeEvent: event\n })\n }\n }\n\n if (drillEnabled && onDataPointClick) {\n return (\n <g key={key}>\n {/* Background to mask grid lines - uses theme surface color */}\n <circle cx={cx} cy={cy} r={6} fill=\"var(--dc-surface)\" style={{ pointerEvents: 'none' }} />\n {/* Visible dot with click handler */}\n <circle\n cx={cx}\n cy={cy}\n r={4}\n fill=\"var(--dc-surface)\"\n stroke={color}\n strokeWidth={2}\n cursor=\"pointer\"\n onClick={(e: React.MouseEvent<SVGCircleElement>) => {\n handleClick(e as unknown as React.MouseEvent)\n }}\n />\n </g>\n )\n }\n\n if (!renderPlain) return null\n\n return <circle key={key} cx={cx} cy={cy} r={3} fill={color} />\n}\n\n/**\n * Format a comparison-data X-axis value: shows the current period's date,\n * formatted to the query granularity, falling back to \"Period N\".\n */\nfunction formatComparisonDate(\n displayDate: unknown,\n queryObject: any,\n xAxisField: string,\n fallbackIndex: number\n): string {\n if (displayDate) {\n const granularity = getFieldGranularity(queryObject, xAxisField)\n return formatTimeValue(displayDate as any, granularity)\n }\n return `Period ${fallbackIndex + 1}`\n}\n\n/**\n * Build the comparison-mode X-axis tick formatter for time-series charts.\n * Returns undefined when not in comparison mode (no custom formatting).\n */\nexport function makeComparisonTickFormatter(\n hasComparisonData: boolean,\n chartData: any[],\n queryObject: any,\n xAxisField: string\n): ((value: string | number, index: number) => string) | undefined {\n if (!hasComparisonData) return undefined\n return (value: string | number, index: number) => {\n const row = chartData[index]\n return formatComparisonDate(row?.__displayDate, queryObject, xAxisField, Number(value))\n }\n}\n\n/**\n * Build the comparison-mode tooltip label formatter for time-series charts.\n * Returns undefined when not in comparison mode (no custom formatting).\n */\nexport function makeComparisonLabelFormatter(\n hasComparisonData: boolean,\n queryObject: any,\n xAxisField: string\n): ((label: any, payload: any) => string) | undefined {\n if (!hasComparisonData) return undefined\n return (label: any, payload: any) => {\n const row = payload && payload.length > 0 ? payload[0]?.payload : undefined\n return formatComparisonDate(row?.__displayDate, queryObject, xAxisField, Number(label))\n }\n}\n\n/**\n * Resolve effective stacking for the Area chart from `stackType` (new) /\n * `stacked` (legacy), disabling stacking when a right axis is present (areas on\n * different axes can't be stacked).\n */\nexport function resolveAreaStacking(\n displayConfig: { stackType?: string; stacked?: boolean } | undefined,\n hasRightAxis: boolean\n): { effectiveShouldStack: boolean; effectiveIsPercentStack: boolean; stackOffset: 'expand' | undefined } {\n const stackType = displayConfig?.stackType ?? (displayConfig?.stacked ? 'normal' : 'none')\n const effectiveShouldStack = stackType !== 'none' && !hasRightAxis\n const effectiveIsPercentStack = stackType === 'percent' && !hasRightAxis\n return {\n effectiveShouldStack,\n effectiveIsPercentStack,\n stackOffset: effectiveIsPercentStack ? 'expand' : undefined\n }\n}\n\n/** Build a map from series display label to original measure field name. */\nexport function buildSeriesKeyToFieldMap(\n yAxisFields: string[],\n getFieldLabel: (field: string) => string\n): Record<string, string> {\n const mapping: Record<string, string> = {}\n yAxisFields.forEach((field) => {\n mapping[getFieldLabel(field)] = field\n })\n return mapping\n}\n\n/**\n * Resolve a series key back to its original measure field, handling comparison\n * suffixes like \"(Current)\" / \"(Prior)\" and dimension prefixes (\"Dim - Label\").\n */\nexport function makeSeriesKeyResolver(\n seriesKeyToField: Record<string, string>\n): (seriesKey: string) => string | undefined {\n return (seriesKey: string): string | undefined => {\n if (seriesKeyToField[seriesKey]) return seriesKeyToField[seriesKey]\n // Guard against excessive input length to prevent ReDoS\n if (seriesKey.length > 1000) return undefined\n const withoutSuffix = seriesKey.replace(/\\s*\\((Current|Prior)\\)$/, '')\n const parts = withoutSuffix.split(' - ')\n const measureLabel = parts[parts.length - 1]\n return seriesKeyToField[measureLabel]\n }\n}\n\n/** Pick a series colour by index, preferring the palette then the defaults. */\nexport function getSeriesColor(colorPalette: ColorPalette | undefined, index: number): string {\n const colors = colorPalette?.colors\n return (colors && colors[index % colors.length]) || CHART_COLORS[index % CHART_COLORS.length]\n}\n\ninterface LineSeriesOptions {\n seriesKeys: string[]\n colorPalette?: ColorPalette\n resolveField: (seriesKey: string) => string | undefined\n yAxisAssignment: Record<string, 'left' | 'right'>\n hoveredLegend: string | null\n connectNulls: boolean\n drillEnabled?: boolean\n onDataPointClick?: (payload: DataPointClickPayload) => void\n /** Comparison styling (omit/false → standard series). */\n hasComparisonData?: boolean\n periodLabels?: string[]\n priorPeriodStyle?: 'solid' | 'dashed' | 'dotted'\n priorPeriodOpacity?: number\n}\n\n/** Render the `<Line>` element for one resolved series (Line chart). */\nfunction renderOneLineSeries(seriesKey: string, index: number, opts: LineSeriesOptions): React.ReactElement {\n const {\n colorPalette, resolveField, yAxisAssignment, hoveredLegend, connectNulls,\n drillEnabled, onDataPointClick, hasComparisonData, periodLabels = [],\n priorPeriodStyle = 'dashed', priorPeriodOpacity = 0.5\n } = opts\n\n const originalField = resolveField(seriesKey)\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n\n const isPriorPeriod = Boolean(hasComparisonData) && isPriorPeriodSeries(seriesKey, periodLabels)\n const strokeDashArray = isPriorPeriod ? getPriorPeriodStrokeDashArray(priorPeriodStyle) : undefined\n const opacity = isPriorPeriod ? priorPeriodOpacity : 1\n const lineColor = getSeriesColor(colorPalette, index)\n\n let strokeOpacity = opacity\n if (hoveredLegend) strokeOpacity = hoveredLegend === seriesKey ? 1 : 0.3\n\n return (\n <Line\n key={seriesKey}\n type=\"monotone\"\n dataKey={seriesKey}\n yAxisId={axisId}\n stroke={lineColor}\n strokeWidth={isPriorPeriod ? 1.5 : 2}\n strokeDasharray={strokeDashArray}\n dot={isPriorPeriod ? false : (props: any) => renderDrillDot({\n props, color: lineColor, drillEnabled, originalField, seriesKey, onDataPointClick, renderPlain: true\n })}\n activeDot={false}\n strokeOpacity={strokeOpacity}\n connectNulls={connectNulls}\n />\n )\n}\n\n/** Render all line series for the Line chart. */\nexport function renderLineSeries(opts: LineSeriesOptions): React.ReactElement[] {\n return opts.seriesKeys.map((seriesKey, index) => renderOneLineSeries(seriesKey, index, opts))\n}\n\ninterface AreaSeriesOptions {\n seriesKeys: string[]\n colorPalette?: ColorPalette\n seriesKeyToField: Record<string, string>\n yAxisAssignment: Record<string, 'left' | 'right'>\n hoveredLegend: string | null\n connectNulls: boolean\n shouldStack: boolean\n drillEnabled?: boolean\n onDataPointClick?: (payload: DataPointClickPayload) => void\n}\n\n/** Render the `<Area>` element for one series (Area chart). */\nfunction renderOneAreaSeries(seriesKey: string, index: number, opts: AreaSeriesOptions): React.ReactElement {\n const {\n colorPalette, seriesKeyToField, yAxisAssignment, hoveredLegend, connectNulls,\n shouldStack, drillEnabled, onDataPointClick\n } = opts\n\n const originalField = seriesKeyToField[seriesKey]\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n const areaColor = getSeriesColor(colorPalette, index)\n\n let fillOpacity = 0.3\n let strokeOpacity = 1\n if (hoveredLegend) {\n const active = hoveredLegend === seriesKey\n fillOpacity = active ? 0.6 : 0.1\n strokeOpacity = active ? 1 : 0.3\n }\n\n return (\n <Area\n key={seriesKey}\n type=\"monotone\"\n dataKey={seriesKey}\n yAxisId={axisId}\n stackId={shouldStack ? 'stack' : undefined}\n stroke={areaColor}\n fill={areaColor}\n fillOpacity={fillOpacity}\n strokeWidth={2}\n strokeOpacity={strokeOpacity}\n connectNulls={connectNulls}\n dot={(props: any) => renderDrillDot({\n props, color: areaColor, drillEnabled, originalField, seriesKey, onDataPointClick, renderPlain: false\n })}\n activeDot={false}\n />\n )\n}\n\n/** Render all area series for the Area chart. */\nexport function renderAreaSeries(opts: AreaSeriesOptions): React.ReactElement[] {\n return opts.seriesKeys.map((seriesKey, index) => renderOneAreaSeries(seriesKey, index, opts))\n}\n\nexport interface TimeSeriesShape {\n chartData: any[]\n seriesKeys: string[]\n effectiveXAxisKey: string\n hasComparisonData: boolean\n periodLabels: string[]\n}\n\n/**\n * Shape raw rows for a Cartesian time-series chart. In comparison mode this\n * uses the overlay transform (aligned by period day index); otherwise the\n * standard series transform. Centralises the branch so the chart body stays\n * flat.\n */\nexport function buildTimeSeriesData(args: {\n data: any[]\n xAxisField: string\n yAxisFields: string[]\n seriesFields: string[]\n queryObject: any\n getFieldLabel: (field: string) => string\n}): TimeSeriesShape {\n const { data, xAxisField, yAxisFields, seriesFields, queryObject, getFieldLabel } = args\n const hasComparisonData = isComparisonData(data)\n\n if (hasComparisonData) {\n const overlay = transformForOverlayMode(data, yAxisFields, xAxisField, getFieldLabel)\n return {\n chartData: overlay.data,\n seriesKeys: overlay.seriesKeys,\n effectiveXAxisKey: '__periodDayIndex',\n hasComparisonData,\n periodLabels: getPeriodLabels(data)\n }\n }\n\n const standard = transformChartDataWithSeries(data, xAxisField, yAxisFields, queryObject, seriesFields, getFieldLabel)\n return {\n chartData: standard.data,\n seriesKeys: standard.seriesKeys,\n effectiveXAxisKey: 'name',\n hasComparisonData,\n periodLabels: []\n }\n}\n","import React, { useState, useMemo } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport { ComposedChart, XAxis, CartesianGrid } from 'recharts'\nimport ChartContainer from './ChartContainer.js'\nimport ChartTooltip from './ChartTooltip.js'\nimport AngledXAxisTick from './AngledXAxisTick.js'\nimport { ChartEmptyState, ChartConfigError, ChartRenderError } from './ChartStates.js'\nimport { resolveChartAxisFields } from './chartAxisResolution.js'\nimport {\n getDualAxisInfo,\n getYAxisChartMargins,\n withTargetData,\n renderDualYAxes,\n renderChartTargetLines,\n makeCartesianTooltipFormatter,\n renderHoverLegend\n} from './chartScaffolding.js'\nimport { buildSeriesKeyToFieldMap, renderAreaSeries, resolveAreaStacking } from './cartesianChartHelpers.js'\nimport { transformChartDataWithSeries } from '../../utils/chartUtils.js'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel.js'\nimport type { ChartProps } from '../../types.js'\n\nconst AreaChart = React.memo(function AreaChart({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n onDataPointClick,\n drillEnabled\n}: ChartProps) {\n const { t } = useTranslation()\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel()\n\n // Resolve + validate axis fields (hooks-first to satisfy React rules)\n const { xAxisField, yAxisFields, seriesFields, errorCode } = useMemo(\n () => resolveChartAxisFields(chartConfig),\n [chartConfig]\n )\n\n // Dual Y-axis support: extract yAxisAssignment from chartConfig\n const yAxisAssignment = useMemo(() =>\n chartConfig?.yAxisAssignment || {},\n [chartConfig?.yAxisAssignment]\n )\n\n // Use shared function to transform data and handle series\n // (empty arrays when config is invalid — early returns happen after hooks)\n const { data: chartData, seriesKeys } = useMemo(() => {\n if (errorCode || !data || data.length === 0 || !xAxisField) {\n return { data: [], seriesKeys: [] }\n }\n return transformChartDataWithSeries(\n data,\n xAxisField,\n yAxisFields,\n queryObject,\n seriesFields,\n getFieldLabel\n )\n }, [data, xAxisField, yAxisFields, queryObject, seriesFields, getFieldLabel, errorCode])\n\n try {\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showGrid: displayConfig?.showGrid ?? true,\n showTooltip: displayConfig?.showTooltip ?? true,\n connectNulls: displayConfig?.connectNulls ?? false\n }\n\n const showAllXLabels = displayConfig?.showAllXLabels ?? true\n\n // Extract axis format configs\n const leftYAxisFormat = displayConfig?.leftYAxisFormat\n const rightYAxisFormat = displayConfig?.rightYAxisFormat\n\n if (!data || data.length === 0) {\n return <ChartEmptyState height={height} hint={t('chart.runtime.noDataHint.area')} />\n }\n\n if (errorCode) {\n return <ChartConfigError height={height} hint={t(`chart.runtime.configErrorHint.${errorCode}`)} />\n }\n\n // Build mapping from series key (label) to original field name\n const seriesKeyToField = buildSeriesKeyToFieldMap(yAxisFields, getFieldLabel)\n\n // Dual Y-axis derivation + margins (shared scaffolding)\n const axisInfo = getDualAxisInfo(yAxisFields, yAxisAssignment)\n const { hasRightAxis } = axisInfo\n\n // Resolve effective stacking (disabled when dual Y-axis present)\n const { effectiveShouldStack, effectiveIsPercentStack, stackOffset } =\n resolveAreaStacking(displayConfig, hasRightAxis)\n\n // Determine if legend will be shown\n const showLegend = safeDisplayConfig.showLegend\n\n // Use custom chart margins with extra space for Y-axis labels\n const chartMargins = getYAxisChartMargins(hasRightAxis)\n\n // Process target values and add to chart data\n const { spreadTargets, enhancedChartData } = withTargetData(chartData, displayConfig?.target)\n\n // Validate transformed data\n if (!chartData || chartData.length === 0) {\n return (\n <ChartEmptyState\n height={height}\n titleKey=\"chart.runtime.noValidData\"\n hint=\"No valid data points for area chart after transformation\"\n />\n )\n }\n\n return (\n <ChartContainer height={height}>\n <ComposedChart data={enhancedChartData} margin={chartMargins} stackOffset={stackOffset} accessibilityLayer={false}>\n {safeDisplayConfig.showGrid && <CartesianGrid strokeDasharray=\"3 3\" style={{ pointerEvents: 'none' }} />}\n <XAxis dataKey=\"name\" type=\"category\" tick={<AngledXAxisTick />} height={60} interval={showAllXLabels ? 0 : undefined} />\n {renderDualYAxes(axisInfo, getFieldLabel, leftYAxisFormat, rightYAxisFormat, effectiveIsPercentStack)}\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={makeCartesianTooltipFormatter({\n leftYAxisFormat,\n rightYAxisFormat,\n yAxisAssignment,\n resolveField: (name) => seriesKeyToField[name],\n isPercentStack: effectiveIsPercentStack\n })}\n />\n )}\n {renderHoverLegend({\n show: showLegend,\n iconType: 'rect',\n paddingTop: 10,\n onHover: setHoveredLegend,\n onLeave: () => setHoveredLegend(null)\n })}\n {renderAreaSeries({\n seriesKeys,\n colorPalette,\n seriesKeyToField,\n yAxisAssignment,\n hoveredLegend,\n connectNulls: safeDisplayConfig.connectNulls,\n shouldStack: effectiveShouldStack,\n drillEnabled,\n onDataPointClick\n })}\n {renderChartTargetLines(spreadTargets)}\n </ComposedChart>\n </ChartContainer>\n )\n } catch (error) {\n return <ChartRenderError height={height} chartType=\"Area Chart\" error={error} />\n }\n})\n\nexport default AreaChart\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chart-bar-BqelQE_I.js","names":[],"sources":["../../../src/client/components/charts/BarChart.helpers.ts","../../../src/client/components/charts/BarSeries.tsx","../../../src/client/components/charts/BarChart.tsx"],"sourcesContent":["/**\n * Co-located helpers for BarChart.\n *\n * Pure decisions about stacking and bar colouring extracted from the component\n * render body. No behaviour change — these mirror the original inline logic.\n */\nimport { isValidNumericValue } from '../../utils/chartUtils'\nimport type { ChartDisplayConfig } from '../../types'\n\nexport interface StackMode {\n shouldStack: boolean\n isPercentStack: boolean\n}\n\n/** Derive stacking flags from `stackType` (new) or `stacked` (legacy). */\nexport function resolveStackMode(displayConfig: ChartDisplayConfig | undefined): StackMode {\n const stackType = displayConfig?.stackType ?? (displayConfig?.stacked ? 'normal' : 'none')\n return {\n shouldStack: stackType !== 'none',\n isPercentStack: stackType === 'percent'\n }\n}\n\n/**\n * Filter out rows where every series value is null, returning the kept rows and\n * the number skipped (used to render the \"N hidden\" footer note).\n */\nexport function filterEmptyRows(\n transformedData: Record<string, any>[],\n seriesKeys: string[]\n): { chartData: Record<string, any>[]; skippedCount: number } {\n if (transformedData.length === 0 || seriesKeys.length === 0) {\n return { chartData: [], skippedCount: 0 }\n }\n const filtered = transformedData.filter(row =>\n seriesKeys.some(key => isValidNumericValue(row[key]))\n )\n return { chartData: filtered, skippedCount: transformedData.length - filtered.length }\n}\n\nexport interface BarColoringMode {\n /** Single series with mixed positive/negative values → green/red bars. */\n usePositiveNegativeColoring: boolean\n /** Single measure, no series dimension, multiple categories → colour per category. */\n useColorByCategory: boolean\n}\n\n/** Decide which special per-bar colouring mode (if any) applies. */\nexport function resolveBarColoringMode(\n seriesKeys: string[],\n chartData: Record<string, any>[],\n seriesFieldsLength: number\n): BarColoringMode {\n const usePositiveNegativeColoring =\n seriesKeys.length === 1 &&\n chartData.some(row => {\n const value = row[seriesKeys[0]]\n return typeof value === 'number' && value < 0\n })\n\n const useColorByCategory =\n seriesKeys.length === 1 &&\n !usePositiveNegativeColoring &&\n !seriesFieldsLength &&\n chartData.length > 1\n\n return { usePositiveNegativeColoring, useColorByCategory }\n}\n","import React from 'react'\nimport { Bar, Cell } from 'recharts'\nimport { CHART_COLORS, POSITIVE_COLOR, NEGATIVE_COLOR } from '../../utils/chartConstants'\nimport type { ColorPalette } from '../../types'\nimport type { ChartDataPointClickEvent } from '../../types/drill'\n\ninterface BarSeriesProps {\n seriesKey: string\n index: number\n /** Original field name backing this series key (for axis assignment + drill). */\n originalField: string | undefined\n axisId: 'left' | 'right'\n stackId: string | undefined\n chartData: Record<string, any>[]\n enhancedChartData: Record<string, any>[]\n colorPalette?: ColorPalette\n hoveredLegend: string | null\n usePositiveNegativeColoring: boolean\n useColorByCategory: boolean\n drillEnabled?: boolean\n onDataPointClick?: (event: ChartDataPointClickEvent) => void\n}\n\n/** Opacity for a bar/cell given the currently hovered legend key. */\nfunction hoverOpacity(hoveredLegend: string | null, seriesKey: string): number {\n if (!hoveredLegend) return 1\n return hoveredLegend === seriesKey ? 1 : 0.3\n}\n\n/**\n * Single `<Bar>` for a series key.\n *\n * Encapsulates the per-bar colouring modes (positive/negative, colour-by-category)\n * and the drill click handler that were previously inlined in BarChart's render.\n * Returned as a fragment-bearing component so it can be used directly inside the\n * Recharts `<ComposedChart>` children list. Behaviour is identical to the original.\n */\nexport function BarSeries({\n seriesKey,\n index,\n originalField,\n axisId,\n stackId,\n chartData,\n enhancedChartData,\n colorPalette,\n hoveredLegend,\n usePositiveNegativeColoring,\n useColorByCategory,\n drillEnabled,\n onDataPointClick\n}: BarSeriesProps) {\n const baseFill = usePositiveNegativeColoring\n ? POSITIVE_COLOR\n : (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) ||\n CHART_COLORS[index % CHART_COLORS.length]\n\n const handleClick = (barData: any, dataIndex: number, event: React.MouseEvent) => {\n if (onDataPointClick && drillEnabled && barData) {\n onDataPointClick({\n dataPoint: enhancedChartData[dataIndex] || barData,\n clickedField: originalField || seriesKey,\n xValue: barData.name,\n position: { x: event.clientX, y: event.clientY },\n nativeEvent: event\n })\n }\n }\n\n return (\n <Bar\n dataKey={seriesKey}\n yAxisId={axisId}\n stackId={stackId}\n fill={baseFill}\n fillOpacity={hoverOpacity(hoveredLegend, seriesKey)}\n cursor={drillEnabled ? 'pointer' : undefined}\n onClick={handleClick}\n >\n {usePositiveNegativeColoring &&\n chartData.map((entry, entryIndex) => {\n const value = entry[seriesKey]\n const fillColor = typeof value === 'number' && value < 0 ? NEGATIVE_COLOR : POSITIVE_COLOR\n return (\n <Cell\n key={`cell-${entryIndex}`}\n fill={fillColor}\n fillOpacity={hoverOpacity(hoveredLegend, seriesKey)}\n />\n )\n })}\n {useColorByCategory &&\n chartData.map((_entry, entryIndex) => {\n const colors = colorPalette?.colors || CHART_COLORS\n return (\n <Cell\n key={`cat-${entryIndex}`}\n fill={colors[entryIndex % colors.length]}\n fillOpacity={hoverOpacity(hoveredLegend, seriesKey)}\n />\n )\n })}\n </Bar>\n )\n}\n","import React, { useState, useMemo } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation'\nimport { ComposedChart, XAxis, CartesianGrid } from 'recharts'\nimport ChartContainer from './ChartContainer'\nimport ChartTooltip from './ChartTooltip'\nimport AngledXAxisTick from './AngledXAxisTick'\nimport { ChartEmptyState, ChartConfigError, ChartRenderError } from './ChartStates'\nimport { resolveChartAxisFields } from './chartAxisResolution'\nimport {\n getDualAxisInfo,\n getYAxisChartMargins,\n withTargetData,\n renderDualYAxes,\n renderChartTargetLines,\n makeCartesianTooltipFormatter,\n renderHoverLegend\n} from './chartScaffolding'\nimport { resolveStackMode, filterEmptyRows, resolveBarColoringMode } from './BarChart.helpers'\nimport { BarSeries } from './BarSeries'\nimport { transformChartDataWithSeries } from '../../utils/chartUtils'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel'\nimport type { ChartProps } from '../../types'\n\nconst BarChart = React.memo(function BarChart({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n onDataPointClick,\n drillEnabled\n}: ChartProps) {\n const { t } = useTranslation()\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel()\n\n // Determine stacking from stackType (new) or stacked (legacy)\n const { shouldStack, isPercentStack } = resolveStackMode(displayConfig)\n\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showGrid: displayConfig?.showGrid ?? true,\n showTooltip: displayConfig?.showTooltip ?? true\n }\n\n const showAllXLabels = displayConfig?.showAllXLabels ?? true\n\n // Extract axis format configs\n const leftYAxisFormat = displayConfig?.leftYAxisFormat\n const rightYAxisFormat = displayConfig?.rightYAxisFormat\n\n // Resolve + validate axis fields (hooks-first; early returns happen after all hooks)\n const { xAxisField, yAxisFields, seriesFields, errorCode } = useMemo(\n () => resolveChartAxisFields(chartConfig),\n [chartConfig]\n )\n\n // Transform data (will be empty arrays if config is invalid)\n const { data: transformedData, seriesKeys } = useMemo(() => {\n if (errorCode || !data || data.length === 0 || !xAxisField) {\n return { data: [], seriesKeys: [] }\n }\n return transformChartDataWithSeries(\n data,\n xAxisField,\n yAxisFields,\n queryObject,\n seriesFields,\n getFieldLabel\n )\n }, [data, xAxisField, yAxisFields, queryObject, seriesFields, getFieldLabel, errorCode])\n\n // Dual Y-axis support: extract yAxisAssignment from chartConfig (memoized to prevent object recreation)\n const yAxisAssignment = useMemo(() =>\n chartConfig?.yAxisAssignment || {},\n [chartConfig?.yAxisAssignment]\n )\n\n // Build mapping from series key (label) to original field name\n const seriesKeyToField: Record<string, string> = useMemo(() => {\n const mapping: Record<string, string> = {}\n yAxisFields.forEach((field) => {\n const label = getFieldLabel(field)\n mapping[label] = field\n })\n return mapping\n }, [yAxisFields, getFieldLabel])\n\n // Dual Y-axis derivation (shared scaffolding)\n const axisInfo = getDualAxisInfo(yAxisFields, yAxisAssignment)\n const { hasRightAxis } = axisInfo\n\n // Null handling: Filter out data points where ALL measure values are null\n // This prevents rendering empty bars and makes the chart clearer\n const { chartData, skippedCount } = useMemo(\n () => filterEmptyRows(transformedData, seriesKeys),\n [transformedData, seriesKeys]\n )\n\n // Now handle early returns AFTER all hooks\n try {\n if (!data || data.length === 0) {\n return <ChartEmptyState height={height} hint={t('chart.runtime.noDataHint.bar')} />\n }\n\n if (errorCode) {\n return <ChartConfigError height={height} hint={t(`chart.runtime.configErrorHint.${errorCode}`)} />\n }\n\n // Determine stack offset for percentage stacking\n // Disable stacking when dual Y-axis is used (bars on different axes can't be stacked)\n const effectiveShouldStack = shouldStack && !hasRightAxis\n const effectiveIsPercentStack = isPercentStack && !hasRightAxis\n const stackOffset = effectiveIsPercentStack ? 'expand' as const : undefined\n\n // Resolve special per-bar colouring modes (positive/negative + colour-by-category)\n const { usePositiveNegativeColoring, useColorByCategory } =\n resolveBarColoringMode(seriesKeys, chartData, seriesFields.length)\n\n // Determine if legend will be shown\n const showLegend = safeDisplayConfig.showLegend\n\n // Use custom chart margins with extra space for Y-axis labels\n const chartMargins = getYAxisChartMargins(hasRightAxis)\n\n // Process target values and add to chart data\n const { spreadTargets, enhancedChartData } = withTargetData(chartData, displayConfig?.target)\n\n // Validate transformed data\n if (!chartData || chartData.length === 0) {\n return (\n <ChartEmptyState\n height={height}\n titleKey=\"chart.runtime.noValidData\"\n hint=\"No valid data points for bar chart after transformation\"\n />\n )\n }\n\n return (\n <div className=\"dc:relative dc:w-full\" style={{ height }}>\n <ChartContainer height={skippedCount > 0 ? `calc(100% - 20px)` : \"100%\"}>\n <ComposedChart data={enhancedChartData} margin={chartMargins} stackOffset={stackOffset} accessibilityLayer={false}>\n {safeDisplayConfig.showGrid && (\n <CartesianGrid strokeDasharray=\"3 3\" style={{ pointerEvents: 'none' }} />\n )}\n <XAxis\n dataKey=\"name\"\n type=\"category\"\n tick={<AngledXAxisTick />}\n height={60}\n interval={showAllXLabels ? 0 : undefined}\n />\n {renderDualYAxes(axisInfo, getFieldLabel, leftYAxisFormat, rightYAxisFormat, effectiveIsPercentStack)}\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={makeCartesianTooltipFormatter({\n leftYAxisFormat,\n rightYAxisFormat,\n yAxisAssignment,\n resolveField: (name) => seriesKeyToField[name],\n isPercentStack: effectiveIsPercentStack\n })}\n />\n )}\n {renderHoverLegend({\n show: showLegend,\n iconType: 'rect',\n paddingTop: 25,\n onHover: setHoveredLegend,\n onLeave: () => setHoveredLegend(null)\n })}\n {seriesKeys.map((seriesKey, index) => {\n // Look up the original field name to get its axis assignment\n const originalField = seriesKeyToField[seriesKey]\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n return (\n <BarSeries\n key={seriesKey}\n seriesKey={seriesKey}\n index={index}\n originalField={originalField}\n axisId={axisId}\n stackId={effectiveShouldStack ? 'stack' : undefined}\n chartData={chartData}\n enhancedChartData={enhancedChartData}\n colorPalette={colorPalette}\n hoveredLegend={hoveredLegend}\n usePositiveNegativeColoring={usePositiveNegativeColoring}\n useColorByCategory={useColorByCategory}\n drillEnabled={drillEnabled}\n onDataPointClick={onDataPointClick}\n />\n )\n })}\n {renderChartTargetLines(spreadTargets)}\n </ComposedChart>\n </ChartContainer>\n {skippedCount > 0 && (\n <div className=\"dc:text-xs text-dc-text-muted dc:text-center dc:mt-1\">\n {skippedCount} data point{skippedCount !== 1 ? 's' : ''} with no values hidden\n </div>\n )}\n </div>\n )\n } catch (error) {\n return <ChartRenderError height={height} chartType=\"Bar Chart\" error={error} />\n }\n})\n\nexport default BarChart"],"mappings":";;;;;;;AAeA,SAAgB,GAAiB,GAA0D;CACzF,IAAM,IAAY,GAAe,cAAc,GAAe,UAAU,WAAW;CACnF,OAAO;EACL,aAAa,MAAc;EAC3B,gBAAgB,MAAc;CAChC;AACF;AAMA,SAAgB,EACd,GACA,GAC4D;CAC5D,IAAI,EAAgB,WAAW,KAAK,EAAW,WAAW,GACxD,OAAO;EAAE,WAAW,CAAC;EAAG,cAAc;CAAE;CAE1C,IAAM,IAAW,EAAgB,QAAO,MACtC,EAAW,MAAK,MAAO,EAAoB,EAAI,EAAI,CAAC,CACtD;CACA,OAAO;EAAE,WAAW;EAAU,cAAc,EAAgB,SAAS,EAAS;CAAO;AACvF;AAUA,SAAgB,EACd,GACA,GACA,GACiB;CACjB,IAAM,IACJ,EAAW,WAAW,KACtB,EAAU,MAAK,MAAO;EACpB,IAAM,IAAQ,EAAI,EAAW;EAC7B,OAAO,OAAO,KAAU,YAAY,IAAQ;CAC9C,CAAC;CAQH,OAAO;EAAE;EAA6B,oBALpC,EAAW,WAAW,KACtB,CAAC,KACD,CAAC,KACD,EAAU,SAAS;CAEoC;AAC3D;;;AC3CA,SAAS,EAAa,GAA8B,GAA2B;CAE7E,OADK,IACE,MAAkB,IAAY,IAAI,KADd;AAE7B;AAUA,SAAgB,EAAU,EACxB,cACA,UACA,kBACA,WACA,YACA,cACA,sBACA,iBACA,kBACA,gCACA,uBACA,iBACA,uBACiB;CAkBjB,OACE,kBAAC,GAAD;EACE,SAAS;EACT,SAAS;EACA;EACT,MAtBa,IACb,IACC,GAAc,UAAU,EAAa,OAAO,IAAQ,EAAa,OAAO,WACzE,EAAa,IAAQ,EAAa;EAoBlC,aAAa,EAAa,GAAe,CAAS;EAClD,QAAQ,IAAe,YAAY,KAAA;EACnC,UApBiB,GAAc,GAAmB,MAA4B;GAChF,AAAI,KAAoB,KAAgB,KACtC,EAAiB;IACf,WAAW,EAAkB,MAAc;IAC3C,cAAc,KAAiB;IAC/B,QAAQ,EAAQ;IAChB,UAAU;KAAE,GAAG,EAAM;KAAS,GAAG,EAAM;IAAQ;IAC/C,aAAa;GACf,CAAC;EAEL;YAGE,CASG,KACC,EAAU,KAAK,GAAO,MAAe;GACnC,IAAM,IAAQ,EAAM;GAEpB,OACE,kBAAC,GAAD;IAEE,MAJc,OAAO,KAAU,YAAY,IAAQ,IAAA,YAAA;IAKnD,aAAa,EAAa,GAAe,CAAS;GACnD,GAHM,QAAQ,GAGd;EAEL,CAAC,GACF,KACC,EAAU,KAAK,GAAQ,MAAe;GACpC,IAAM,IAAS,GAAc,UAAU;GACvC,OACE,kBAAC,GAAD;IAEE,MAAM,EAAO,IAAa,EAAO;IACjC,aAAa,EAAa,GAAe,CAAS;GACnD,GAHM,OAAO,GAGb;EAEL,CAAC,CACA;;AAET;;;iDCjFM,IAAW,EAAM,KAAK,SAAkB,EAC5C,SACA,gBACA,mBAAgB,CAAC,GACjB,gBACA,YAAS,QACT,iBACA,qBACA,mBACa;CACb,IAAM,EAAE,SAAM,EAAe,GACvB,CAAC,GAAe,KAAoB,EAAwB,IAAI,GAEhE,IAAgB,EAAkB,GAGlC,EAAE,gBAAa,uBAAmB,GAAiB,CAAa,GAEhE,IAAoB;EACxB,YAAY,GAAe,cAAc;EACzC,UAAU,GAAe,YAAY;EACrC,aAAa,GAAe,eAAe;CAC7C,GAEM,KAAiB,GAAe,kBAAkB,IAGlD,IAAkB,GAAe,iBACjC,IAAmB,GAAe,kBAGlC,EAAE,eAAY,gBAAa,iBAAc,iBAAc,QACrD,EAAuB,CAAW,GACxC,CAAC,CAAW,CACd,GAGM,EAAE,MAAM,GAAiB,kBAAe,QACxC,KAAa,CAAC,KAAQ,EAAK,WAAW,KAAK,CAAC,IACvC;EAAE,MAAM,CAAC;EAAG,YAAY,CAAC;CAAE,IAE7B,EACL,GACA,GACA,GACA,GACA,GACA,CACF,GACC;EAAC;EAAM;EAAY;EAAa;EAAa;EAAc;EAAe;CAAS,CAAC,GAGjF,IAAkB,QACtB,GAAa,mBAAmB,CAAC,GACjC,CAAC,GAAa,eAAe,CAC/B,GAGM,IAA2C,QAAc;EAC7D,IAAM,IAAkC,CAAC;EAKzC,OAJA,EAAY,SAAS,MAAU;GAC7B,IAAM,IAAQ,EAAc,CAAK;GACjC,EAAQ,KAAS;EACnB,CAAC,GACM;CACT,GAAG,CAAC,GAAa,CAAa,CAAC,GAGzB,IAAW,EAAgB,GAAa,CAAe,GACvD,EAAE,oBAAiB,GAInB,EAAE,cAAW,oBAAiB,QAC5B,EAAgB,GAAiB,CAAU,GACjD,CAAC,GAAiB,CAAU,CAC9B;CAGA,IAAI;EACF,IAAI,CAAC,KAAQ,EAAK,WAAW,GAC3B,OAAO,kBAAC,GAAD;GAAyB;GAAQ,MAAM,EAAE,8BAA8B;EAAI,CAAA;EAGpF,IAAI,GACF,OAAO,kBAAC,GAAD;GAA0B;GAAQ,MAAM,EAAE,iCAAiC,GAAW;EAAI,CAAA;EAKnG,IAAM,IAAuB,KAAe,CAAC,GACvC,IAA0B,MAAkB,CAAC,GAC7C,IAAc,IAA0B,WAAoB,KAAA,GAG5D,EAAE,gCAA6B,0BACnC,EAAuB,GAAY,GAAW,EAAa,MAAM,GAG7D,IAAa,EAAkB,YAG/B,IAAe,EAAqB,CAAY,GAGhD,EAAE,kBAAe,yBAAsB,EAAe,GAAW,GAAe,MAAM;EAa5F,OAVI,CAAC,KAAa,EAAU,WAAW,IAEnC,kBAAC,GAAD;GACU;GACR,UAAS;GACT,MAAK;EACN,CAAA,IAKH,kBAAC,OAAD;GAAK,WAAU;GAAwB,OAAO,EAAE,UAAO;aAAvD,CACE,kBAAC,GAAD;IAAgB,QAAQ,IAAe,IAAI,sBAAsB;cAC/D,kBAAC,GAAD;KAAe,MAAM;KAAmB,QAAQ;KAA2B;KAAa,oBAAoB;eAA5G;MACC,EAAkB,YACjB,kBAAC,GAAD;OAAe,iBAAgB;OAAM,OAAO,EAAE,eAAe,OAAO;MAAI,CAAA;MAE1E,kBAAC,IAAD;OACE,SAAQ;OACR,MAAK;OACL,MAAM,kBAAC,GAAD,CAAkB,CAAA;OACxB,QAAQ;OACR,UAAU,KAAiB,IAAI,KAAA;MAChC,CAAA;MACA,EAAgB,GAAU,GAAe,GAAiB,GAAkB,CAAuB;MACnG,EAAkB,eACjB,kBAAC,GAAD,EACE,WAAW,EAA8B;OACvC;OACA;OACA;OACA,eAAe,MAAS,EAAiB;OACzC,gBAAgB;MAClB,CAAC,EACF,CAAA;MAEF,EAAkB;OACjB,MAAM;OACN,UAAU;OACV,YAAY;OACZ,SAAS;OACT,eAAe,EAAiB,IAAI;MACtC,CAAC;MACA,EAAW,KAAK,GAAW,MAAU;OAEpC,IAAM,IAAgB,EAAiB;OAEvC,OACE,kBAAC,GAAD;QAEa;QACJ;QACQ;QACP,QAPG,KAAiB,EAAgB,OAAmB,UAAU,UAAU;QAQnF,SAAS,IAAuB,UAAU,KAAA;QAC/B;QACQ;QACL;QACC;QACc;QACT;QACN;QACI;OACnB,GAdM,CAcN;MAEL,CAAC;MACA,EAAuB,CAAa;KACtB;;GACD,CAAA,GACf,IAAe,KACd,kBAAC,OAAD;IAAK,WAAU;cAAf;KACG;KAAa;KAAY,MAAiB,IAAU,KAAN;KAAS;IACrD;KAEJ;;CAET,SAAS,GAAO;EACd,OAAO,kBAAC,GAAD;GAA0B;GAAQ,WAAU;GAAmB;EAAQ,CAAA;CAChF;AACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"chart-bar-BqelQE_I.js","names":[],"sources":["../../../src/client/components/charts/BarChart.helpers.ts","../../../src/client/components/charts/BarSeries.tsx","../../../src/client/components/charts/BarChart.tsx"],"sourcesContent":["/**\n * Co-located helpers for BarChart.\n *\n * Pure decisions about stacking and bar colouring extracted from the component\n * render body. No behaviour change — these mirror the original inline logic.\n */\nimport { isValidNumericValue } from '../../utils/chartUtils.js'\nimport type { ChartDisplayConfig } from '../../types.js'\n\nexport interface StackMode {\n shouldStack: boolean\n isPercentStack: boolean\n}\n\n/** Derive stacking flags from `stackType` (new) or `stacked` (legacy). */\nexport function resolveStackMode(displayConfig: ChartDisplayConfig | undefined): StackMode {\n const stackType = displayConfig?.stackType ?? (displayConfig?.stacked ? 'normal' : 'none')\n return {\n shouldStack: stackType !== 'none',\n isPercentStack: stackType === 'percent'\n }\n}\n\n/**\n * Filter out rows where every series value is null, returning the kept rows and\n * the number skipped (used to render the \"N hidden\" footer note).\n */\nexport function filterEmptyRows(\n transformedData: Record<string, any>[],\n seriesKeys: string[]\n): { chartData: Record<string, any>[]; skippedCount: number } {\n if (transformedData.length === 0 || seriesKeys.length === 0) {\n return { chartData: [], skippedCount: 0 }\n }\n const filtered = transformedData.filter(row =>\n seriesKeys.some(key => isValidNumericValue(row[key]))\n )\n return { chartData: filtered, skippedCount: transformedData.length - filtered.length }\n}\n\nexport interface BarColoringMode {\n /** Single series with mixed positive/negative values → green/red bars. */\n usePositiveNegativeColoring: boolean\n /** Single measure, no series dimension, multiple categories → colour per category. */\n useColorByCategory: boolean\n}\n\n/** Decide which special per-bar colouring mode (if any) applies. */\nexport function resolveBarColoringMode(\n seriesKeys: string[],\n chartData: Record<string, any>[],\n seriesFieldsLength: number\n): BarColoringMode {\n const usePositiveNegativeColoring =\n seriesKeys.length === 1 &&\n chartData.some(row => {\n const value = row[seriesKeys[0]]\n return typeof value === 'number' && value < 0\n })\n\n const useColorByCategory =\n seriesKeys.length === 1 &&\n !usePositiveNegativeColoring &&\n !seriesFieldsLength &&\n chartData.length > 1\n\n return { usePositiveNegativeColoring, useColorByCategory }\n}\n","import React from 'react'\nimport { Bar, Cell } from 'recharts'\nimport { CHART_COLORS, POSITIVE_COLOR, NEGATIVE_COLOR } from '../../utils/chartConstants.js'\nimport type { ColorPalette } from '../../types.js'\nimport type { ChartDataPointClickEvent } from '../../types/drill.js'\n\ninterface BarSeriesProps {\n seriesKey: string\n index: number\n /** Original field name backing this series key (for axis assignment + drill). */\n originalField: string | undefined\n axisId: 'left' | 'right'\n stackId: string | undefined\n chartData: Record<string, any>[]\n enhancedChartData: Record<string, any>[]\n colorPalette?: ColorPalette\n hoveredLegend: string | null\n usePositiveNegativeColoring: boolean\n useColorByCategory: boolean\n drillEnabled?: boolean\n onDataPointClick?: (event: ChartDataPointClickEvent) => void\n}\n\n/** Opacity for a bar/cell given the currently hovered legend key. */\nfunction hoverOpacity(hoveredLegend: string | null, seriesKey: string): number {\n if (!hoveredLegend) return 1\n return hoveredLegend === seriesKey ? 1 : 0.3\n}\n\n/**\n * Single `<Bar>` for a series key.\n *\n * Encapsulates the per-bar colouring modes (positive/negative, colour-by-category)\n * and the drill click handler that were previously inlined in BarChart's render.\n * Returned as a fragment-bearing component so it can be used directly inside the\n * Recharts `<ComposedChart>` children list. Behaviour is identical to the original.\n */\nexport function BarSeries({\n seriesKey,\n index,\n originalField,\n axisId,\n stackId,\n chartData,\n enhancedChartData,\n colorPalette,\n hoveredLegend,\n usePositiveNegativeColoring,\n useColorByCategory,\n drillEnabled,\n onDataPointClick\n}: BarSeriesProps) {\n const baseFill = usePositiveNegativeColoring\n ? POSITIVE_COLOR\n : (colorPalette?.colors && colorPalette.colors[index % colorPalette.colors.length]) ||\n CHART_COLORS[index % CHART_COLORS.length]\n\n const handleClick = (barData: any, dataIndex: number, event: React.MouseEvent) => {\n if (onDataPointClick && drillEnabled && barData) {\n onDataPointClick({\n dataPoint: enhancedChartData[dataIndex] || barData,\n clickedField: originalField || seriesKey,\n xValue: barData.name,\n position: { x: event.clientX, y: event.clientY },\n nativeEvent: event\n })\n }\n }\n\n return (\n <Bar\n dataKey={seriesKey}\n yAxisId={axisId}\n stackId={stackId}\n fill={baseFill}\n fillOpacity={hoverOpacity(hoveredLegend, seriesKey)}\n cursor={drillEnabled ? 'pointer' : undefined}\n onClick={handleClick}\n >\n {usePositiveNegativeColoring &&\n chartData.map((entry, entryIndex) => {\n const value = entry[seriesKey]\n const fillColor = typeof value === 'number' && value < 0 ? NEGATIVE_COLOR : POSITIVE_COLOR\n return (\n <Cell\n key={`cell-${entryIndex}`}\n fill={fillColor}\n fillOpacity={hoverOpacity(hoveredLegend, seriesKey)}\n />\n )\n })}\n {useColorByCategory &&\n chartData.map((_entry, entryIndex) => {\n const colors = colorPalette?.colors || CHART_COLORS\n return (\n <Cell\n key={`cat-${entryIndex}`}\n fill={colors[entryIndex % colors.length]}\n fillOpacity={hoverOpacity(hoveredLegend, seriesKey)}\n />\n )\n })}\n </Bar>\n )\n}\n","import React, { useState, useMemo } from 'react'\nimport { useTranslation } from '../../hooks/useTranslation.js'\nimport { ComposedChart, XAxis, CartesianGrid } from 'recharts'\nimport ChartContainer from './ChartContainer.js'\nimport ChartTooltip from './ChartTooltip.js'\nimport AngledXAxisTick from './AngledXAxisTick.js'\nimport { ChartEmptyState, ChartConfigError, ChartRenderError } from './ChartStates.js'\nimport { resolveChartAxisFields } from './chartAxisResolution.js'\nimport {\n getDualAxisInfo,\n getYAxisChartMargins,\n withTargetData,\n renderDualYAxes,\n renderChartTargetLines,\n makeCartesianTooltipFormatter,\n renderHoverLegend\n} from './chartScaffolding.js'\nimport { resolveStackMode, filterEmptyRows, resolveBarColoringMode } from './BarChart.helpers.js'\nimport { BarSeries } from './BarSeries.js'\nimport { transformChartDataWithSeries } from '../../utils/chartUtils.js'\nimport { useCubeFieldLabel } from '../../hooks/useCubeFieldLabel.js'\nimport type { ChartProps } from '../../types.js'\n\nconst BarChart = React.memo(function BarChart({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n onDataPointClick,\n drillEnabled\n}: ChartProps) {\n const { t } = useTranslation()\n const [hoveredLegend, setHoveredLegend] = useState<string | null>(null)\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel()\n\n // Determine stacking from stackType (new) or stacked (legacy)\n const { shouldStack, isPercentStack } = resolveStackMode(displayConfig)\n\n const safeDisplayConfig = {\n showLegend: displayConfig?.showLegend ?? true,\n showGrid: displayConfig?.showGrid ?? true,\n showTooltip: displayConfig?.showTooltip ?? true\n }\n\n const showAllXLabels = displayConfig?.showAllXLabels ?? true\n\n // Extract axis format configs\n const leftYAxisFormat = displayConfig?.leftYAxisFormat\n const rightYAxisFormat = displayConfig?.rightYAxisFormat\n\n // Resolve + validate axis fields (hooks-first; early returns happen after all hooks)\n const { xAxisField, yAxisFields, seriesFields, errorCode } = useMemo(\n () => resolveChartAxisFields(chartConfig),\n [chartConfig]\n )\n\n // Transform data (will be empty arrays if config is invalid)\n const { data: transformedData, seriesKeys } = useMemo(() => {\n if (errorCode || !data || data.length === 0 || !xAxisField) {\n return { data: [], seriesKeys: [] }\n }\n return transformChartDataWithSeries(\n data,\n xAxisField,\n yAxisFields,\n queryObject,\n seriesFields,\n getFieldLabel\n )\n }, [data, xAxisField, yAxisFields, queryObject, seriesFields, getFieldLabel, errorCode])\n\n // Dual Y-axis support: extract yAxisAssignment from chartConfig (memoized to prevent object recreation)\n const yAxisAssignment = useMemo(() =>\n chartConfig?.yAxisAssignment || {},\n [chartConfig?.yAxisAssignment]\n )\n\n // Build mapping from series key (label) to original field name\n const seriesKeyToField: Record<string, string> = useMemo(() => {\n const mapping: Record<string, string> = {}\n yAxisFields.forEach((field) => {\n const label = getFieldLabel(field)\n mapping[label] = field\n })\n return mapping\n }, [yAxisFields, getFieldLabel])\n\n // Dual Y-axis derivation (shared scaffolding)\n const axisInfo = getDualAxisInfo(yAxisFields, yAxisAssignment)\n const { hasRightAxis } = axisInfo\n\n // Null handling: Filter out data points where ALL measure values are null\n // This prevents rendering empty bars and makes the chart clearer\n const { chartData, skippedCount } = useMemo(\n () => filterEmptyRows(transformedData, seriesKeys),\n [transformedData, seriesKeys]\n )\n\n // Now handle early returns AFTER all hooks\n try {\n if (!data || data.length === 0) {\n return <ChartEmptyState height={height} hint={t('chart.runtime.noDataHint.bar')} />\n }\n\n if (errorCode) {\n return <ChartConfigError height={height} hint={t(`chart.runtime.configErrorHint.${errorCode}`)} />\n }\n\n // Determine stack offset for percentage stacking\n // Disable stacking when dual Y-axis is used (bars on different axes can't be stacked)\n const effectiveShouldStack = shouldStack && !hasRightAxis\n const effectiveIsPercentStack = isPercentStack && !hasRightAxis\n const stackOffset = effectiveIsPercentStack ? 'expand' as const : undefined\n\n // Resolve special per-bar colouring modes (positive/negative + colour-by-category)\n const { usePositiveNegativeColoring, useColorByCategory } =\n resolveBarColoringMode(seriesKeys, chartData, seriesFields.length)\n\n // Determine if legend will be shown\n const showLegend = safeDisplayConfig.showLegend\n\n // Use custom chart margins with extra space for Y-axis labels\n const chartMargins = getYAxisChartMargins(hasRightAxis)\n\n // Process target values and add to chart data\n const { spreadTargets, enhancedChartData } = withTargetData(chartData, displayConfig?.target)\n\n // Validate transformed data\n if (!chartData || chartData.length === 0) {\n return (\n <ChartEmptyState\n height={height}\n titleKey=\"chart.runtime.noValidData\"\n hint=\"No valid data points for bar chart after transformation\"\n />\n )\n }\n\n return (\n <div className=\"dc:relative dc:w-full\" style={{ height }}>\n <ChartContainer height={skippedCount > 0 ? `calc(100% - 20px)` : \"100%\"}>\n <ComposedChart data={enhancedChartData} margin={chartMargins} stackOffset={stackOffset} accessibilityLayer={false}>\n {safeDisplayConfig.showGrid && (\n <CartesianGrid strokeDasharray=\"3 3\" style={{ pointerEvents: 'none' }} />\n )}\n <XAxis\n dataKey=\"name\"\n type=\"category\"\n tick={<AngledXAxisTick />}\n height={60}\n interval={showAllXLabels ? 0 : undefined}\n />\n {renderDualYAxes(axisInfo, getFieldLabel, leftYAxisFormat, rightYAxisFormat, effectiveIsPercentStack)}\n {safeDisplayConfig.showTooltip && (\n <ChartTooltip\n formatter={makeCartesianTooltipFormatter({\n leftYAxisFormat,\n rightYAxisFormat,\n yAxisAssignment,\n resolveField: (name) => seriesKeyToField[name],\n isPercentStack: effectiveIsPercentStack\n })}\n />\n )}\n {renderHoverLegend({\n show: showLegend,\n iconType: 'rect',\n paddingTop: 25,\n onHover: setHoveredLegend,\n onLeave: () => setHoveredLegend(null)\n })}\n {seriesKeys.map((seriesKey, index) => {\n // Look up the original field name to get its axis assignment\n const originalField = seriesKeyToField[seriesKey]\n const axisId = originalField && yAxisAssignment[originalField] === 'right' ? 'right' : 'left'\n return (\n <BarSeries\n key={seriesKey}\n seriesKey={seriesKey}\n index={index}\n originalField={originalField}\n axisId={axisId}\n stackId={effectiveShouldStack ? 'stack' : undefined}\n chartData={chartData}\n enhancedChartData={enhancedChartData}\n colorPalette={colorPalette}\n hoveredLegend={hoveredLegend}\n usePositiveNegativeColoring={usePositiveNegativeColoring}\n useColorByCategory={useColorByCategory}\n drillEnabled={drillEnabled}\n onDataPointClick={onDataPointClick}\n />\n )\n })}\n {renderChartTargetLines(spreadTargets)}\n </ComposedChart>\n </ChartContainer>\n {skippedCount > 0 && (\n <div className=\"dc:text-xs text-dc-text-muted dc:text-center dc:mt-1\">\n {skippedCount} data point{skippedCount !== 1 ? 's' : ''} with no values hidden\n </div>\n )}\n </div>\n )\n } catch (error) {\n return <ChartRenderError height={height} chartType=\"Bar Chart\" error={error} />\n }\n})\n\nexport default BarChart"],"mappings":"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"}
|