drizzle-cube 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/client/charts.js +13 -13
  2. package/dist/client/chunks/{RetentionCombinedChart-7bGP_ozM.js → RetentionCombinedChart-BK8N-MOQ.js} +2 -2
  3. package/dist/client/chunks/{RetentionCombinedChart-7bGP_ozM.js.map → RetentionCombinedChart-BK8N-MOQ.js.map} +1 -1
  4. package/dist/client/chunks/{analysis-builder-BlCXpnDL.js → analysis-builder-CNBmAGAO.js} +8 -9
  5. package/dist/client/chunks/{analysis-builder-BlCXpnDL.js.map → analysis-builder-CNBmAGAO.js.map} +1 -1
  6. package/dist/client/chunks/{analysis-builder-shared-BOYNJQY8.js → analysis-builder-shared-INGGwyTG.js} +6 -6
  7. package/dist/client/chunks/{analysis-builder-shared-BOYNJQY8.js.map → analysis-builder-shared-INGGwyTG.js.map} +1 -1
  8. package/dist/client/chunks/{chart-activity-grid-sUkuCJzm.js → chart-activity-grid-DvgTYQaE.js} +2 -2
  9. package/dist/client/chunks/{chart-activity-grid-sUkuCJzm.js.map → chart-activity-grid-DvgTYQaE.js.map} +1 -1
  10. package/dist/client/chunks/{chart-area-C4DIi9k_.js → chart-area-BKjd_STS.js} +3 -3
  11. package/dist/client/chunks/{chart-area-C4DIi9k_.js.map → chart-area-BKjd_STS.js.map} +1 -1
  12. package/dist/client/chunks/{chart-bar-7ZleGCsN.js → chart-bar-DBI41w05.js} +2 -2
  13. package/dist/client/chunks/{chart-bar-7ZleGCsN.js.map → chart-bar-DBI41w05.js.map} +1 -1
  14. package/dist/client/chunks/{chart-bubble-D-auWIjP.js → chart-bubble-BD-1kneU.js} +2 -2
  15. package/dist/client/chunks/{chart-bubble-D-auWIjP.js.map → chart-bubble-BD-1kneU.js.map} +1 -1
  16. package/dist/client/chunks/{chart-config-markdown-DZxdGNVQ.js → chart-config-markdown-6fsr-U6_.js} +38 -8
  17. package/dist/client/chunks/chart-config-markdown-6fsr-U6_.js.map +1 -0
  18. package/dist/client/chunks/{chart-data-table-DW6VNGeW.js → chart-data-table-B74HLpAx.js} +3 -3
  19. package/dist/client/chunks/{chart-data-table-DW6VNGeW.js.map → chart-data-table-B74HLpAx.js.map} +1 -1
  20. package/dist/client/chunks/{chart-funnel-eUJApzmA.js → chart-funnel-DQ9cW6j9.js} +2 -2
  21. package/dist/client/chunks/{chart-funnel-eUJApzmA.js.map → chart-funnel-DQ9cW6j9.js.map} +1 -1
  22. package/dist/client/chunks/{chart-heat-map-DR3O3GXx.js → chart-heat-map-CpIr4tbs.js} +2 -2
  23. package/dist/client/chunks/{chart-heat-map-DR3O3GXx.js.map → chart-heat-map-CpIr4tbs.js.map} +1 -1
  24. package/dist/client/chunks/{chart-kpi-delta-Bp9Yrgyc.js → chart-kpi-delta-DPcSazD3.js} +3 -3
  25. package/dist/client/chunks/{chart-kpi-delta-Bp9Yrgyc.js.map → chart-kpi-delta-DPcSazD3.js.map} +1 -1
  26. package/dist/client/chunks/{chart-kpi-number-B3yvgbr-.js → chart-kpi-number-D62PzKZ1.js} +2 -2
  27. package/dist/client/chunks/{chart-kpi-number-B3yvgbr-.js.map → chart-kpi-number-D62PzKZ1.js.map} +1 -1
  28. package/dist/client/chunks/{chart-kpi-text-D0TsUiaO.js → chart-kpi-text-B6Z5tFV1.js} +3 -3
  29. package/dist/client/chunks/{chart-kpi-text-D0TsUiaO.js.map → chart-kpi-text-B6Z5tFV1.js.map} +1 -1
  30. package/dist/client/chunks/{chart-line-C7VStgaJ.js → chart-line-Ds4BYA0K.js} +3 -3
  31. package/dist/client/chunks/{chart-line-C7VStgaJ.js.map → chart-line-Ds4BYA0K.js.map} +1 -1
  32. package/dist/client/chunks/chart-markdown-Rq6ORisB.js +276 -0
  33. package/dist/client/chunks/chart-markdown-Rq6ORisB.js.map +1 -0
  34. package/dist/client/chunks/{chart-pie-DPdYJSM3.js → chart-pie-BBrLh0iU.js} +2 -2
  35. package/dist/client/chunks/{chart-pie-DPdYJSM3.js.map → chart-pie-BBrLh0iU.js.map} +1 -1
  36. package/dist/client/chunks/{chart-radar-DF6VNSiC.js → chart-radar-vr5FgjHT.js} +2 -2
  37. package/dist/client/chunks/{chart-radar-DF6VNSiC.js.map → chart-radar-vr5FgjHT.js.map} +1 -1
  38. package/dist/client/chunks/{chart-radial-bar-7Ttfz-kO.js → chart-radial-bar-BV_3Nm5P.js} +2 -2
  39. package/dist/client/chunks/{chart-radial-bar-7Ttfz-kO.js.map → chart-radial-bar-BV_3Nm5P.js.map} +1 -1
  40. package/dist/client/chunks/{chart-sankey-D86RYk9B.js → chart-sankey-CHROzr4S.js} +2 -2
  41. package/dist/client/chunks/{chart-sankey-D86RYk9B.js.map → chart-sankey-CHROzr4S.js.map} +1 -1
  42. package/dist/client/chunks/{chart-scatter-BUnIiLr_.js → chart-scatter-DbRTU3FG.js} +2 -2
  43. package/dist/client/chunks/{chart-scatter-BUnIiLr_.js.map → chart-scatter-DbRTU3FG.js.map} +1 -1
  44. package/dist/client/chunks/{chart-sunburst-CsHlHfZQ.js → chart-sunburst-B-aPUzYL.js} +2 -2
  45. package/dist/client/chunks/{chart-sunburst-CsHlHfZQ.js.map → chart-sunburst-B-aPUzYL.js.map} +1 -1
  46. package/dist/client/chunks/{chart-tree-map-DFAbkeo1.js → chart-tree-map-qY148fiC.js} +2 -2
  47. package/dist/client/chunks/{chart-tree-map-DFAbkeo1.js.map → chart-tree-map-qY148fiC.js.map} +1 -1
  48. package/dist/client/chunks/{chartConfigRegistry-DNEbwgTc.js → chartConfigRegistry-Di34paQH.js} +2 -2
  49. package/dist/client/chunks/{chartConfigRegistry-DNEbwgTc.js.map → chartConfigRegistry-Di34paQH.js.map} +1 -1
  50. package/dist/client/chunks/{charts-core-DAF1iT_h.js → charts-core-T8UglYyq.js} +224 -224
  51. package/dist/client/chunks/charts-core-T8UglYyq.js.map +1 -0
  52. package/dist/client/chunks/{charts-loader-7pEaEz-f.js → charts-loader-YnhJHubD.js} +21 -21
  53. package/dist/client/chunks/{charts-loader-7pEaEz-f.js.map → charts-loader-YnhJHubD.js.map} +1 -1
  54. package/dist/client/chunks/components-D0i1yQsk.js +9835 -0
  55. package/dist/client/chunks/components-D0i1yQsk.js.map +1 -0
  56. package/dist/client/chunks/{core-DGLxd5v5.js → core-D_8mkGpQ.js} +2 -2
  57. package/dist/client/chunks/{core-DGLxd5v5.js.map → core-D_8mkGpQ.js.map} +1 -1
  58. package/dist/client/chunks/{providers-D2xswCYu.js → providers-CgxXm6Ll.js} +2 -2
  59. package/dist/client/chunks/{providers-D2xswCYu.js.map → providers-CgxXm6Ll.js.map} +1 -1
  60. package/dist/client/chunks/{syntaxHighlighting-DAT9C6Ss.js → syntaxHighlighting-BQfjio-i.js} +2 -2
  61. package/dist/client/chunks/{syntaxHighlighting-DAT9C6Ss.js.map → syntaxHighlighting-BQfjio-i.js.map} +1 -1
  62. package/dist/client/chunks/{useDirtyStateTracking-CTS_m9mg.js → useDirtyStateTracking-Cu1HSjmo.js} +9 -9
  63. package/dist/client/chunks/{useDirtyStateTracking-CTS_m9mg.js.map → useDirtyStateTracking-Cu1HSjmo.js.map} +1 -1
  64. package/dist/client/chunks/{vendor-DfDLOfBe.js → vendor-AVsJ2ni0.js} +4 -4
  65. package/dist/client/chunks/{vendor-DfDLOfBe.js.map → vendor-AVsJ2ni0.js.map} +1 -1
  66. package/dist/client/components/AnalysisBuilder/AnalysisDisplayConfigPanel.d.ts +3 -1
  67. package/dist/client/components/FloatingEditToolbar.d.ts +3 -1
  68. package/dist/client/components/TextPortletModal.d.ts +12 -0
  69. package/dist/client/components.js +15 -352
  70. package/dist/client/components.js.map +1 -1
  71. package/dist/client/hooks/useDashboardHook.d.ts +7 -2
  72. package/dist/client/hooks.js +23 -23
  73. package/dist/client/index.js +8 -8
  74. package/dist/client/providers.js +1 -1
  75. package/dist/client/stores/dashboardStore.d.ts +14 -0
  76. package/dist/client/styles.css +1 -1
  77. package/dist/client/types.d.ts +3 -0
  78. package/dist/client/utils.js +6 -6
  79. package/dist/client-bundle-stats.html +1 -1
  80. package/package.json +1 -1
  81. package/dist/client/chunks/DashboardEditModal-BGDhxKg1.js +0 -9169
  82. package/dist/client/chunks/DashboardEditModal-BGDhxKg1.js.map +0 -1
  83. package/dist/client/chunks/chart-config-markdown-DZxdGNVQ.js.map +0 -1
  84. package/dist/client/chunks/chart-markdown-Cget3iEq.js +0 -257
  85. package/dist/client/chunks/chart-markdown-Cget3iEq.js.map +0 -1
  86. package/dist/client/chunks/charts-core-DAF1iT_h.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"chart-funnel-eUJApzmA.js","sources":["../../../src/client/components/charts/FunnelChart.tsx"],"sourcesContent":["/**\n * FunnelChart Component\n *\n * Visualizes funnel data showing conversion rates between steps.\n * Uses horizontal bars with percentage widths to represent the funnel shape.\n * Works with data from useFunnelQuery hook which provides FunnelChartData.\n *\n * When displayConfig.showFunnelTimeMetrics is enabled, displays time-to-convert\n * metrics from server-side funnel execution (avg, median, P90 seconds).\n */\n\nimport React, { useMemo } from 'react'\nimport { FunnelChart as RechartsFunnelChart, Funnel, LabelList, Tooltip, Cell, ResponsiveContainer } from 'recharts'\nimport { CHART_COLORS } from '../../utils/chartConstants'\nimport type { ChartProps } from '../../types'\nimport type { FunnelChartData } from '../../types/funnel'\nimport { formatDuration } from '../../utils/funnelExecution'\n\n// Color gradient for funnel steps (darker to lighter)\nconst FUNNEL_COLORS = [\n '#3B82F6', // blue-500\n '#60A5FA', // blue-400\n '#93C5FD', // blue-300\n '#BFDBFE', // blue-200\n '#DBEAFE', // blue-100\n]\n\n/**\n * Get color for a funnel step\n */\nfunction getStepColor(index: number, colorPalette?: string[]): string {\n const colors = colorPalette || FUNNEL_COLORS\n return colors[index % colors.length]\n}\n\n/**\n * Check if data is funnel data format\n */\nfunction isFunnelData(data: unknown[]): data is FunnelChartData[] {\n if (!data || data.length === 0) return false\n const first = data[0]\n return (\n typeof first === 'object' &&\n first !== null &&\n 'name' in first &&\n 'value' in first &&\n 'percentage' in first\n )\n}\n\n/**\n * Render time metrics based on config options - returns array of lines for vertical stacking\n */\nfunction getTimeMetricsLines(\n step: FunnelChartData,\n showAvg: boolean,\n showMedian: boolean,\n showP90: boolean\n): string[] {\n const lines: string[] = []\n if (showAvg && step.avgSecondsToConvert != null) {\n lines.push(`Avg: ${formatDuration(step.avgSecondsToConvert)}`)\n }\n if (showMedian && step.medianSecondsToConvert != null) {\n lines.push(`Med: ${formatDuration(step.medianSecondsToConvert)}`)\n }\n if (showP90 && step.p90SecondsToConvert != null) {\n lines.push(`P90: ${formatDuration(step.p90SecondsToConvert)}`)\n }\n return lines\n}\n\n/**\n * FunnelChart Component\n *\n * Renders a funnel visualization from FunnelChartData array.\n * Shows each step as a horizontal bar with width proportional to count.\n * Displays conversion rates between steps.\n */\nconst FunnelChart = React.memo(function FunnelChart({\n data,\n height = '100%',\n colorPalette,\n displayConfig,\n}: ChartProps) {\n // Get display config options\n const customStepLabels = displayConfig?.funnelStepLabels\n const orientation = displayConfig?.funnelOrientation || 'horizontal'\n const isVertical = orientation === 'vertical'\n const funnelStyle = displayConfig?.funnelStyle ?? 'bars'\n const showConversion = displayConfig?.showFunnelConversion ?? true\n\n // Time metrics - individual toggles with backward compat for showFunnelTimeMetrics\n const showAvgTime = displayConfig?.showFunnelAvgTime ??\n (displayConfig?.showFunnelTimeMetrics ?? false) // backward compat\n const showMedianTime = displayConfig?.showFunnelMedianTime ?? false\n const showP90Time = displayConfig?.showFunnelP90Time ?? false\n\n // Transform data if needed\n const funnelData = useMemo<FunnelChartData[]>(() => {\n if (!data || data.length === 0) return []\n\n // If already funnel data format, use directly\n if (isFunnelData(data)) {\n return data\n }\n\n // Try to convert from raw query results\n // Look for common patterns: step/name, count/value, percentage\n return data.map((row, index) => {\n const record = row as Record<string, unknown>\n\n // Find name field\n const nameField = Object.keys(record).find(\n (k) => k.toLowerCase().includes('step') || k.toLowerCase().includes('name') || k === '__stepName'\n )\n const name = nameField ? String(record[nameField]) : `Step ${index + 1}`\n\n // Find value field\n const valueField = Object.keys(record).find(\n (k) => k.toLowerCase().includes('count') || k.toLowerCase().includes('value') || k === '__count'\n )\n const value = valueField ? Number(record[valueField]) || 0 : 0\n\n // Find percentage field\n const percentField = Object.keys(record).find(\n (k) => k.toLowerCase().includes('percent') || k === '__percentage'\n )\n const percentage = percentField ? Number(record[percentField]) || 0 : 0\n\n // Find conversion rate field\n const convRateField = Object.keys(record).find(\n (k) => k.toLowerCase().includes('conversion') || k === '__conversionRate'\n )\n const conversionRate = convRateField ? Number(record[convRateField]) || null : null\n\n return {\n name,\n value,\n percentage,\n conversionRate,\n stepIndex: index,\n }\n })\n }, [data])\n\n // Calculate first step value for percentage calculations\n const firstStepValue = funnelData[0]?.value || 0\n\n // Handle no data\n if (!data || data.length === 0 || funnelData.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No funnel data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n Configure a funnel with at least 2 steps and a binding key\n </div>\n </div>\n </div>\n )\n }\n\n const paletteColors = colorPalette?.colors || CHART_COLORS\n\n // Render Recharts Funnel style (trapezoid shape)\n if (funnelStyle === 'funnel') {\n // Recharts FunnelChart layout: 'horizontal' = funnel flows left-to-right, 'vertical' = top-to-bottom (default)\n // Our config: 'horizontal' orientation = standard top-to-bottom funnel\n // 'vertical' orientation = left-to-right funnel\n const rechartsLayout: 'horizontal' | 'vertical' = isVertical ? 'horizontal' : 'vertical'\n\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:flex dc:flex-col\" style={{ height }}>\n <div className=\"dc:flex-1\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <RechartsFunnelChart layout={rechartsLayout} accessibilityLayer={false}>\n <Tooltip\n formatter={(value) => typeof value === 'number' ? value.toLocaleString() : String(value)}\n contentStyle={{\n backgroundColor: 'var(--dc-surface)',\n border: '1px solid var(--dc-border)',\n borderRadius: '4px',\n }}\n />\n <Funnel\n dataKey=\"value\"\n nameKey=\"name\"\n data={funnelData}\n isAnimationActive\n >\n {funnelData.map((_, index) => (\n <Cell key={`cell-${index}`} fill={getStepColor(index, paletteColors)} />\n ))}\n <LabelList\n position=\"right\"\n dataKey=\"name\"\n fill=\"var(--dc-text)\"\n style={{ fontSize: '12px' }}\n />\n <LabelList\n position=\"center\"\n dataKey=\"percentage\"\n formatter={(v) => typeof v === 'number' ? `${v.toFixed(1)}%` : String(v)}\n fill=\"#fff\"\n style={{ fontSize: '11px', fontWeight: 500 }}\n />\n </Funnel>\n </RechartsFunnelChart>\n </ResponsiveContainer>\n </div>\n {/* Summary Footer */}\n {!displayConfig?.hideSummaryFooter && (\n <div className=\"dc:flex-shrink-0 dc:px-4 dc:py-2 dc:border-t border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:text-sm\">\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{funnelData.length}</span> steps\n </div>\n <div className=\"text-dc-text\">\n <span className=\"text-dc-text-muted\">Overall:</span>{' '}\n <span className=\"dc:font-medium\">\n {firstStepValue > 0\n ? `${((funnelData[funnelData.length - 1]?.value || 0) / firstStepValue * 100).toFixed(1)}%`\n : '0%'}\n </span>\n </div>\n <div className=\"text-dc-text-muted\">\n {funnelData[funnelData.length - 1]?.value.toLocaleString() || 0} / {firstStepValue.toLocaleString()} completed\n </div>\n </div>\n </div>\n )}\n </div>\n )\n }\n\n // Render vertical orientation (bars grow from bottom to top, steps laid out horizontally)\n if (isVertical) {\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:flex dc:flex-col\" style={{ height }}>\n {/* Funnel Steps - Vertical Layout */}\n <div className=\"dc:flex-1 dc:flex dc:items-end dc:justify-center dc:gap-4 dc:px-4 dc:py-3 dc:overflow-hidden\">\n {funnelData.map((step, index) => {\n const heightPercent = firstStepValue > 0 ? (step.value / firstStepValue) * 100 : 0\n const prevStep = index > 0 ? funnelData[index - 1] : null\n const stepConversionRate = prevStep && prevStep.value > 0\n ? (step.value / prevStep.value) * 100\n : null\n\n // Use custom label if provided, otherwise fall back to step name\n const displayName = customStepLabels?.[index] || step.name\n\n const timeMetricsLines = getTimeMetricsLines(step, showAvgTime, showMedianTime, showP90Time)\n const metricsCount = timeMetricsLines.length\n\n return (\n <div key={step.name} className=\"dc:flex dc:flex-col dc:items-center dc:gap-2 dc:flex-1 dc:max-w-32 dc:h-full\">\n {/* Conversion Rate from Previous (top) */}\n <div className={`${metricsCount > 0 ? (metricsCount > 1 ? 'dc:min-h-16' : 'dc:min-h-10') : 'dc:h-5'} dc:flex-shrink-0 dc:text-center`}>\n {stepConversionRate !== null ? (\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {showConversion && <span>→ {stepConversionRate.toFixed(1)}%</span>}\n {/* Time metrics (when enabled) */}\n {metricsCount > 0 && (\n <div className=\"text-dc-text-muted dc:mt-0.5 dc:space-y-0.5\">\n {timeMetricsLines.map((line, i) => (\n <div key={i}>⏱ {line}</div>\n ))}\n </div>\n )}\n </div>\n ) : (\n <div className=\"dc:text-xs text-dc-text-muted\">—</div>\n )}\n </div>\n\n {/* Bar Container */}\n <div className=\"dc:flex-1 dc:w-full dc:relative dc:min-h-12\">\n {/* Background Track */}\n <div className=\"dc:absolute dc:inset-0 bg-dc-surface-secondary dc:rounded-sm\" />\n\n {/* Filled Bar (grows from bottom) */}\n <div\n className=\"dc:absolute dc:bottom-0 dc:left-0 dc:right-0 dc:rounded-sm dc:transition-all dc:duration-300\"\n style={{\n height: `${Math.max(heightPercent, 5)}%`,\n backgroundColor: getStepColor(index, paletteColors),\n }}\n />\n\n {/* Percentage Label on Bar */}\n <div\n className=\"dc:absolute dc:bottom-0 dc:left-0 dc:right-0 dc:flex dc:items-end dc:justify-center dc:pb-1 dc:pointer-events-none\"\n style={{ height: `${Math.max(heightPercent, 20)}%` }}\n >\n <span className=\"dc:text-xs dc:font-medium text-white dc:drop-shadow-sm\">\n {step.percentage?.toFixed(1) ?? heightPercent.toFixed(1)}%\n </span>\n </div>\n </div>\n\n {/* Step Label (bottom) */}\n <div className=\"dc:flex-shrink-0 dc:text-center\">\n <div className=\"dc:text-sm dc:font-medium text-dc-text dc:truncate\" title={displayName}>\n {displayName}\n </div>\n <div className=\"dc:text-xs text-dc-text-muted\">\n {step.value.toLocaleString()}\n </div>\n </div>\n </div>\n )\n })}\n </div>\n\n {/* Summary Footer */}\n {!displayConfig?.hideSummaryFooter && (\n <div className=\"dc:flex-shrink-0 dc:px-4 dc:py-2 dc:border-t border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:text-sm\">\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{funnelData.length}</span> steps\n </div>\n <div className=\"text-dc-text\">\n <span className=\"text-dc-text-muted\">Overall:</span>{' '}\n <span className=\"dc:font-medium\">\n {firstStepValue > 0\n ? `${((funnelData[funnelData.length - 1]?.value || 0) / firstStepValue * 100).toFixed(1)}%`\n : '0%'}\n </span>\n </div>\n <div className=\"text-dc-text-muted\">\n {funnelData[funnelData.length - 1]?.value.toLocaleString() || 0} / {firstStepValue.toLocaleString()} completed\n </div>\n </div>\n </div>\n )}\n </div>\n )\n }\n\n // Render horizontal orientation (default - bars grow left to right, steps stacked vertically)\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:flex dc:flex-col\" style={{ height }}>\n {/* Funnel Steps - Horizontal Layout */}\n <div className=\"dc:flex-1 dc:flex dc:flex-col dc:justify-center dc:gap-2 dc:px-4 dc:py-3 dc:overflow-hidden\">\n {funnelData.map((step, index) => {\n const widthPercent = firstStepValue > 0 ? (step.value / firstStepValue) * 100 : 0\n const prevStep = index > 0 ? funnelData[index - 1] : null\n const stepConversionRate = prevStep && prevStep.value > 0\n ? (step.value / prevStep.value) * 100\n : null\n\n // Use custom label if provided, otherwise fall back to step name\n const displayName = customStepLabels?.[index] || step.name\n const timeMetricsLines = getTimeMetricsLines(step, showAvgTime, showMedianTime, showP90Time)\n const metricsCount = timeMetricsLines.length\n\n return (\n <div key={step.name} className=\"dc:flex dc:items-center dc:gap-3\">\n {/* Step Label */}\n <div className=\"dc:w-24 dc:flex-shrink-0 dc:text-right\">\n <div className=\"dc:text-sm dc:font-medium text-dc-text dc:truncate\" title={displayName}>\n {displayName}\n </div>\n <div className=\"dc:text-xs text-dc-text-muted\">\n {step.value.toLocaleString()}\n </div>\n </div>\n\n {/* Bar Container */}\n <div className=\"dc:flex-1 dc:relative\">\n {/* Background Track */}\n <div className=\"dc:w-full dc:h-8 bg-dc-surface-secondary dc:rounded-sm\" />\n\n {/* Filled Bar */}\n <div\n className=\"dc:absolute dc:top-0 dc:left-0 dc:h-8 dc:rounded-sm dc:transition-all dc:duration-300\"\n style={{\n width: `${Math.max(widthPercent, 2)}%`,\n backgroundColor: getStepColor(index, paletteColors),\n }}\n />\n\n {/* Percentage Label on Bar */}\n <div\n className=\"dc:absolute dc:top-0 dc:left-0 dc:h-8 dc:flex dc:items-center dc:px-2 dc:pointer-events-none\"\n style={{ width: `${Math.max(widthPercent, 20)}%` }}\n >\n <span className=\"dc:text-xs dc:font-medium text-white dc:drop-shadow-sm\">\n {step.percentage?.toFixed(1) ?? widthPercent.toFixed(1)}%\n </span>\n </div>\n </div>\n\n {/* Conversion Rate from Previous */}\n <div className={`${metricsCount > 0 ? (metricsCount > 1 ? 'dc:w-36' : 'dc:w-28') : 'dc:w-16'} dc:flex-shrink-0 dc:text-left`}>\n {stepConversionRate !== null ? (\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {showConversion && <span>↓ {stepConversionRate.toFixed(1)}%</span>}\n {/* Time metrics (when enabled) */}\n {metricsCount > 0 && (\n <div className=\"text-dc-text-muted dc:mt-0.5 dc:space-y-0.5\">\n {timeMetricsLines.map((line, i) => (\n <div key={i}>⏱ {line}</div>\n ))}\n </div>\n )}\n </div>\n ) : (\n <div className=\"dc:text-xs text-dc-text-muted\">—</div>\n )}\n </div>\n </div>\n )\n })}\n </div>\n\n {/* Summary Footer */}\n {!displayConfig?.hideSummaryFooter && (\n <div className=\"dc:flex-shrink-0 dc:px-4 dc:py-2 dc:border-t border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:text-sm\">\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{funnelData.length}</span> steps\n </div>\n <div className=\"text-dc-text\">\n <span className=\"text-dc-text-muted\">Overall:</span>{' '}\n <span className=\"dc:font-medium\">\n {firstStepValue > 0\n ? `${((funnelData[funnelData.length - 1]?.value || 0) / firstStepValue * 100).toFixed(1)}%`\n : '0%'}\n </span>\n </div>\n <div className=\"text-dc-text-muted\">\n {funnelData[funnelData.length - 1]?.value.toLocaleString() || 0} / {firstStepValue.toLocaleString()} completed\n </div>\n </div>\n </div>\n )}\n </div>\n )\n})\n\nexport default FunnelChart\n"],"names":["FUNNEL_COLORS","getStepColor","index","colorPalette","colors","isFunnelData","data","first","getTimeMetricsLines","step","showAvg","showMedian","showP90","lines","formatDuration","FunnelChart","React","height","displayConfig","customStepLabels","isVertical","funnelStyle","showConversion","showAvgTime","showMedianTime","showP90Time","funnelData","useMemo","row","record","nameField","k","name","valueField","value","percentField","percentage","convRateField","conversionRate","firstStepValue","jsx","jsxs","paletteColors","CHART_COLORS","ResponsiveContainer","RechartsFunnelChart","Tooltip","Funnel","_","Cell","LabelList","v","heightPercent","prevStep","stepConversionRate","displayName","timeMetricsLines","metricsCount","line","i","widthPercent"],"mappings":"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"}
1
+ {"version":3,"file":"chart-funnel-DQ9cW6j9.js","sources":["../../../src/client/components/charts/FunnelChart.tsx"],"sourcesContent":["/**\n * FunnelChart Component\n *\n * Visualizes funnel data showing conversion rates between steps.\n * Uses horizontal bars with percentage widths to represent the funnel shape.\n * Works with data from useFunnelQuery hook which provides FunnelChartData.\n *\n * When displayConfig.showFunnelTimeMetrics is enabled, displays time-to-convert\n * metrics from server-side funnel execution (avg, median, P90 seconds).\n */\n\nimport React, { useMemo } from 'react'\nimport { FunnelChart as RechartsFunnelChart, Funnel, LabelList, Tooltip, Cell, ResponsiveContainer } from 'recharts'\nimport { CHART_COLORS } from '../../utils/chartConstants'\nimport type { ChartProps } from '../../types'\nimport type { FunnelChartData } from '../../types/funnel'\nimport { formatDuration } from '../../utils/funnelExecution'\n\n// Color gradient for funnel steps (darker to lighter)\nconst FUNNEL_COLORS = [\n '#3B82F6', // blue-500\n '#60A5FA', // blue-400\n '#93C5FD', // blue-300\n '#BFDBFE', // blue-200\n '#DBEAFE', // blue-100\n]\n\n/**\n * Get color for a funnel step\n */\nfunction getStepColor(index: number, colorPalette?: string[]): string {\n const colors = colorPalette || FUNNEL_COLORS\n return colors[index % colors.length]\n}\n\n/**\n * Check if data is funnel data format\n */\nfunction isFunnelData(data: unknown[]): data is FunnelChartData[] {\n if (!data || data.length === 0) return false\n const first = data[0]\n return (\n typeof first === 'object' &&\n first !== null &&\n 'name' in first &&\n 'value' in first &&\n 'percentage' in first\n )\n}\n\n/**\n * Render time metrics based on config options - returns array of lines for vertical stacking\n */\nfunction getTimeMetricsLines(\n step: FunnelChartData,\n showAvg: boolean,\n showMedian: boolean,\n showP90: boolean\n): string[] {\n const lines: string[] = []\n if (showAvg && step.avgSecondsToConvert != null) {\n lines.push(`Avg: ${formatDuration(step.avgSecondsToConvert)}`)\n }\n if (showMedian && step.medianSecondsToConvert != null) {\n lines.push(`Med: ${formatDuration(step.medianSecondsToConvert)}`)\n }\n if (showP90 && step.p90SecondsToConvert != null) {\n lines.push(`P90: ${formatDuration(step.p90SecondsToConvert)}`)\n }\n return lines\n}\n\n/**\n * FunnelChart Component\n *\n * Renders a funnel visualization from FunnelChartData array.\n * Shows each step as a horizontal bar with width proportional to count.\n * Displays conversion rates between steps.\n */\nconst FunnelChart = React.memo(function FunnelChart({\n data,\n height = '100%',\n colorPalette,\n displayConfig,\n}: ChartProps) {\n // Get display config options\n const customStepLabels = displayConfig?.funnelStepLabels\n const orientation = displayConfig?.funnelOrientation || 'horizontal'\n const isVertical = orientation === 'vertical'\n const funnelStyle = displayConfig?.funnelStyle ?? 'bars'\n const showConversion = displayConfig?.showFunnelConversion ?? true\n\n // Time metrics - individual toggles with backward compat for showFunnelTimeMetrics\n const showAvgTime = displayConfig?.showFunnelAvgTime ??\n (displayConfig?.showFunnelTimeMetrics ?? false) // backward compat\n const showMedianTime = displayConfig?.showFunnelMedianTime ?? false\n const showP90Time = displayConfig?.showFunnelP90Time ?? false\n\n // Transform data if needed\n const funnelData = useMemo<FunnelChartData[]>(() => {\n if (!data || data.length === 0) return []\n\n // If already funnel data format, use directly\n if (isFunnelData(data)) {\n return data\n }\n\n // Try to convert from raw query results\n // Look for common patterns: step/name, count/value, percentage\n return data.map((row, index) => {\n const record = row as Record<string, unknown>\n\n // Find name field\n const nameField = Object.keys(record).find(\n (k) => k.toLowerCase().includes('step') || k.toLowerCase().includes('name') || k === '__stepName'\n )\n const name = nameField ? String(record[nameField]) : `Step ${index + 1}`\n\n // Find value field\n const valueField = Object.keys(record).find(\n (k) => k.toLowerCase().includes('count') || k.toLowerCase().includes('value') || k === '__count'\n )\n const value = valueField ? Number(record[valueField]) || 0 : 0\n\n // Find percentage field\n const percentField = Object.keys(record).find(\n (k) => k.toLowerCase().includes('percent') || k === '__percentage'\n )\n const percentage = percentField ? Number(record[percentField]) || 0 : 0\n\n // Find conversion rate field\n const convRateField = Object.keys(record).find(\n (k) => k.toLowerCase().includes('conversion') || k === '__conversionRate'\n )\n const conversionRate = convRateField ? Number(record[convRateField]) || null : null\n\n return {\n name,\n value,\n percentage,\n conversionRate,\n stepIndex: index,\n }\n })\n }, [data])\n\n // Calculate first step value for percentage calculations\n const firstStepValue = funnelData[0]?.value || 0\n\n // Handle no data\n if (!data || data.length === 0 || funnelData.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No funnel data</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n Configure a funnel with at least 2 steps and a binding key\n </div>\n </div>\n </div>\n )\n }\n\n const paletteColors = colorPalette?.colors || CHART_COLORS\n\n // Render Recharts Funnel style (trapezoid shape)\n if (funnelStyle === 'funnel') {\n // Recharts FunnelChart layout: 'horizontal' = funnel flows left-to-right, 'vertical' = top-to-bottom (default)\n // Our config: 'horizontal' orientation = standard top-to-bottom funnel\n // 'vertical' orientation = left-to-right funnel\n const rechartsLayout: 'horizontal' | 'vertical' = isVertical ? 'horizontal' : 'vertical'\n\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:flex dc:flex-col\" style={{ height }}>\n <div className=\"dc:flex-1\">\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <RechartsFunnelChart layout={rechartsLayout} accessibilityLayer={false}>\n <Tooltip\n formatter={(value) => typeof value === 'number' ? value.toLocaleString() : String(value)}\n contentStyle={{\n backgroundColor: 'var(--dc-surface)',\n border: '1px solid var(--dc-border)',\n borderRadius: '4px',\n }}\n />\n <Funnel\n dataKey=\"value\"\n nameKey=\"name\"\n data={funnelData}\n isAnimationActive\n >\n {funnelData.map((_, index) => (\n <Cell key={`cell-${index}`} fill={getStepColor(index, paletteColors)} />\n ))}\n <LabelList\n position=\"right\"\n dataKey=\"name\"\n fill=\"var(--dc-text)\"\n style={{ fontSize: '12px' }}\n />\n <LabelList\n position=\"center\"\n dataKey=\"percentage\"\n formatter={(v) => typeof v === 'number' ? `${v.toFixed(1)}%` : String(v)}\n fill=\"#fff\"\n style={{ fontSize: '11px', fontWeight: 500 }}\n />\n </Funnel>\n </RechartsFunnelChart>\n </ResponsiveContainer>\n </div>\n {/* Summary Footer */}\n {!displayConfig?.hideSummaryFooter && (\n <div className=\"dc:flex-shrink-0 dc:px-4 dc:py-2 dc:border-t border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:text-sm\">\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{funnelData.length}</span> steps\n </div>\n <div className=\"text-dc-text\">\n <span className=\"text-dc-text-muted\">Overall:</span>{' '}\n <span className=\"dc:font-medium\">\n {firstStepValue > 0\n ? `${((funnelData[funnelData.length - 1]?.value || 0) / firstStepValue * 100).toFixed(1)}%`\n : '0%'}\n </span>\n </div>\n <div className=\"text-dc-text-muted\">\n {funnelData[funnelData.length - 1]?.value.toLocaleString() || 0} / {firstStepValue.toLocaleString()} completed\n </div>\n </div>\n </div>\n )}\n </div>\n )\n }\n\n // Render vertical orientation (bars grow from bottom to top, steps laid out horizontally)\n if (isVertical) {\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:flex dc:flex-col\" style={{ height }}>\n {/* Funnel Steps - Vertical Layout */}\n <div className=\"dc:flex-1 dc:flex dc:items-end dc:justify-center dc:gap-4 dc:px-4 dc:py-3 dc:overflow-hidden\">\n {funnelData.map((step, index) => {\n const heightPercent = firstStepValue > 0 ? (step.value / firstStepValue) * 100 : 0\n const prevStep = index > 0 ? funnelData[index - 1] : null\n const stepConversionRate = prevStep && prevStep.value > 0\n ? (step.value / prevStep.value) * 100\n : null\n\n // Use custom label if provided, otherwise fall back to step name\n const displayName = customStepLabels?.[index] || step.name\n\n const timeMetricsLines = getTimeMetricsLines(step, showAvgTime, showMedianTime, showP90Time)\n const metricsCount = timeMetricsLines.length\n\n return (\n <div key={step.name} className=\"dc:flex dc:flex-col dc:items-center dc:gap-2 dc:flex-1 dc:max-w-32 dc:h-full\">\n {/* Conversion Rate from Previous (top) */}\n <div className={`${metricsCount > 0 ? (metricsCount > 1 ? 'dc:min-h-16' : 'dc:min-h-10') : 'dc:h-5'} dc:flex-shrink-0 dc:text-center`}>\n {stepConversionRate !== null ? (\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {showConversion && <span>→ {stepConversionRate.toFixed(1)}%</span>}\n {/* Time metrics (when enabled) */}\n {metricsCount > 0 && (\n <div className=\"text-dc-text-muted dc:mt-0.5 dc:space-y-0.5\">\n {timeMetricsLines.map((line, i) => (\n <div key={i}>⏱ {line}</div>\n ))}\n </div>\n )}\n </div>\n ) : (\n <div className=\"dc:text-xs text-dc-text-muted\">—</div>\n )}\n </div>\n\n {/* Bar Container */}\n <div className=\"dc:flex-1 dc:w-full dc:relative dc:min-h-12\">\n {/* Background Track */}\n <div className=\"dc:absolute dc:inset-0 bg-dc-surface-secondary dc:rounded-sm\" />\n\n {/* Filled Bar (grows from bottom) */}\n <div\n className=\"dc:absolute dc:bottom-0 dc:left-0 dc:right-0 dc:rounded-sm dc:transition-all dc:duration-300\"\n style={{\n height: `${Math.max(heightPercent, 5)}%`,\n backgroundColor: getStepColor(index, paletteColors),\n }}\n />\n\n {/* Percentage Label on Bar */}\n <div\n className=\"dc:absolute dc:bottom-0 dc:left-0 dc:right-0 dc:flex dc:items-end dc:justify-center dc:pb-1 dc:pointer-events-none\"\n style={{ height: `${Math.max(heightPercent, 20)}%` }}\n >\n <span className=\"dc:text-xs dc:font-medium text-white dc:drop-shadow-sm\">\n {step.percentage?.toFixed(1) ?? heightPercent.toFixed(1)}%\n </span>\n </div>\n </div>\n\n {/* Step Label (bottom) */}\n <div className=\"dc:flex-shrink-0 dc:text-center\">\n <div className=\"dc:text-sm dc:font-medium text-dc-text dc:truncate\" title={displayName}>\n {displayName}\n </div>\n <div className=\"dc:text-xs text-dc-text-muted\">\n {step.value.toLocaleString()}\n </div>\n </div>\n </div>\n )\n })}\n </div>\n\n {/* Summary Footer */}\n {!displayConfig?.hideSummaryFooter && (\n <div className=\"dc:flex-shrink-0 dc:px-4 dc:py-2 dc:border-t border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:text-sm\">\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{funnelData.length}</span> steps\n </div>\n <div className=\"text-dc-text\">\n <span className=\"text-dc-text-muted\">Overall:</span>{' '}\n <span className=\"dc:font-medium\">\n {firstStepValue > 0\n ? `${((funnelData[funnelData.length - 1]?.value || 0) / firstStepValue * 100).toFixed(1)}%`\n : '0%'}\n </span>\n </div>\n <div className=\"text-dc-text-muted\">\n {funnelData[funnelData.length - 1]?.value.toLocaleString() || 0} / {firstStepValue.toLocaleString()} completed\n </div>\n </div>\n </div>\n )}\n </div>\n )\n }\n\n // Render horizontal orientation (default - bars grow left to right, steps stacked vertically)\n return (\n <div className=\"dc:relative dc:w-full dc:h-full dc:flex dc:flex-col\" style={{ height }}>\n {/* Funnel Steps - Horizontal Layout */}\n <div className=\"dc:flex-1 dc:flex dc:flex-col dc:justify-center dc:gap-2 dc:px-4 dc:py-3 dc:overflow-hidden\">\n {funnelData.map((step, index) => {\n const widthPercent = firstStepValue > 0 ? (step.value / firstStepValue) * 100 : 0\n const prevStep = index > 0 ? funnelData[index - 1] : null\n const stepConversionRate = prevStep && prevStep.value > 0\n ? (step.value / prevStep.value) * 100\n : null\n\n // Use custom label if provided, otherwise fall back to step name\n const displayName = customStepLabels?.[index] || step.name\n const timeMetricsLines = getTimeMetricsLines(step, showAvgTime, showMedianTime, showP90Time)\n const metricsCount = timeMetricsLines.length\n\n return (\n <div key={step.name} className=\"dc:flex dc:items-center dc:gap-3\">\n {/* Step Label */}\n <div className=\"dc:w-24 dc:flex-shrink-0 dc:text-right\">\n <div className=\"dc:text-sm dc:font-medium text-dc-text dc:truncate\" title={displayName}>\n {displayName}\n </div>\n <div className=\"dc:text-xs text-dc-text-muted\">\n {step.value.toLocaleString()}\n </div>\n </div>\n\n {/* Bar Container */}\n <div className=\"dc:flex-1 dc:relative\">\n {/* Background Track */}\n <div className=\"dc:w-full dc:h-8 bg-dc-surface-secondary dc:rounded-sm\" />\n\n {/* Filled Bar */}\n <div\n className=\"dc:absolute dc:top-0 dc:left-0 dc:h-8 dc:rounded-sm dc:transition-all dc:duration-300\"\n style={{\n width: `${Math.max(widthPercent, 2)}%`,\n backgroundColor: getStepColor(index, paletteColors),\n }}\n />\n\n {/* Percentage Label on Bar */}\n <div\n className=\"dc:absolute dc:top-0 dc:left-0 dc:h-8 dc:flex dc:items-center dc:px-2 dc:pointer-events-none\"\n style={{ width: `${Math.max(widthPercent, 20)}%` }}\n >\n <span className=\"dc:text-xs dc:font-medium text-white dc:drop-shadow-sm\">\n {step.percentage?.toFixed(1) ?? widthPercent.toFixed(1)}%\n </span>\n </div>\n </div>\n\n {/* Conversion Rate from Previous */}\n <div className={`${metricsCount > 0 ? (metricsCount > 1 ? 'dc:w-36' : 'dc:w-28') : 'dc:w-16'} dc:flex-shrink-0 dc:text-left`}>\n {stepConversionRate !== null ? (\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {showConversion && <span>↓ {stepConversionRate.toFixed(1)}%</span>}\n {/* Time metrics (when enabled) */}\n {metricsCount > 0 && (\n <div className=\"text-dc-text-muted dc:mt-0.5 dc:space-y-0.5\">\n {timeMetricsLines.map((line, i) => (\n <div key={i}>⏱ {line}</div>\n ))}\n </div>\n )}\n </div>\n ) : (\n <div className=\"dc:text-xs text-dc-text-muted\">—</div>\n )}\n </div>\n </div>\n )\n })}\n </div>\n\n {/* Summary Footer */}\n {!displayConfig?.hideSummaryFooter && (\n <div className=\"dc:flex-shrink-0 dc:px-4 dc:py-2 dc:border-t border-dc-border bg-dc-surface-secondary\">\n <div className=\"dc:flex dc:items-center dc:justify-between dc:text-sm\">\n <div className=\"text-dc-text-muted\">\n <span className=\"dc:font-medium\">{funnelData.length}</span> steps\n </div>\n <div className=\"text-dc-text\">\n <span className=\"text-dc-text-muted\">Overall:</span>{' '}\n <span className=\"dc:font-medium\">\n {firstStepValue > 0\n ? `${((funnelData[funnelData.length - 1]?.value || 0) / firstStepValue * 100).toFixed(1)}%`\n : '0%'}\n </span>\n </div>\n <div className=\"text-dc-text-muted\">\n {funnelData[funnelData.length - 1]?.value.toLocaleString() || 0} / {firstStepValue.toLocaleString()} completed\n </div>\n </div>\n </div>\n )}\n </div>\n )\n})\n\nexport default FunnelChart\n"],"names":["FUNNEL_COLORS","getStepColor","index","colorPalette","colors","isFunnelData","data","first","getTimeMetricsLines","step","showAvg","showMedian","showP90","lines","formatDuration","FunnelChart","React","height","displayConfig","customStepLabels","isVertical","funnelStyle","showConversion","showAvgTime","showMedianTime","showP90Time","funnelData","useMemo","row","record","nameField","k","name","valueField","value","percentField","percentage","convRateField","conversionRate","firstStepValue","jsx","jsxs","paletteColors","CHART_COLORS","ResponsiveContainer","RechartsFunnelChart","Tooltip","Funnel","_","Cell","LabelList","v","heightPercent","prevStep","stepConversionRate","displayName","timeMetricsLines","metricsCount","line","i","widthPercent"],"mappings":";;;;;AAmBA,MAAMA,IAAgB;AAAA,EACpB;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACF;AAKA,SAASC,EAAaC,GAAeC,GAAiC;AACpE,QAAMC,IAASD,KAAgBH;AAC/B,SAAOI,EAAOF,IAAQE,EAAO,MAAM;AACrC;AAKA,SAASC,EAAaC,GAA4C;AAChE,MAAI,CAACA,KAAQA,EAAK,WAAW,EAAG,QAAO;AACvC,QAAMC,IAAQD,EAAK,CAAC;AACpB,SACE,OAAOC,KAAU,YACjBA,MAAU,QACV,UAAUA,KACV,WAAWA,KACX,gBAAgBA;AAEpB;AAKA,SAASC,EACPC,GACAC,GACAC,GACAC,GACU;AACV,QAAMC,IAAkB,CAAA;AACxB,SAAIH,KAAWD,EAAK,uBAAuB,QACzCI,EAAM,KAAK,QAAQC,EAAeL,EAAK,mBAAmB,CAAC,EAAE,GAE3DE,KAAcF,EAAK,0BAA0B,QAC/CI,EAAM,KAAK,QAAQC,EAAeL,EAAK,sBAAsB,CAAC,EAAE,GAE9DG,KAAWH,EAAK,uBAAuB,QACzCI,EAAM,KAAK,QAAQC,EAAeL,EAAK,mBAAmB,CAAC,EAAE,GAExDI;AACT;AASA,MAAME,IAAcC,EAAM,KAAK,SAAqB;AAAA,EAClD,MAAAV;AAAA,EACA,QAAAW,IAAS;AAAA,EACT,cAAAd;AAAA,EACA,eAAAe;AACF,GAAe;AAEb,QAAMC,IAAmBD,GAAe,kBAElCE,KADcF,GAAe,qBAAqB,kBACrB,YAC7BG,IAAcH,GAAe,eAAe,QAC5CI,IAAiBJ,GAAe,wBAAwB,IAGxDK,IAAcL,GAAe,qBAChCA,GAAe,yBAAyB,IACrCM,IAAiBN,GAAe,wBAAwB,IACxDO,IAAcP,GAAe,qBAAqB,IAGlDQ,IAAaC,EAA2B,MACxC,CAACrB,KAAQA,EAAK,WAAW,IAAU,CAAA,IAGnCD,EAAaC,CAAI,IACZA,IAKFA,EAAK,IAAI,CAACsB,GAAK1B,MAAU;AAC9B,UAAM2B,IAASD,GAGTE,IAAY,OAAO,KAAKD,CAAM,EAAE;AAAA,MACpC,CAACE,MAAMA,EAAE,YAAA,EAAc,SAAS,MAAM,KAAKA,EAAE,YAAA,EAAc,SAAS,MAAM,KAAKA,MAAM;AAAA,IAAA,GAEjFC,IAAOF,IAAY,OAAOD,EAAOC,CAAS,CAAC,IAAI,QAAQ5B,IAAQ,CAAC,IAGhE+B,IAAa,OAAO,KAAKJ,CAAM,EAAE;AAAA,MACrC,CAACE,MAAMA,EAAE,YAAA,EAAc,SAAS,OAAO,KAAKA,EAAE,YAAA,EAAc,SAAS,OAAO,KAAKA,MAAM;AAAA,IAAA,GAEnFG,IAAQD,KAAa,OAAOJ,EAAOI,CAAU,CAAC,KAAK,GAGnDE,IAAe,OAAO,KAAKN,CAAM,EAAE;AAAA,MACvC,CAACE,MAAMA,EAAE,YAAA,EAAc,SAAS,SAAS,KAAKA,MAAM;AAAA,IAAA,GAEhDK,IAAaD,KAAe,OAAON,EAAOM,CAAY,CAAC,KAAK,GAG5DE,IAAgB,OAAO,KAAKR,CAAM,EAAE;AAAA,MACxC,CAACE,MAAMA,EAAE,YAAA,EAAc,SAAS,YAAY,KAAKA,MAAM;AAAA,IAAA,GAEnDO,IAAiBD,KAAgB,OAAOR,EAAOQ,CAAa,CAAC,KAAK;AAExE,WAAO;AAAA,MACL,MAAAL;AAAA,MACA,OAAAE;AAAA,MACA,YAAAE;AAAA,MACA,gBAAAE;AAAA,MACA,WAAWpC;AAAA,IAAA;AAAA,EAEf,CAAC,GACA,CAACI,CAAI,CAAC,GAGHiC,IAAiBb,EAAW,CAAC,GAAG,SAAS;AAG/C,MAAI,CAACpB,KAAQA,EAAK,WAAW,KAAKoB,EAAW,WAAW;AACtD,WACE,gBAAAc;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO,EAAE,QAAAvB,EAAA;AAAA,QAET,UAAA,gBAAAwB,EAAC,OAAA,EAAI,WAAU,kBACb,UAAA;AAAA,UAAA,gBAAAD,EAAC,OAAA,EAAI,WAAU,uCAAsC,UAAA,kBAAc;AAAA,UACnE,gBAAAA,EAAC,OAAA,EAAI,WAAU,qCAAoC,UAAA,6DAAA,CAEnD;AAAA,QAAA,EAAA,CACF;AAAA,MAAA;AAAA,IAAA;AAKN,QAAME,IAAgBvC,GAAc,UAAUwC;AAG9C,SAAItB,MAAgB,6BAOf,OAAA,EAAI,WAAU,uDAAsD,OAAO,EAAE,QAAAJ,KAC5E,UAAA;AAAA,IAAA,gBAAAuB,EAAC,OAAA,EAAI,WAAU,aACb,UAAA,gBAAAA,EAACI,KAAoB,OAAM,QAAO,QAAO,QACvC,UAAA,gBAAAH,EAACI,GAAA,EAAoB,QANqBzB,IAAa,eAAe,YAMzB,oBAAoB,IAC/D,UAAA;AAAA,MAAA,gBAAAoB;AAAA,QAACM;AAAA,QAAA;AAAA,UACC,WAAW,CAACZ,MAAU,OAAOA,KAAU,WAAWA,EAAM,eAAA,IAAmB,OAAOA,CAAK;AAAA,UACvF,cAAc;AAAA,YACZ,iBAAiB;AAAA,YACjB,QAAQ;AAAA,YACR,cAAc;AAAA,UAAA;AAAA,QAChB;AAAA,MAAA;AAAA,MAEF,gBAAAO;AAAA,QAACM;AAAA,QAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAQ;AAAA,UACR,MAAMrB;AAAA,UACN,mBAAiB;AAAA,UAEhB,UAAA;AAAA,YAAAA,EAAW,IAAI,CAACsB,GAAG9C,wBACjB+C,GAAA,EAA2B,MAAMhD,EAAaC,GAAOwC,CAAa,EAAA,GAAxD,QAAQxC,CAAK,EAA8C,CACvE;AAAA,YACD,gBAAAsC;AAAA,cAACU;AAAA,cAAA;AAAA,gBACC,UAAS;AAAA,gBACT,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,OAAO,EAAE,UAAU,OAAA;AAAA,cAAO;AAAA,YAAA;AAAA,YAE5B,gBAAAV;AAAA,cAACU;AAAA,cAAA;AAAA,gBACC,UAAS;AAAA,gBACT,SAAQ;AAAA,gBACR,WAAW,CAACC,MAAM,OAAOA,KAAM,WAAW,GAAGA,EAAE,QAAQ,CAAC,CAAC,MAAM,OAAOA,CAAC;AAAA,gBACvE,MAAK;AAAA,gBACL,OAAO,EAAE,UAAU,QAAQ,YAAY,IAAA;AAAA,cAAI;AAAA,YAAA;AAAA,UAC7C;AAAA,QAAA;AAAA,MAAA;AAAA,IACF,EAAA,CACF,GACF,GACF;AAAA,IAEC,CAACjC,GAAe,qBACf,gBAAAsB,EAAC,OAAA,EAAI,WAAU,yFACb,UAAA,gBAAAC,EAAC,OAAA,EAAI,WAAU,yDACb,UAAA;AAAA,MAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,sBACb,UAAA;AAAA,QAAA,gBAAAD,EAAC,QAAA,EAAK,WAAU,kBAAkB,UAAAd,EAAW,QAAO;AAAA,QAAO;AAAA,MAAA,GAC7D;AAAA,MACA,gBAAAe,EAAC,OAAA,EAAI,WAAU,gBACb,UAAA;AAAA,QAAA,gBAAAD,EAAC,QAAA,EAAK,WAAU,sBAAqB,UAAA,YAAQ;AAAA,QAAQ;AAAA,QACrD,gBAAAA,EAAC,UAAK,WAAU,kBACb,cAAiB,IACd,KAAKd,EAAWA,EAAW,SAAS,CAAC,GAAG,SAAS,KAAKa,IAAiB,KAAK,QAAQ,CAAC,CAAC,MACtF,KAAA,CACN;AAAA,MAAA,GACF;AAAA,MACA,gBAAAE,EAAC,OAAA,EAAI,WAAU,sBACZ,UAAA;AAAA,QAAAf,EAAWA,EAAW,SAAS,CAAC,GAAG,MAAM,oBAAoB;AAAA,QAAE;AAAA,QAAIa,EAAe,eAAA;AAAA,QAAiB;AAAA,MAAA,EAAA,CACtG;AAAA,IAAA,EAAA,CACF,EAAA,CACF;AAAA,EAAA,GAEJ,IAKAnB,sBAEC,OAAA,EAAI,WAAU,uDAAsD,OAAO,EAAE,QAAAH,KAE5E,UAAA;AAAA,IAAA,gBAAAuB,EAAC,SAAI,WAAU,gGACZ,YAAW,IAAI,CAAC/B,GAAMP,MAAU;AAC/B,YAAMkD,IAAgBb,IAAiB,IAAK9B,EAAK,QAAQ8B,IAAkB,MAAM,GAC3Ec,IAAWnD,IAAQ,IAAIwB,EAAWxB,IAAQ,CAAC,IAAI,MAC/CoD,IAAqBD,KAAYA,EAAS,QAAQ,IACnD5C,EAAK,QAAQ4C,EAAS,QAAS,MAChC,MAGEE,IAAcpC,IAAmBjB,CAAK,KAAKO,EAAK,MAEhD+C,IAAmBhD,EAAoBC,GAAMc,GAAaC,GAAgBC,CAAW,GACrFgC,IAAeD,EAAiB;AAEtC,aACE,gBAAAf,EAAC,OAAA,EAAoB,WAAU,gFAE7B,UAAA;AAAA,QAAA,gBAAAD,EAAC,SAAI,WAAW,GAAGiB,IAAe,IAAKA,IAAe,IAAI,gBAAgB,gBAAiB,QAAQ,oCAChG,UAAAH,MAAuB,OACtB,gBAAAb,EAAC,OAAA,EAAI,WAAU,qCACZ,UAAA;AAAA,UAAAnB,uBAAmB,QAAA,EAAK,UAAA;AAAA,YAAA;AAAA,YAAGgC,EAAmB,QAAQ,CAAC;AAAA,YAAE;AAAA,UAAA,GAAC;AAAA,UAE1DG,IAAe,KACd,gBAAAjB,EAAC,OAAA,EAAI,WAAU,+CACZ,UAAAgB,EAAiB,IAAI,CAACE,GAAMC,MAC3B,gBAAAlB,EAAC,OAAA,EAAY,UAAA;AAAA,YAAA;AAAA,YAAGiB;AAAA,UAAA,EAAA,GAANC,CAAW,CACtB,EAAA,CACH;AAAA,QAAA,GAEJ,IAEA,gBAAAnB,EAAC,OAAA,EAAI,WAAU,iCAAgC,eAAC,GAEpD;AAAA,QAGA,gBAAAC,EAAC,OAAA,EAAI,WAAU,+CAEb,UAAA;AAAA,UAAA,gBAAAD,EAAC,OAAA,EAAI,WAAU,+DAAA,CAA+D;AAAA,UAG9E,gBAAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,OAAO;AAAA,gBACL,QAAQ,GAAG,KAAK,IAAIY,GAAe,CAAC,CAAC;AAAA,gBACrC,iBAAiBnD,EAAaC,GAAOwC,CAAa;AAAA,cAAA;AAAA,YACpD;AAAA,UAAA;AAAA,UAIF,gBAAAF;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,OAAO,EAAE,QAAQ,GAAG,KAAK,IAAIY,GAAe,EAAE,CAAC,IAAA;AAAA,cAE/C,UAAA,gBAAAX,EAAC,QAAA,EAAK,WAAU,0DACb,UAAA;AAAA,gBAAAhC,EAAK,YAAY,QAAQ,CAAC,KAAK2C,EAAc,QAAQ,CAAC;AAAA,gBAAE;AAAA,cAAA,EAAA,CAC3D;AAAA,YAAA;AAAA,UAAA;AAAA,QACF,GACF;AAAA,QAGA,gBAAAX,EAAC,OAAA,EAAI,WAAU,mCACb,UAAA;AAAA,UAAA,gBAAAD,EAAC,OAAA,EAAI,WAAU,sDAAqD,OAAOe,GACxE,UAAAA,GACH;AAAA,4BACC,OAAA,EAAI,WAAU,iCACZ,UAAA9C,EAAK,MAAM,iBAAe,CAC7B;AAAA,QAAA,EAAA,CACF;AAAA,MAAA,EAAA,GArDQA,EAAK,IAsDf;AAAA,IAEJ,CAAC,EAAA,CACH;AAAA,IAGC,CAACS,GAAe,qBACf,gBAAAsB,EAAC,OAAA,EAAI,WAAU,yFACb,UAAA,gBAAAC,EAAC,OAAA,EAAI,WAAU,yDACb,UAAA;AAAA,MAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,sBACb,UAAA;AAAA,QAAA,gBAAAD,EAAC,QAAA,EAAK,WAAU,kBAAkB,UAAAd,EAAW,QAAO;AAAA,QAAO;AAAA,MAAA,GAC7D;AAAA,MACA,gBAAAe,EAAC,OAAA,EAAI,WAAU,gBACb,UAAA;AAAA,QAAA,gBAAAD,EAAC,QAAA,EAAK,WAAU,sBAAqB,UAAA,YAAQ;AAAA,QAAQ;AAAA,QACrD,gBAAAA,EAAC,UAAK,WAAU,kBACb,cAAiB,IACd,KAAKd,EAAWA,EAAW,SAAS,CAAC,GAAG,SAAS,KAAKa,IAAiB,KAAK,QAAQ,CAAC,CAAC,MACtF,KAAA,CACN;AAAA,MAAA,GACF;AAAA,MACA,gBAAAE,EAAC,OAAA,EAAI,WAAU,sBACZ,UAAA;AAAA,QAAAf,EAAWA,EAAW,SAAS,CAAC,GAAG,MAAM,oBAAoB;AAAA,QAAE;AAAA,QAAIa,EAAe,eAAA;AAAA,QAAiB;AAAA,MAAA,EAAA,CACtG;AAAA,IAAA,EAAA,CACF,EAAA,CACF;AAAA,EAAA,GAEJ,sBAMD,OAAA,EAAI,WAAU,uDAAsD,OAAO,EAAE,QAAAtB,KAE5E,UAAA;AAAA,IAAA,gBAAAuB,EAAC,SAAI,WAAU,+FACZ,YAAW,IAAI,CAAC/B,GAAMP,MAAU;AAC/B,YAAM0D,IAAerB,IAAiB,IAAK9B,EAAK,QAAQ8B,IAAkB,MAAM,GAC1Ec,IAAWnD,IAAQ,IAAIwB,EAAWxB,IAAQ,CAAC,IAAI,MAC/CoD,IAAqBD,KAAYA,EAAS,QAAQ,IACnD5C,EAAK,QAAQ4C,EAAS,QAAS,MAChC,MAGEE,IAAcpC,IAAmBjB,CAAK,KAAKO,EAAK,MAChD+C,IAAmBhD,EAAoBC,GAAMc,GAAaC,GAAgBC,CAAW,GACrFgC,IAAeD,EAAiB;AAEtC,aACE,gBAAAf,EAAC,OAAA,EAAoB,WAAU,oCAE7B,UAAA;AAAA,QAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,0CACb,UAAA;AAAA,UAAA,gBAAAD,EAAC,OAAA,EAAI,WAAU,sDAAqD,OAAOe,GACxE,UAAAA,GACH;AAAA,4BACC,OAAA,EAAI,WAAU,iCACZ,UAAA9C,EAAK,MAAM,iBAAe,CAC7B;AAAA,QAAA,GACF;AAAA,QAGA,gBAAAgC,EAAC,OAAA,EAAI,WAAU,yBAEb,UAAA;AAAA,UAAA,gBAAAD,EAAC,OAAA,EAAI,WAAU,yDAAA,CAAyD;AAAA,UAGxE,gBAAAA;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,OAAO;AAAA,gBACL,OAAO,GAAG,KAAK,IAAIoB,GAAc,CAAC,CAAC;AAAA,gBACnC,iBAAiB3D,EAAaC,GAAOwC,CAAa;AAAA,cAAA;AAAA,YACpD;AAAA,UAAA;AAAA,UAIF,gBAAAF;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,WAAU;AAAA,cACV,OAAO,EAAE,OAAO,GAAG,KAAK,IAAIoB,GAAc,EAAE,CAAC,IAAA;AAAA,cAE7C,UAAA,gBAAAnB,EAAC,QAAA,EAAK,WAAU,0DACb,UAAA;AAAA,gBAAAhC,EAAK,YAAY,QAAQ,CAAC,KAAKmD,EAAa,QAAQ,CAAC;AAAA,gBAAE;AAAA,cAAA,EAAA,CAC1D;AAAA,YAAA;AAAA,UAAA;AAAA,QACF,GACF;AAAA,0BAGC,OAAA,EAAI,WAAW,GAAGH,IAAe,IAAKA,IAAe,IAAI,YAAY,YAAa,SAAS,kCACzF,UAAAH,MAAuB,OACtB,gBAAAb,EAAC,OAAA,EAAI,WAAU,qCACZ,UAAA;AAAA,UAAAnB,uBAAmB,QAAA,EAAK,UAAA;AAAA,YAAA;AAAA,YAAGgC,EAAmB,QAAQ,CAAC;AAAA,YAAE;AAAA,UAAA,GAAC;AAAA,UAE1DG,IAAe,KACd,gBAAAjB,EAAC,OAAA,EAAI,WAAU,+CACZ,UAAAgB,EAAiB,IAAI,CAACE,GAAMC,MAC3B,gBAAAlB,EAAC,OAAA,EAAY,UAAA;AAAA,YAAA;AAAA,YAAGiB;AAAA,UAAA,EAAA,GAANC,CAAW,CACtB,EAAA,CACH;AAAA,QAAA,GAEJ,IAEA,gBAAAnB,EAAC,OAAA,EAAI,WAAU,iCAAgC,eAAC,EAAA,CAEpD;AAAA,MAAA,EAAA,GArDQ/B,EAAK,IAsDf;AAAA,IAEJ,CAAC,EAAA,CACH;AAAA,IAGC,CAACS,GAAe,qBACf,gBAAAsB,EAAC,OAAA,EAAI,WAAU,yFACb,UAAA,gBAAAC,EAAC,OAAA,EAAI,WAAU,yDACb,UAAA;AAAA,MAAA,gBAAAA,EAAC,OAAA,EAAI,WAAU,sBACb,UAAA;AAAA,QAAA,gBAAAD,EAAC,QAAA,EAAK,WAAU,kBAAkB,UAAAd,EAAW,QAAO;AAAA,QAAO;AAAA,MAAA,GAC7D;AAAA,MACA,gBAAAe,EAAC,OAAA,EAAI,WAAU,gBACb,UAAA;AAAA,QAAA,gBAAAD,EAAC,QAAA,EAAK,WAAU,sBAAqB,UAAA,YAAQ;AAAA,QAAQ;AAAA,QACrD,gBAAAA,EAAC,UAAK,WAAU,kBACb,cAAiB,IACd,KAAKd,EAAWA,EAAW,SAAS,CAAC,GAAG,SAAS,KAAKa,IAAiB,KAAK,QAAQ,CAAC,CAAC,MACtF,KAAA,CACN;AAAA,MAAA,GACF;AAAA,MACA,gBAAAE,EAAC,OAAA,EAAI,WAAU,sBACZ,UAAA;AAAA,QAAAf,EAAWA,EAAW,SAAS,CAAC,GAAG,MAAM,oBAAoB;AAAA,QAAE;AAAA,QAAIa,EAAe,eAAA;AAAA,QAAiB;AAAA,MAAA,EAAA,CACtG;AAAA,IAAA,EAAA,CACF,EAAA,CACF;AAAA,EAAA,GAEJ;AAEJ,CAAC;"}
@@ -1,7 +1,7 @@
1
1
  import { jsx as x, jsxs as b } from "react/jsx-runtime";
2
2
  import q, { useMemo as C } from "react";
3
3
  import { ResponsiveHeatMap as P } from "@nivo/heatmap";
4
- import { g as L, f as k, d as T } from "./charts-core-DAF1iT_h.js";
4
+ import { g as L, f as k, d as T } from "./charts-core-T8UglYyq.js";
5
5
  function z(i) {
6
6
  if (i.startsWith("#")) {
7
7
  const s = i.slice(1);
@@ -228,4 +228,4 @@ const G = q.memo(function({
228
228
  export {
229
229
  G as default
230
230
  };
231
- //# sourceMappingURL=chart-heat-map-DR3O3GXx.js.map
231
+ //# sourceMappingURL=chart-heat-map-CpIr4tbs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chart-heat-map-DR3O3GXx.js","sources":["../../../src/client/components/charts/HeatMapChart.tsx"],"sourcesContent":["/**\n * HeatMapChart Component\n *\n * Visualizes intensity across two categorical dimensions using a color matrix.\n * Uses @nivo/heatmap for rendering.\n *\n * The chart displays:\n * - Rows: Y-axis dimension values\n * - Columns: X-axis dimension values\n * - Cell color: Intensity based on measure value\n */\n\nimport React, { useMemo } from 'react'\nimport { ResponsiveHeatMap } from '@nivo/heatmap'\nimport { formatTimeValue, getFieldGranularity, formatAxisValue } from '../../utils/chartUtils'\nimport type { AxisFormatConfig } from '../../types'\nimport type { ChartProps } from '../../types'\n\n/**\n * Parse color string (hex or rgb) to RGB values\n */\nfunction parseColor(color: string): { r: number; g: number; b: number } | null {\n // Handle hex colors\n if (color.startsWith('#')) {\n const hex = color.slice(1)\n return {\n r: parseInt(hex.substring(0, 2), 16),\n g: parseInt(hex.substring(2, 4), 16),\n b: parseInt(hex.substring(4, 6), 16),\n }\n }\n\n // Handle rgb/rgba colors\n const rgbMatch = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/)\n if (rgbMatch) {\n return {\n r: parseInt(rgbMatch[1], 10),\n g: parseInt(rgbMatch[2], 10),\n b: parseInt(rgbMatch[3], 10),\n }\n }\n\n return null\n}\n\n/**\n * Calculate relative luminance of a color\n * Returns value between 0 (black) and 1 (white)\n */\nfunction getLuminance(color: string): number {\n const rgb = parseColor(color)\n if (!rgb) return 0.5 // Default to mid-gray if parsing fails\n\n const r = rgb.r / 255\n const g = rgb.g / 255\n const b = rgb.b / 255\n\n // Apply gamma correction\n const rLinear = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4)\n const gLinear = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4)\n const bLinear = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4)\n\n // Calculate luminance using WCAG formula\n return 0.2126 * rLinear + 0.7152 * gLinear + 0.0722 * bLinear\n}\n\n/**\n * Get contrasting text color (white or dark) based on background color\n */\nfunction getContrastingTextColor(bgColor: string): string {\n const luminance = getLuminance(bgColor)\n // Use white text on dark backgrounds, dark text on light backgrounds\n return luminance < 0.4 ? '#ffffff' : '#1f2937'\n}\n\n/**\n * Maximum dimensions for heatmap to prevent browser lockup\n * 50x50 = 2500 cells max\n */\nconst MAX_HEATMAP_ROWS = 50\nconst MAX_HEATMAP_COLS = 50\n\n/**\n * Nivo heatmap data format\n */\ninterface HeatMapDatum {\n x: string\n y: number | null\n}\n\ninterface HeatMapSerie {\n id: string\n data: HeatMapDatum[]\n}\n\n/**\n * Result of heatmap transformation including truncation info\n */\ninterface HeatMapTransformResult {\n data: HeatMapSerie[]\n truncated: boolean\n originalRows: number\n originalCols: number\n}\n\n/**\n * Transform drizzle-cube flat query results to nivo heatmap format\n *\n * Input (drizzle-cube):\n * [\n * { \"Region.name\": \"East\", \"Product.category\": \"Electronics\", \"Sales.total\": 1500 },\n * { \"Region.name\": \"East\", \"Product.category\": \"Clothing\", \"Sales.total\": 800 },\n * ]\n *\n * Output (nivo format):\n * [\n * { id: \"East\", data: [{ x: \"Electronics\", y: 1500 }, { x: \"Clothing\", y: 800 }] }\n * ]\n *\n * Data is truncated to MAX_HEATMAP_ROWS x MAX_HEATMAP_COLS to prevent browser lockup\n */\nfunction transformToHeatMapFormat(\n data: Record<string, unknown>[],\n xAxisField: string | undefined,\n yAxisField: string | undefined,\n valueField: string | undefined,\n xGranularity?: string,\n yGranularity?: string\n): HeatMapTransformResult {\n if (!xAxisField || !yAxisField || !valueField) {\n return { data: [], truncated: false, originalRows: 0, originalCols: 0 }\n }\n\n // Group data by Y-axis dimension\n const groupedByY = new Map<string, Map<string, number>>()\n const allXValues = new Set<string>()\n // Keep original values for sorting timestamps correctly\n const xValueOriginals = new Map<string, unknown>()\n\n for (const row of data) {\n const rawYValue = row[yAxisField]\n const rawXValue = row[xAxisField]\n\n // Format time values based on granularity\n const yValue = formatTimeValue(rawYValue, yGranularity) || String(rawYValue ?? '(empty)')\n const xValue = formatTimeValue(rawXValue, xGranularity) || String(rawXValue ?? '(empty)')\n const value = Number(row[valueField]) || 0\n\n allXValues.add(xValue)\n // Store original for sorting\n if (!xValueOriginals.has(xValue)) {\n xValueOriginals.set(xValue, rawXValue)\n }\n\n if (!groupedByY.has(yValue)) {\n groupedByY.set(yValue, new Map())\n }\n groupedByY.get(yValue)!.set(xValue, value)\n }\n\n // Sort X values - try to sort by original timestamp if available\n const xValueArray = Array.from(allXValues).sort((a, b) => {\n const origA = xValueOriginals.get(a)\n const origB = xValueOriginals.get(b)\n // If both are date strings, sort chronologically\n if (typeof origA === 'string' && typeof origB === 'string' &&\n origA.match(/^\\d{4}-\\d{2}-\\d{2}/) && origB.match(/^\\d{4}-\\d{2}-\\d{2}/)) {\n return origA.localeCompare(origB)\n }\n // Otherwise sort alphabetically by formatted value\n return a.localeCompare(b)\n })\n\n // Track original dimensions for truncation warning\n const originalRows = groupedByY.size\n const originalCols = xValueArray.length\n const truncated = originalRows > MAX_HEATMAP_ROWS || originalCols > MAX_HEATMAP_COLS\n\n // Truncate X values if needed\n const limitedXValues = xValueArray.slice(0, MAX_HEATMAP_COLS)\n\n // Build result with truncation\n const result: HeatMapSerie[] = []\n let rowCount = 0\n for (const [yValue, xMap] of groupedByY) {\n if (rowCount >= MAX_HEATMAP_ROWS) break\n result.push({\n id: yValue,\n data: limitedXValues.map((x) => ({\n x,\n y: xMap.get(x) ?? null,\n })),\n })\n rowCount++\n }\n\n return { data: result, truncated, originalRows, originalCols }\n}\n\n/**\n * HeatMapChart Component\n *\n * Renders a heatmap visualization from query results.\n * Shows intensity patterns across two categorical dimensions.\n */\nconst HeatMapChart = React.memo(function HeatMapChart({\n data,\n height = '100%',\n chartConfig,\n colorPalette,\n displayConfig,\n queryObject,\n}: ChartProps) {\n // Get display config options\n const displayConfigAny = displayConfig as Record<string, unknown> | undefined\n const showLabels = (displayConfigAny?.showLabels as boolean) ?? false\n const cellShape = (displayConfigAny?.cellShape as 'rect' | 'circle') ?? 'rect'\n const showLegend = (displayConfigAny?.showLegend as boolean) ?? true\n const xAxisFormat = displayConfigAny?.xAxisFormat as AxisFormatConfig | undefined\n const yAxisFormat = displayConfigAny?.yAxisFormat as AxisFormatConfig | undefined\n const valueFormat = displayConfigAny?.valueFormat as AxisFormatConfig | undefined\n\n // Extract field names from chartConfig (handle both array and string formats)\n const xAxisField = chartConfig?.xAxis\n ? (Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis)\n : undefined\n const yAxisField = chartConfig?.yAxis\n ? (Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis[0] : chartConfig.yAxis)\n : undefined\n const valueField = chartConfig?.valueField\n ? (Array.isArray(chartConfig.valueField) ? chartConfig.valueField[0] : chartConfig.valueField)\n : undefined\n\n // Get granularity for time dimensions (only if field is defined)\n const xGranularity = xAxisField ? getFieldGranularity(queryObject, xAxisField) : undefined\n const yGranularity = yAxisField ? getFieldGranularity(queryObject, yAxisField) : undefined\n\n // Transform data to nivo format\n const { data: heatmapData, truncated, originalRows, originalCols } = useMemo(() => {\n if (!data || data.length === 0) {\n return { data: [], truncated: false, originalRows: 0, originalCols: 0 }\n }\n return transformToHeatMapFormat(\n data as Record<string, unknown>[],\n xAxisField,\n yAxisField,\n valueField,\n xGranularity,\n yGranularity\n )\n }, [data, xAxisField, yAxisField, valueField, xGranularity, yGranularity])\n\n // Handle no data or missing config\n if (!data || data.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n Run a query to see heatmap visualization\n </div>\n </div>\n </div>\n )\n }\n\n if (!xAxisField || !yAxisField || !valueField) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration required</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {!xAxisField && 'X-axis dimension required. '}\n {!yAxisField && 'Y-axis dimension required. '}\n {!valueField && 'Value measure required.'}\n </div>\n </div>\n </div>\n )\n }\n\n if (heatmapData.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data to display</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n The query returned no results for the heatmap\n </div>\n </div>\n </div>\n )\n }\n\n // Use gradient colors from palette, or default sequential blue gradient\n // Sequential single-hue gradients are ideal for heatmaps showing magnitude/intensity\n const colors = colorPalette?.gradient || [\n '#eff3ff', // lightest blue\n '#c6dbef',\n '#9ecae1',\n '#6baed6',\n '#3182bd',\n '#08519c', // darkest blue\n ]\n\n return (\n <div className=\"dc:relative dc:w-full dc:h-full\" style={{ height }}>\n {truncated && (\n <div className=\"dc:absolute dc:top-0 dc:left-0 dc:right-0 dc:z-10 dc:px-3 dc:py-1.5 dc:text-xs bg-dc-warning-bg text-dc-warning dc:border-b border-dc-border\">\n Data truncated to {MAX_HEATMAP_ROWS}x{MAX_HEATMAP_COLS} cells (original: {originalRows}x{originalCols}). Add filters to reduce dimensions.\n </div>\n )}\n <ResponsiveHeatMap\n data={heatmapData}\n margin={{ top: truncated ? 40 : 20, right: 20, bottom: 120, left: 120 }}\n valueFormat={valueFormat ? (v) => formatAxisValue(v, valueFormat) : '>-.2s'}\n axisTop={null}\n axisRight={null}\n axisBottom={{\n tickSize: 5,\n tickPadding: 5,\n tickRotation: -45,\n legend: xAxisFormat?.label || xAxisField?.split('.').pop() || 'X Axis',\n legendPosition: 'middle',\n legendOffset: 70,\n format: xAxisFormat\n ? (v) => {\n const num = parseFloat(String(v))\n return isNaN(num) ? String(v) : formatAxisValue(num, xAxisFormat)\n }\n : undefined,\n }}\n axisLeft={{\n tickSize: 5,\n tickPadding: 5,\n tickRotation: 0,\n legend: yAxisFormat?.label || yAxisField?.split('.').pop() || 'Y Axis',\n legendPosition: 'middle',\n legendOffset: -80,\n format: yAxisFormat\n ? (v) => {\n const num = parseFloat(String(v))\n return isNaN(num) ? String(v) : formatAxisValue(num, yAxisFormat)\n }\n : undefined,\n }}\n colors={\n colors.length >= 2\n // Sequential scale needs [minColor, maxColor] tuple\n // Use first color for low values, last color for high values\n ? { type: 'sequential', colors: [colors[0], colors[colors.length - 1]] as [string, string] }\n : { type: 'sequential', scheme: 'greens' } // Fallback to built-in scheme\n }\n emptyColor=\"var(--dc-surface-tertiary)\"\n cellComponent={cellShape === 'circle' ? 'circle' : 'rect'}\n enableLabels={showLabels}\n labelTextColor={({ color }) => getContrastingTextColor(color)}\n legends={\n showLegend\n ? [\n {\n anchor: 'bottom',\n translateX: 0,\n translateY: 95,\n length: 400,\n thickness: 8,\n direction: 'row',\n tickPosition: 'after',\n tickSize: 3,\n tickSpacing: 4,\n tickOverlap: false,\n title: valueField?.split('.').pop() || 'Value',\n titleAlign: 'start',\n titleOffset: 4,\n },\n ]\n : []\n }\n annotations={[]}\n theme={{\n text: {\n fill: 'var(--dc-text)',\n },\n axis: {\n legend: {\n text: {\n fill: 'var(--dc-text)',\n },\n },\n ticks: {\n text: {\n fill: 'var(--dc-text-secondary)',\n },\n },\n },\n legends: {\n text: {\n fill: 'var(--dc-text-secondary)',\n },\n title: {\n text: {\n fill: 'var(--dc-text)',\n },\n },\n },\n tooltip: {\n container: {\n background: 'var(--dc-surface)',\n color: 'var(--dc-text)',\n borderRadius: '4px',\n boxShadow: '0 1px 2px rgba(0, 0, 0, 0.25)',\n },\n },\n }}\n />\n </div>\n )\n})\n\nexport default HeatMapChart\n"],"names":["parseColor","color","hex","rgbMatch","getLuminance","rgb","r","g","b","rLinear","gLinear","bLinear","getContrastingTextColor","bgColor","MAX_HEATMAP_ROWS","MAX_HEATMAP_COLS","transformToHeatMapFormat","data","xAxisField","yAxisField","valueField","xGranularity","yGranularity","groupedByY","allXValues","xValueOriginals","row","rawYValue","rawXValue","yValue","formatTimeValue","xValue","value","xValueArray","origA","origB","originalRows","originalCols","truncated","limitedXValues","result","rowCount","xMap","x","HeatMapChart","React","height","chartConfig","colorPalette","displayConfig","queryObject","displayConfigAny","showLabels","cellShape","showLegend","xAxisFormat","yAxisFormat","valueFormat","getFieldGranularity","heatmapData","useMemo","jsx","jsxs","colors","ResponsiveHeatMap","v","formatAxisValue","num"],"mappings":"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"}
1
+ {"version":3,"file":"chart-heat-map-CpIr4tbs.js","sources":["../../../src/client/components/charts/HeatMapChart.tsx"],"sourcesContent":["/**\n * HeatMapChart Component\n *\n * Visualizes intensity across two categorical dimensions using a color matrix.\n * Uses @nivo/heatmap for rendering.\n *\n * The chart displays:\n * - Rows: Y-axis dimension values\n * - Columns: X-axis dimension values\n * - Cell color: Intensity based on measure value\n */\n\nimport React, { useMemo } from 'react'\nimport { ResponsiveHeatMap } from '@nivo/heatmap'\nimport { formatTimeValue, getFieldGranularity, formatAxisValue } from '../../utils/chartUtils'\nimport type { AxisFormatConfig } from '../../types'\nimport type { ChartProps } from '../../types'\n\n/**\n * Parse color string (hex or rgb) to RGB values\n */\nfunction parseColor(color: string): { r: number; g: number; b: number } | null {\n // Handle hex colors\n if (color.startsWith('#')) {\n const hex = color.slice(1)\n return {\n r: parseInt(hex.substring(0, 2), 16),\n g: parseInt(hex.substring(2, 4), 16),\n b: parseInt(hex.substring(4, 6), 16),\n }\n }\n\n // Handle rgb/rgba colors\n const rgbMatch = color.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/)\n if (rgbMatch) {\n return {\n r: parseInt(rgbMatch[1], 10),\n g: parseInt(rgbMatch[2], 10),\n b: parseInt(rgbMatch[3], 10),\n }\n }\n\n return null\n}\n\n/**\n * Calculate relative luminance of a color\n * Returns value between 0 (black) and 1 (white)\n */\nfunction getLuminance(color: string): number {\n const rgb = parseColor(color)\n if (!rgb) return 0.5 // Default to mid-gray if parsing fails\n\n const r = rgb.r / 255\n const g = rgb.g / 255\n const b = rgb.b / 255\n\n // Apply gamma correction\n const rLinear = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4)\n const gLinear = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4)\n const bLinear = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4)\n\n // Calculate luminance using WCAG formula\n return 0.2126 * rLinear + 0.7152 * gLinear + 0.0722 * bLinear\n}\n\n/**\n * Get contrasting text color (white or dark) based on background color\n */\nfunction getContrastingTextColor(bgColor: string): string {\n const luminance = getLuminance(bgColor)\n // Use white text on dark backgrounds, dark text on light backgrounds\n return luminance < 0.4 ? '#ffffff' : '#1f2937'\n}\n\n/**\n * Maximum dimensions for heatmap to prevent browser lockup\n * 50x50 = 2500 cells max\n */\nconst MAX_HEATMAP_ROWS = 50\nconst MAX_HEATMAP_COLS = 50\n\n/**\n * Nivo heatmap data format\n */\ninterface HeatMapDatum {\n x: string\n y: number | null\n}\n\ninterface HeatMapSerie {\n id: string\n data: HeatMapDatum[]\n}\n\n/**\n * Result of heatmap transformation including truncation info\n */\ninterface HeatMapTransformResult {\n data: HeatMapSerie[]\n truncated: boolean\n originalRows: number\n originalCols: number\n}\n\n/**\n * Transform drizzle-cube flat query results to nivo heatmap format\n *\n * Input (drizzle-cube):\n * [\n * { \"Region.name\": \"East\", \"Product.category\": \"Electronics\", \"Sales.total\": 1500 },\n * { \"Region.name\": \"East\", \"Product.category\": \"Clothing\", \"Sales.total\": 800 },\n * ]\n *\n * Output (nivo format):\n * [\n * { id: \"East\", data: [{ x: \"Electronics\", y: 1500 }, { x: \"Clothing\", y: 800 }] }\n * ]\n *\n * Data is truncated to MAX_HEATMAP_ROWS x MAX_HEATMAP_COLS to prevent browser lockup\n */\nfunction transformToHeatMapFormat(\n data: Record<string, unknown>[],\n xAxisField: string | undefined,\n yAxisField: string | undefined,\n valueField: string | undefined,\n xGranularity?: string,\n yGranularity?: string\n): HeatMapTransformResult {\n if (!xAxisField || !yAxisField || !valueField) {\n return { data: [], truncated: false, originalRows: 0, originalCols: 0 }\n }\n\n // Group data by Y-axis dimension\n const groupedByY = new Map<string, Map<string, number>>()\n const allXValues = new Set<string>()\n // Keep original values for sorting timestamps correctly\n const xValueOriginals = new Map<string, unknown>()\n\n for (const row of data) {\n const rawYValue = row[yAxisField]\n const rawXValue = row[xAxisField]\n\n // Format time values based on granularity\n const yValue = formatTimeValue(rawYValue, yGranularity) || String(rawYValue ?? '(empty)')\n const xValue = formatTimeValue(rawXValue, xGranularity) || String(rawXValue ?? '(empty)')\n const value = Number(row[valueField]) || 0\n\n allXValues.add(xValue)\n // Store original for sorting\n if (!xValueOriginals.has(xValue)) {\n xValueOriginals.set(xValue, rawXValue)\n }\n\n if (!groupedByY.has(yValue)) {\n groupedByY.set(yValue, new Map())\n }\n groupedByY.get(yValue)!.set(xValue, value)\n }\n\n // Sort X values - try to sort by original timestamp if available\n const xValueArray = Array.from(allXValues).sort((a, b) => {\n const origA = xValueOriginals.get(a)\n const origB = xValueOriginals.get(b)\n // If both are date strings, sort chronologically\n if (typeof origA === 'string' && typeof origB === 'string' &&\n origA.match(/^\\d{4}-\\d{2}-\\d{2}/) && origB.match(/^\\d{4}-\\d{2}-\\d{2}/)) {\n return origA.localeCompare(origB)\n }\n // Otherwise sort alphabetically by formatted value\n return a.localeCompare(b)\n })\n\n // Track original dimensions for truncation warning\n const originalRows = groupedByY.size\n const originalCols = xValueArray.length\n const truncated = originalRows > MAX_HEATMAP_ROWS || originalCols > MAX_HEATMAP_COLS\n\n // Truncate X values if needed\n const limitedXValues = xValueArray.slice(0, MAX_HEATMAP_COLS)\n\n // Build result with truncation\n const result: HeatMapSerie[] = []\n let rowCount = 0\n for (const [yValue, xMap] of groupedByY) {\n if (rowCount >= MAX_HEATMAP_ROWS) break\n result.push({\n id: yValue,\n data: limitedXValues.map((x) => ({\n x,\n y: xMap.get(x) ?? null,\n })),\n })\n rowCount++\n }\n\n return { data: result, truncated, originalRows, originalCols }\n}\n\n/**\n * HeatMapChart Component\n *\n * Renders a heatmap visualization from query results.\n * Shows intensity patterns across two categorical dimensions.\n */\nconst HeatMapChart = React.memo(function HeatMapChart({\n data,\n height = '100%',\n chartConfig,\n colorPalette,\n displayConfig,\n queryObject,\n}: ChartProps) {\n // Get display config options\n const displayConfigAny = displayConfig as Record<string, unknown> | undefined\n const showLabels = (displayConfigAny?.showLabels as boolean) ?? false\n const cellShape = (displayConfigAny?.cellShape as 'rect' | 'circle') ?? 'rect'\n const showLegend = (displayConfigAny?.showLegend as boolean) ?? true\n const xAxisFormat = displayConfigAny?.xAxisFormat as AxisFormatConfig | undefined\n const yAxisFormat = displayConfigAny?.yAxisFormat as AxisFormatConfig | undefined\n const valueFormat = displayConfigAny?.valueFormat as AxisFormatConfig | undefined\n\n // Extract field names from chartConfig (handle both array and string formats)\n const xAxisField = chartConfig?.xAxis\n ? (Array.isArray(chartConfig.xAxis) ? chartConfig.xAxis[0] : chartConfig.xAxis)\n : undefined\n const yAxisField = chartConfig?.yAxis\n ? (Array.isArray(chartConfig.yAxis) ? chartConfig.yAxis[0] : chartConfig.yAxis)\n : undefined\n const valueField = chartConfig?.valueField\n ? (Array.isArray(chartConfig.valueField) ? chartConfig.valueField[0] : chartConfig.valueField)\n : undefined\n\n // Get granularity for time dimensions (only if field is defined)\n const xGranularity = xAxisField ? getFieldGranularity(queryObject, xAxisField) : undefined\n const yGranularity = yAxisField ? getFieldGranularity(queryObject, yAxisField) : undefined\n\n // Transform data to nivo format\n const { data: heatmapData, truncated, originalRows, originalCols } = useMemo(() => {\n if (!data || data.length === 0) {\n return { data: [], truncated: false, originalRows: 0, originalCols: 0 }\n }\n return transformToHeatMapFormat(\n data as Record<string, unknown>[],\n xAxisField,\n yAxisField,\n valueField,\n xGranularity,\n yGranularity\n )\n }, [data, xAxisField, yAxisField, valueField, xGranularity, yGranularity])\n\n // Handle no data or missing config\n if (!data || data.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n Run a query to see heatmap visualization\n </div>\n </div>\n </div>\n )\n }\n\n if (!xAxisField || !yAxisField || !valueField) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration required</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n {!xAxisField && 'X-axis dimension required. '}\n {!yAxisField && 'Y-axis dimension required. '}\n {!valueField && 'Value measure required.'}\n </div>\n </div>\n </div>\n )\n }\n\n if (heatmapData.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full text-dc-text-muted\"\n style={{ height }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data to display</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n The query returned no results for the heatmap\n </div>\n </div>\n </div>\n )\n }\n\n // Use gradient colors from palette, or default sequential blue gradient\n // Sequential single-hue gradients are ideal for heatmaps showing magnitude/intensity\n const colors = colorPalette?.gradient || [\n '#eff3ff', // lightest blue\n '#c6dbef',\n '#9ecae1',\n '#6baed6',\n '#3182bd',\n '#08519c', // darkest blue\n ]\n\n return (\n <div className=\"dc:relative dc:w-full dc:h-full\" style={{ height }}>\n {truncated && (\n <div className=\"dc:absolute dc:top-0 dc:left-0 dc:right-0 dc:z-10 dc:px-3 dc:py-1.5 dc:text-xs bg-dc-warning-bg text-dc-warning dc:border-b border-dc-border\">\n Data truncated to {MAX_HEATMAP_ROWS}x{MAX_HEATMAP_COLS} cells (original: {originalRows}x{originalCols}). Add filters to reduce dimensions.\n </div>\n )}\n <ResponsiveHeatMap\n data={heatmapData}\n margin={{ top: truncated ? 40 : 20, right: 20, bottom: 120, left: 120 }}\n valueFormat={valueFormat ? (v) => formatAxisValue(v, valueFormat) : '>-.2s'}\n axisTop={null}\n axisRight={null}\n axisBottom={{\n tickSize: 5,\n tickPadding: 5,\n tickRotation: -45,\n legend: xAxisFormat?.label || xAxisField?.split('.').pop() || 'X Axis',\n legendPosition: 'middle',\n legendOffset: 70,\n format: xAxisFormat\n ? (v) => {\n const num = parseFloat(String(v))\n return isNaN(num) ? String(v) : formatAxisValue(num, xAxisFormat)\n }\n : undefined,\n }}\n axisLeft={{\n tickSize: 5,\n tickPadding: 5,\n tickRotation: 0,\n legend: yAxisFormat?.label || yAxisField?.split('.').pop() || 'Y Axis',\n legendPosition: 'middle',\n legendOffset: -80,\n format: yAxisFormat\n ? (v) => {\n const num = parseFloat(String(v))\n return isNaN(num) ? String(v) : formatAxisValue(num, yAxisFormat)\n }\n : undefined,\n }}\n colors={\n colors.length >= 2\n // Sequential scale needs [minColor, maxColor] tuple\n // Use first color for low values, last color for high values\n ? { type: 'sequential', colors: [colors[0], colors[colors.length - 1]] as [string, string] }\n : { type: 'sequential', scheme: 'greens' } // Fallback to built-in scheme\n }\n emptyColor=\"var(--dc-surface-tertiary)\"\n cellComponent={cellShape === 'circle' ? 'circle' : 'rect'}\n enableLabels={showLabels}\n labelTextColor={({ color }) => getContrastingTextColor(color)}\n legends={\n showLegend\n ? [\n {\n anchor: 'bottom',\n translateX: 0,\n translateY: 95,\n length: 400,\n thickness: 8,\n direction: 'row',\n tickPosition: 'after',\n tickSize: 3,\n tickSpacing: 4,\n tickOverlap: false,\n title: valueField?.split('.').pop() || 'Value',\n titleAlign: 'start',\n titleOffset: 4,\n },\n ]\n : []\n }\n annotations={[]}\n theme={{\n text: {\n fill: 'var(--dc-text)',\n },\n axis: {\n legend: {\n text: {\n fill: 'var(--dc-text)',\n },\n },\n ticks: {\n text: {\n fill: 'var(--dc-text-secondary)',\n },\n },\n },\n legends: {\n text: {\n fill: 'var(--dc-text-secondary)',\n },\n title: {\n text: {\n fill: 'var(--dc-text)',\n },\n },\n },\n tooltip: {\n container: {\n background: 'var(--dc-surface)',\n color: 'var(--dc-text)',\n borderRadius: '4px',\n boxShadow: '0 1px 2px rgba(0, 0, 0, 0.25)',\n },\n },\n }}\n />\n </div>\n )\n})\n\nexport default HeatMapChart\n"],"names":["parseColor","color","hex","rgbMatch","getLuminance","rgb","r","g","b","rLinear","gLinear","bLinear","getContrastingTextColor","bgColor","MAX_HEATMAP_ROWS","MAX_HEATMAP_COLS","transformToHeatMapFormat","data","xAxisField","yAxisField","valueField","xGranularity","yGranularity","groupedByY","allXValues","xValueOriginals","row","rawYValue","rawXValue","yValue","formatTimeValue","xValue","value","xValueArray","origA","origB","originalRows","originalCols","truncated","limitedXValues","result","rowCount","xMap","x","HeatMapChart","React","height","chartConfig","colorPalette","displayConfig","queryObject","displayConfigAny","showLabels","cellShape","showLegend","xAxisFormat","yAxisFormat","valueFormat","getFieldGranularity","heatmapData","useMemo","jsx","jsxs","colors","ResponsiveHeatMap","v","formatAxisValue","num"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { jsx as t, jsxs as s } from "react/jsx-runtime";
2
2
  import ce, { useState as O, useRef as U, useEffect as se } from "react";
3
3
  import { I as de, d as ne } from "./icons-DRreo6m8.js";
4
- import { u as ie } from "./charts-core-DAF1iT_h.js";
5
- import { f as re } from "./chart-kpi-number-B3yvgbr-.js";
4
+ import { u as ie } from "./charts-core-T8UglYyq.js";
5
+ import { f as re } from "./chart-kpi-number-D62PzKZ1.js";
6
6
  function oe({
7
7
  values: w,
8
8
  lastValue: p,
@@ -348,4 +348,4 @@ const he = ce.memo(function({
348
348
  export {
349
349
  he as default
350
350
  };
351
- //# sourceMappingURL=chart-kpi-delta-Bp9Yrgyc.js.map
351
+ //# sourceMappingURL=chart-kpi-delta-DPcSazD3.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chart-kpi-delta-Bp9Yrgyc.js","sources":["../../../src/client/components/charts/KpiDelta.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect } from \"react\";\nimport { Icon } from \"@iconify/react\";\nimport infoCircleIcon from \"@iconify-icons/tabler/info-circle\";\nimport { useCubeFieldLabel } from \"../../hooks/useCubeFieldLabel\";\nimport { filterIncompletePeriod } from \"../../utils/periodUtils\";\nimport type { ChartProps } from \"../../types\";\n\ninterface VarianceHistogramProps {\n values: number[];\n lastValue: number;\n positiveColor: string;\n negativeColor: string;\n formatValue: (value: number) => string;\n width: number;\n height: number;\n}\n\nfunction VarianceHistogram({\n values,\n lastValue,\n positiveColor,\n negativeColor,\n formatValue,\n width,\n height,\n}: VarianceHistogramProps) {\n // Limit to most recent N values to fit in the histogram\n // Calculate max bars based on width (minimum 8px per bar including gap)\n const maxBars = Math.max(10, Math.floor(width / 10));\n const limitedValues =\n values.length > maxBars\n ? values.slice(-maxBars) // Take the most recent values\n : values;\n\n // Calculate variance (difference) from current/last value for each point\n const variances = limitedValues.map((value) => value - lastValue);\n\n // Find min/max variance for scaling (include 0 as baseline)\n const minVariance = Math.min(...variances, 0);\n const maxVariance = Math.max(...variances, 0);\n const range = Math.max(Math.abs(minVariance), Math.abs(maxVariance));\n\n if (range === 0 || variances.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center bg-dc-bg-secondary dc:rounded-sm dc:border border-dc-border\"\n style={{ width: `${width}px`, height: `${height}px` }}\n >\n <span className=\"dc:text-xs text-dc-text-muted\">No variance data</span>\n </div>\n );\n }\n\n // Calculate bar dimensions\n const barGap = 2;\n const availableWidth = width - (limitedValues.length - 1) * barGap;\n const barWidth = Math.max(4, availableWidth / limitedValues.length);\n\n // Calculate where zero line should be positioned (as percentage from top)\n // If maxVariance = 67 and minVariance = -24, total range = 91\n // Zero should be at 67/91 = 73.6% from top\n const totalRange = maxVariance - minVariance;\n const zeroLinePercent =\n totalRange > 0 ? (maxVariance / totalRange) * 100 : 50;\n\n return (\n <div className=\"dc:flex dc:items-center dc:space-x-2\">\n {/* Histogram bars */}\n <div\n className=\"dc:relative\"\n style={{\n width: `${width}px`,\n height: `${height}px`,\n }}\n >\n {/* Zero line (represents current value) */}\n <div\n className=\"dc:absolute dc:left-0 dc:right-0\"\n style={{\n height: \"1px\",\n top: `${zeroLinePercent}%`,\n backgroundColor: \"var(--dc-border)\",\n zIndex: 1,\n }}\n />\n\n {/* Variance bars - oldest to newest (left to right) */}\n {variances.map((variance, index) => {\n // Calculate bar height as proportion of total range\n const normalizedHeight = Math.abs(variance) / totalRange;\n const barHeight = Math.max(2, normalizedHeight * (height - 4));\n const isPositive = variance >= 0;\n const isLastValue = index === limitedValues.length - 1;\n const color = isPositive ? positiveColor : negativeColor;\n const xPosition = index * (barWidth + barGap);\n\n return (\n <div\n key={index}\n className=\"dc:absolute rounded-xs\"\n style={{\n left: `${xPosition}px`,\n width: `${barWidth}px`,\n height: `${barHeight}px`,\n backgroundColor: color,\n opacity: isLastValue ? 1 : 0.6,\n // Position bar relative to zero line\n ...(isPositive\n ? { bottom: `${100 - zeroLinePercent}%` }\n : { top: `${zeroLinePercent}%` }),\n zIndex: 2,\n }}\n title={`${formatValue(limitedValues[index])}: ${variance >= 0 ? \"+\" : \"\"}${formatValue(variance)} vs current`}\n />\n );\n })}\n </div>\n\n {/* Variance labels on the right - show actual value difference */}\n <div\n className=\"dc:flex dc:flex-col dc:justify-between dc:text-xs text-dc-text-muted\"\n style={{ height: `${height}px` }}\n >\n <span>+{formatValue(maxVariance)}</span>\n <span>\n {minVariance < 0 ? \"\" : \"\"}\n {formatValue(minVariance)}\n </span>\n </div>\n </div>\n );\n}\n\nconst KpiDelta = React.memo(function KpiDelta({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n}: ChartProps) {\n const [fontSize, setFontSize] = useState(32);\n const [textWidth, setTextWidth] = useState(250);\n const containerRef = useRef<HTMLDivElement>(null);\n const valueRef = useRef<HTMLDivElement>(null);\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel();\n\n // Calculate font size and text width based on container dimensions\n useEffect(() => {\n const updateDimensions = () => {\n if (containerRef.current) {\n const container = containerRef.current;\n const rect = container.getBoundingClientRect();\n const containerWidth = rect.width;\n const containerHeight = rect.height;\n\n if (containerWidth > 0 && containerHeight > 0) {\n const widthBasedSize = containerWidth / 4;\n const heightBasedSize = containerHeight / 4;\n const baseFontSize = Math.min(widthBasedSize, heightBasedSize);\n const clampedFontSize = Math.max(28, Math.min(baseFontSize, 140));\n setFontSize(clampedFontSize);\n\n setTimeout(() => {\n if (valueRef.current) {\n const textRect = valueRef.current.getBoundingClientRect();\n const measuredWidth = textRect.width;\n // Scale histogram width with container, accounting for labels on the right (~60px)\n const maxHistogramWidth = containerWidth - 100; // Leave room for padding and labels\n const effectiveWidth = Math.max(\n measuredWidth,\n Math.min(maxHistogramWidth, containerWidth * 0.7),\n );\n setTextWidth(Math.max(100, effectiveWidth)); // Minimum 100px\n }\n }, 10);\n }\n }\n };\n\n const timer = setTimeout(updateDimensions, 50);\n\n const resizeObserver = new ResizeObserver(() => {\n setTimeout(updateDimensions, 10);\n });\n\n if (containerRef.current) {\n resizeObserver.observe(containerRef.current);\n }\n\n return () => {\n clearTimeout(timer);\n resizeObserver.disconnect();\n };\n }, [data, chartConfig]);\n\n if (!data || data.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\"\n style={{\n height: height === \"100%\" ? \"100%\" : height,\n minHeight: height === \"100%\" ? \"200px\" : undefined,\n }}\n >\n <div className=\"dc:text-center text-dc-text-muted\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n No data points to display\n </div>\n </div>\n </div>\n );\n }\n\n // Extract value and dimension fields from chart config\n let valueFields: string[] = [];\n let dimensionFields: string[] = [];\n\n if (chartConfig?.yAxis) {\n valueFields = Array.isArray(chartConfig.yAxis)\n ? chartConfig.yAxis\n : [chartConfig.yAxis];\n }\n\n if (chartConfig?.xAxis) {\n dimensionFields = Array.isArray(chartConfig.xAxis)\n ? chartConfig.xAxis\n : [chartConfig.xAxis];\n }\n\n if (valueFields.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\"\n style={{\n height: height === \"100%\" ? \"100%\" : height,\n minHeight: height === \"100%\" ? \"200px\" : undefined,\n backgroundColor: \"var(--dc-danger-bg)\",\n color: \"var(--dc-danger)\",\n borderColor: \"var(--dc-danger-border)\",\n }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">No measure field configured</div>\n </div>\n </div>\n );\n }\n\n const valueField = valueFields[0];\n const dimensionField = dimensionFields[0]; // Optional\n\n // Sort data by dimension if available (for time series)\n let sortedData = [...data];\n if (dimensionField) {\n sortedData = sortedData.sort((a, b) => {\n const aVal = a[dimensionField];\n const bVal = b[dimensionField];\n if (aVal < bVal) return -1;\n if (aVal > bVal) return 1;\n return 0;\n });\n }\n\n // Filter out incomplete or last period if enabled\n const { useLastCompletePeriod = true, skipLastPeriod = false } =\n displayConfig;\n const {\n filteredData,\n excludedIncompletePeriod,\n skippedLastPeriod,\n granularity,\n } = filterIncompletePeriod(\n sortedData,\n dimensionField,\n queryObject,\n useLastCompletePeriod,\n skipLastPeriod,\n );\n\n // Use filtered data for calculations\n const dataToUse = filteredData;\n\n // Extract values from filtered data\n const values = dataToUse\n .map((row) => row[valueField])\n .filter((val) => val !== null && val !== undefined && !isNaN(Number(val)))\n .map((val) => Number(val));\n\n if (values.length < 2) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\"\n style={{\n height: height === \"100%\" ? \"100%\" : height,\n minHeight: height === \"100%\" ? \"200px\" : undefined,\n backgroundColor: \"var(--dc-warning-bg)\",\n color: \"var(--dc-warning)\",\n borderColor: \"var(--dc-warning-border)\",\n }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Insufficient Data</div>\n <div className=\"dc:text-xs\">\n Delta calculation requires at least 2 data points\n </div>\n <div className=\"dc:text-xs\">Current data points: {values.length}</div>\n </div>\n </div>\n );\n }\n\n // Calculate delta between last and second-last values\n const lastValue = values[values.length - 1];\n const secondLastValue = values[values.length - 2];\n const absoluteChange = lastValue - secondLastValue;\n const percentageChange =\n secondLastValue !== 0\n ? (absoluteChange / Math.abs(secondLastValue)) * 100\n : 0;\n\n const isPositiveChange = absoluteChange >= 0;\n\n // Format number with appropriate units and decimals\n const formatNumber = (value: number | null | undefined): string => {\n // If custom formatValue is provided, use it exclusively\n if (displayConfig.formatValue) {\n return displayConfig.formatValue(value);\n }\n\n // Null handling: Show placeholder for missing data\n if (value === null || value === undefined) {\n return \"—\";\n }\n\n const decimals = displayConfig.decimals ?? 0;\n const prefix = displayConfig.prefix ?? \"\";\n\n let formattedValue: string;\n\n if (Math.abs(value) >= 1e9) {\n formattedValue = (value / 1e9).toFixed(decimals) + \"B\";\n } else if (Math.abs(value) >= 1e6) {\n formattedValue = (value / 1e6).toFixed(decimals) + \"M\";\n } else if (Math.abs(value) >= 1e3) {\n formattedValue = (value / 1e3).toFixed(decimals) + \"K\";\n } else {\n formattedValue = value.toFixed(decimals);\n }\n\n return prefix + formattedValue;\n };\n\n // Get colors from palette\n const getPositiveColor = (): string => {\n if (\n displayConfig.positiveColorIndex !== undefined &&\n colorPalette?.colors\n ) {\n const colorIndex = displayConfig.positiveColorIndex;\n if (colorIndex >= 0 && colorIndex < colorPalette.colors.length) {\n return colorPalette.colors[colorIndex];\n }\n }\n return \"#10b981\"; // Default green\n };\n\n const getNegativeColor = (): string => {\n if (\n displayConfig.negativeColorIndex !== undefined &&\n colorPalette?.colors\n ) {\n const colorIndex = displayConfig.negativeColorIndex;\n if (colorIndex >= 0 && colorIndex < colorPalette.colors.length) {\n return colorPalette.colors[colorIndex];\n }\n }\n return \"#ef4444\"; // Default red\n };\n\n const positiveColor = getPositiveColor();\n const negativeColor = getNegativeColor();\n const currentColor = isPositiveChange ? positiveColor : negativeColor;\n\n return (\n <div\n ref={containerRef}\n className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full dc:h-full dc:p-4\"\n style={{\n height: height === \"100%\" ? \"100%\" : height,\n minHeight: height === \"100%\" ? \"200px\" : undefined,\n }}\n >\n {/* Field Label */}\n <div\n className=\"text-dc-text-secondary dc:font-bold dc:text-center dc:mb-2 dc:flex dc:items-center dc:justify-center dc:gap-1\"\n style={{\n fontSize: \"14px\",\n lineHeight: \"1.2\",\n }}\n >\n <span>\n {(() => {\n const label = getFieldLabel(valueField);\n return label && label.length > 1 ? label : valueField;\n })()}\n </span>\n {(excludedIncompletePeriod || skippedLastPeriod) && (\n <span\n title={\n skippedLastPeriod\n ? `Excludes last ${granularity || \"period\"}`\n : `Excludes current incomplete ${granularity}`\n }\n className=\"dc:cursor-help\"\n >\n <Icon\n icon={infoCircleIcon}\n className=\"dc:w-4 dc:h-4 text-dc-text-muted dc:opacity-70\"\n />\n </span>\n )}\n </div>\n\n {/* Main KPI Value and Delta */}\n <div className=\"dc:flex dc:items-center dc:justify-center dc:space-x-4 dc:mb-2\">\n {/* Main KPI Value */}\n <div\n ref={valueRef}\n className=\"dc:font-bold dc:leading-none\"\n style={{\n fontSize: `${fontSize}px`,\n color: \"var(--dc-text)\", // Keep main value neutral\n }}\n >\n {formatNumber(lastValue)}\n </div>\n\n {/* Delta Information */}\n <div className=\"dc:flex dc:items-center dc:space-x-1\">\n {/* Arrow */}\n <div\n className=\"dc:font-bold\"\n style={{\n color: currentColor,\n fontSize: `${fontSize * 0.35}px`,\n }}\n >\n {isPositiveChange ? \"▲\" : \"▼\"}\n </div>\n\n {/* Delta Values */}\n <div className=\"dc:text-left\">\n <div\n className=\"dc:font-bold dc:leading-tight\"\n style={{\n fontSize: `${fontSize * 0.35}px`,\n color: currentColor,\n }}\n >\n {isPositiveChange ? \"+\" : \"\"}\n {formatNumber(absoluteChange)}\n </div>\n <div\n className=\"dc:font-semibold dc:leading-tight\"\n style={{\n fontSize: `${fontSize * 0.28}px`,\n color: currentColor,\n opacity: 0.8,\n }}\n >\n {isPositiveChange ? \"+\" : \"\"}\n {percentageChange.toFixed(1)}%\n </div>\n </div>\n </div>\n </div>\n\n {/* Unit/Suffix (hidden when formatValue is provided) */}\n {displayConfig.suffix && !displayConfig.formatValue && (\n <div\n className=\"text-dc-text-muted dc:text-center dc:mb-3\"\n style={{\n fontSize: \"14px\",\n lineHeight: \"1.2\",\n opacity: 0.8,\n }}\n >\n {displayConfig.suffix}\n </div>\n )}\n\n {/* Variance Histogram */}\n {displayConfig.showHistogram !== false && values.length > 2 && (\n <div className=\"dc:mt-2 dc:w-full dc:flex dc:justify-center dc:overflow-hidden\">\n <VarianceHistogram\n values={values}\n lastValue={lastValue}\n positiveColor={positiveColor}\n negativeColor={negativeColor}\n formatValue={formatNumber}\n width={textWidth}\n height={64}\n />\n </div>\n )}\n </div>\n );\n})\n\nexport default KpiDelta\n"],"names":["VarianceHistogram","values","lastValue","positiveColor","negativeColor","formatValue","width","height","maxBars","limitedValues","variances","value","minVariance","maxVariance","jsx","barGap","availableWidth","barWidth","totalRange","zeroLinePercent","jsxs","variance","index","normalizedHeight","barHeight","isPositive","isLastValue","color","xPosition","KpiDelta","React","data","chartConfig","displayConfig","queryObject","colorPalette","fontSize","setFontSize","useState","textWidth","setTextWidth","containerRef","useRef","valueRef","getFieldLabel","useCubeFieldLabel","useEffect","updateDimensions","rect","containerWidth","containerHeight","widthBasedSize","heightBasedSize","baseFontSize","clampedFontSize","measuredWidth","maxHistogramWidth","effectiveWidth","timer","resizeObserver","valueFields","dimensionFields","valueField","dimensionField","sortedData","a","b","aVal","bVal","useLastCompletePeriod","skipLastPeriod","filteredData","excludedIncompletePeriod","skippedLastPeriod","granularity","filterIncompletePeriod","row","val","secondLastValue","absoluteChange","percentageChange","isPositiveChange","formatNumber","decimals","prefix","formattedValue","getPositiveColor","colorIndex","getNegativeColor","currentColor","label","Icon","infoCircleIcon"],"mappings":"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"}
1
+ {"version":3,"file":"chart-kpi-delta-DPcSazD3.js","sources":["../../../src/client/components/charts/KpiDelta.tsx"],"sourcesContent":["import React, { useState, useRef, useEffect } from \"react\";\nimport { Icon } from \"@iconify/react\";\nimport infoCircleIcon from \"@iconify-icons/tabler/info-circle\";\nimport { useCubeFieldLabel } from \"../../hooks/useCubeFieldLabel\";\nimport { filterIncompletePeriod } from \"../../utils/periodUtils\";\nimport type { ChartProps } from \"../../types\";\n\ninterface VarianceHistogramProps {\n values: number[];\n lastValue: number;\n positiveColor: string;\n negativeColor: string;\n formatValue: (value: number) => string;\n width: number;\n height: number;\n}\n\nfunction VarianceHistogram({\n values,\n lastValue,\n positiveColor,\n negativeColor,\n formatValue,\n width,\n height,\n}: VarianceHistogramProps) {\n // Limit to most recent N values to fit in the histogram\n // Calculate max bars based on width (minimum 8px per bar including gap)\n const maxBars = Math.max(10, Math.floor(width / 10));\n const limitedValues =\n values.length > maxBars\n ? values.slice(-maxBars) // Take the most recent values\n : values;\n\n // Calculate variance (difference) from current/last value for each point\n const variances = limitedValues.map((value) => value - lastValue);\n\n // Find min/max variance for scaling (include 0 as baseline)\n const minVariance = Math.min(...variances, 0);\n const maxVariance = Math.max(...variances, 0);\n const range = Math.max(Math.abs(minVariance), Math.abs(maxVariance));\n\n if (range === 0 || variances.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center bg-dc-bg-secondary dc:rounded-sm dc:border border-dc-border\"\n style={{ width: `${width}px`, height: `${height}px` }}\n >\n <span className=\"dc:text-xs text-dc-text-muted\">No variance data</span>\n </div>\n );\n }\n\n // Calculate bar dimensions\n const barGap = 2;\n const availableWidth = width - (limitedValues.length - 1) * barGap;\n const barWidth = Math.max(4, availableWidth / limitedValues.length);\n\n // Calculate where zero line should be positioned (as percentage from top)\n // If maxVariance = 67 and minVariance = -24, total range = 91\n // Zero should be at 67/91 = 73.6% from top\n const totalRange = maxVariance - minVariance;\n const zeroLinePercent =\n totalRange > 0 ? (maxVariance / totalRange) * 100 : 50;\n\n return (\n <div className=\"dc:flex dc:items-center dc:space-x-2\">\n {/* Histogram bars */}\n <div\n className=\"dc:relative\"\n style={{\n width: `${width}px`,\n height: `${height}px`,\n }}\n >\n {/* Zero line (represents current value) */}\n <div\n className=\"dc:absolute dc:left-0 dc:right-0\"\n style={{\n height: \"1px\",\n top: `${zeroLinePercent}%`,\n backgroundColor: \"var(--dc-border)\",\n zIndex: 1,\n }}\n />\n\n {/* Variance bars - oldest to newest (left to right) */}\n {variances.map((variance, index) => {\n // Calculate bar height as proportion of total range\n const normalizedHeight = Math.abs(variance) / totalRange;\n const barHeight = Math.max(2, normalizedHeight * (height - 4));\n const isPositive = variance >= 0;\n const isLastValue = index === limitedValues.length - 1;\n const color = isPositive ? positiveColor : negativeColor;\n const xPosition = index * (barWidth + barGap);\n\n return (\n <div\n key={index}\n className=\"dc:absolute rounded-xs\"\n style={{\n left: `${xPosition}px`,\n width: `${barWidth}px`,\n height: `${barHeight}px`,\n backgroundColor: color,\n opacity: isLastValue ? 1 : 0.6,\n // Position bar relative to zero line\n ...(isPositive\n ? { bottom: `${100 - zeroLinePercent}%` }\n : { top: `${zeroLinePercent}%` }),\n zIndex: 2,\n }}\n title={`${formatValue(limitedValues[index])}: ${variance >= 0 ? \"+\" : \"\"}${formatValue(variance)} vs current`}\n />\n );\n })}\n </div>\n\n {/* Variance labels on the right - show actual value difference */}\n <div\n className=\"dc:flex dc:flex-col dc:justify-between dc:text-xs text-dc-text-muted\"\n style={{ height: `${height}px` }}\n >\n <span>+{formatValue(maxVariance)}</span>\n <span>\n {minVariance < 0 ? \"\" : \"\"}\n {formatValue(minVariance)}\n </span>\n </div>\n </div>\n );\n}\n\nconst KpiDelta = React.memo(function KpiDelta({\n data,\n chartConfig,\n displayConfig = {},\n queryObject,\n height = \"100%\",\n colorPalette,\n}: ChartProps) {\n const [fontSize, setFontSize] = useState(32);\n const [textWidth, setTextWidth] = useState(250);\n const containerRef = useRef<HTMLDivElement>(null);\n const valueRef = useRef<HTMLDivElement>(null);\n // Use specialized hook to avoid re-renders from unrelated context changes\n const getFieldLabel = useCubeFieldLabel();\n\n // Calculate font size and text width based on container dimensions\n useEffect(() => {\n const updateDimensions = () => {\n if (containerRef.current) {\n const container = containerRef.current;\n const rect = container.getBoundingClientRect();\n const containerWidth = rect.width;\n const containerHeight = rect.height;\n\n if (containerWidth > 0 && containerHeight > 0) {\n const widthBasedSize = containerWidth / 4;\n const heightBasedSize = containerHeight / 4;\n const baseFontSize = Math.min(widthBasedSize, heightBasedSize);\n const clampedFontSize = Math.max(28, Math.min(baseFontSize, 140));\n setFontSize(clampedFontSize);\n\n setTimeout(() => {\n if (valueRef.current) {\n const textRect = valueRef.current.getBoundingClientRect();\n const measuredWidth = textRect.width;\n // Scale histogram width with container, accounting for labels on the right (~60px)\n const maxHistogramWidth = containerWidth - 100; // Leave room for padding and labels\n const effectiveWidth = Math.max(\n measuredWidth,\n Math.min(maxHistogramWidth, containerWidth * 0.7),\n );\n setTextWidth(Math.max(100, effectiveWidth)); // Minimum 100px\n }\n }, 10);\n }\n }\n };\n\n const timer = setTimeout(updateDimensions, 50);\n\n const resizeObserver = new ResizeObserver(() => {\n setTimeout(updateDimensions, 10);\n });\n\n if (containerRef.current) {\n resizeObserver.observe(containerRef.current);\n }\n\n return () => {\n clearTimeout(timer);\n resizeObserver.disconnect();\n };\n }, [data, chartConfig]);\n\n if (!data || data.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\"\n style={{\n height: height === \"100%\" ? \"100%\" : height,\n minHeight: height === \"100%\" ? \"200px\" : undefined,\n }}\n >\n <div className=\"dc:text-center text-dc-text-muted\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">No data available</div>\n <div className=\"dc:text-xs text-dc-text-secondary\">\n No data points to display\n </div>\n </div>\n </div>\n );\n }\n\n // Extract value and dimension fields from chart config\n let valueFields: string[] = [];\n let dimensionFields: string[] = [];\n\n if (chartConfig?.yAxis) {\n valueFields = Array.isArray(chartConfig.yAxis)\n ? chartConfig.yAxis\n : [chartConfig.yAxis];\n }\n\n if (chartConfig?.xAxis) {\n dimensionFields = Array.isArray(chartConfig.xAxis)\n ? chartConfig.xAxis\n : [chartConfig.xAxis];\n }\n\n if (valueFields.length === 0) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\"\n style={{\n height: height === \"100%\" ? \"100%\" : height,\n minHeight: height === \"100%\" ? \"200px\" : undefined,\n backgroundColor: \"var(--dc-danger-bg)\",\n color: \"var(--dc-danger)\",\n borderColor: \"var(--dc-danger-border)\",\n }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Configuration Error</div>\n <div className=\"dc:text-xs\">No measure field configured</div>\n </div>\n </div>\n );\n }\n\n const valueField = valueFields[0];\n const dimensionField = dimensionFields[0]; // Optional\n\n // Sort data by dimension if available (for time series)\n let sortedData = [...data];\n if (dimensionField) {\n sortedData = sortedData.sort((a, b) => {\n const aVal = a[dimensionField];\n const bVal = b[dimensionField];\n if (aVal < bVal) return -1;\n if (aVal > bVal) return 1;\n return 0;\n });\n }\n\n // Filter out incomplete or last period if enabled\n const { useLastCompletePeriod = true, skipLastPeriod = false } =\n displayConfig;\n const {\n filteredData,\n excludedIncompletePeriod,\n skippedLastPeriod,\n granularity,\n } = filterIncompletePeriod(\n sortedData,\n dimensionField,\n queryObject,\n useLastCompletePeriod,\n skipLastPeriod,\n );\n\n // Use filtered data for calculations\n const dataToUse = filteredData;\n\n // Extract values from filtered data\n const values = dataToUse\n .map((row) => row[valueField])\n .filter((val) => val !== null && val !== undefined && !isNaN(Number(val)))\n .map((val) => Number(val));\n\n if (values.length < 2) {\n return (\n <div\n className=\"dc:flex dc:items-center dc:justify-center dc:w-full dc:h-full\"\n style={{\n height: height === \"100%\" ? \"100%\" : height,\n minHeight: height === \"100%\" ? \"200px\" : undefined,\n backgroundColor: \"var(--dc-warning-bg)\",\n color: \"var(--dc-warning)\",\n borderColor: \"var(--dc-warning-border)\",\n }}\n >\n <div className=\"dc:text-center\">\n <div className=\"dc:text-sm dc:font-semibold dc:mb-1\">Insufficient Data</div>\n <div className=\"dc:text-xs\">\n Delta calculation requires at least 2 data points\n </div>\n <div className=\"dc:text-xs\">Current data points: {values.length}</div>\n </div>\n </div>\n );\n }\n\n // Calculate delta between last and second-last values\n const lastValue = values[values.length - 1];\n const secondLastValue = values[values.length - 2];\n const absoluteChange = lastValue - secondLastValue;\n const percentageChange =\n secondLastValue !== 0\n ? (absoluteChange / Math.abs(secondLastValue)) * 100\n : 0;\n\n const isPositiveChange = absoluteChange >= 0;\n\n // Format number with appropriate units and decimals\n const formatNumber = (value: number | null | undefined): string => {\n // If custom formatValue is provided, use it exclusively\n if (displayConfig.formatValue) {\n return displayConfig.formatValue(value);\n }\n\n // Null handling: Show placeholder for missing data\n if (value === null || value === undefined) {\n return \"—\";\n }\n\n const decimals = displayConfig.decimals ?? 0;\n const prefix = displayConfig.prefix ?? \"\";\n\n let formattedValue: string;\n\n if (Math.abs(value) >= 1e9) {\n formattedValue = (value / 1e9).toFixed(decimals) + \"B\";\n } else if (Math.abs(value) >= 1e6) {\n formattedValue = (value / 1e6).toFixed(decimals) + \"M\";\n } else if (Math.abs(value) >= 1e3) {\n formattedValue = (value / 1e3).toFixed(decimals) + \"K\";\n } else {\n formattedValue = value.toFixed(decimals);\n }\n\n return prefix + formattedValue;\n };\n\n // Get colors from palette\n const getPositiveColor = (): string => {\n if (\n displayConfig.positiveColorIndex !== undefined &&\n colorPalette?.colors\n ) {\n const colorIndex = displayConfig.positiveColorIndex;\n if (colorIndex >= 0 && colorIndex < colorPalette.colors.length) {\n return colorPalette.colors[colorIndex];\n }\n }\n return \"#10b981\"; // Default green\n };\n\n const getNegativeColor = (): string => {\n if (\n displayConfig.negativeColorIndex !== undefined &&\n colorPalette?.colors\n ) {\n const colorIndex = displayConfig.negativeColorIndex;\n if (colorIndex >= 0 && colorIndex < colorPalette.colors.length) {\n return colorPalette.colors[colorIndex];\n }\n }\n return \"#ef4444\"; // Default red\n };\n\n const positiveColor = getPositiveColor();\n const negativeColor = getNegativeColor();\n const currentColor = isPositiveChange ? positiveColor : negativeColor;\n\n return (\n <div\n ref={containerRef}\n className=\"dc:flex dc:flex-col dc:items-center dc:justify-center dc:w-full dc:h-full dc:p-4\"\n style={{\n height: height === \"100%\" ? \"100%\" : height,\n minHeight: height === \"100%\" ? \"200px\" : undefined,\n }}\n >\n {/* Field Label */}\n <div\n className=\"text-dc-text-secondary dc:font-bold dc:text-center dc:mb-2 dc:flex dc:items-center dc:justify-center dc:gap-1\"\n style={{\n fontSize: \"14px\",\n lineHeight: \"1.2\",\n }}\n >\n <span>\n {(() => {\n const label = getFieldLabel(valueField);\n return label && label.length > 1 ? label : valueField;\n })()}\n </span>\n {(excludedIncompletePeriod || skippedLastPeriod) && (\n <span\n title={\n skippedLastPeriod\n ? `Excludes last ${granularity || \"period\"}`\n : `Excludes current incomplete ${granularity}`\n }\n className=\"dc:cursor-help\"\n >\n <Icon\n icon={infoCircleIcon}\n className=\"dc:w-4 dc:h-4 text-dc-text-muted dc:opacity-70\"\n />\n </span>\n )}\n </div>\n\n {/* Main KPI Value and Delta */}\n <div className=\"dc:flex dc:items-center dc:justify-center dc:space-x-4 dc:mb-2\">\n {/* Main KPI Value */}\n <div\n ref={valueRef}\n className=\"dc:font-bold dc:leading-none\"\n style={{\n fontSize: `${fontSize}px`,\n color: \"var(--dc-text)\", // Keep main value neutral\n }}\n >\n {formatNumber(lastValue)}\n </div>\n\n {/* Delta Information */}\n <div className=\"dc:flex dc:items-center dc:space-x-1\">\n {/* Arrow */}\n <div\n className=\"dc:font-bold\"\n style={{\n color: currentColor,\n fontSize: `${fontSize * 0.35}px`,\n }}\n >\n {isPositiveChange ? \"▲\" : \"▼\"}\n </div>\n\n {/* Delta Values */}\n <div className=\"dc:text-left\">\n <div\n className=\"dc:font-bold dc:leading-tight\"\n style={{\n fontSize: `${fontSize * 0.35}px`,\n color: currentColor,\n }}\n >\n {isPositiveChange ? \"+\" : \"\"}\n {formatNumber(absoluteChange)}\n </div>\n <div\n className=\"dc:font-semibold dc:leading-tight\"\n style={{\n fontSize: `${fontSize * 0.28}px`,\n color: currentColor,\n opacity: 0.8,\n }}\n >\n {isPositiveChange ? \"+\" : \"\"}\n {percentageChange.toFixed(1)}%\n </div>\n </div>\n </div>\n </div>\n\n {/* Unit/Suffix (hidden when formatValue is provided) */}\n {displayConfig.suffix && !displayConfig.formatValue && (\n <div\n className=\"text-dc-text-muted dc:text-center dc:mb-3\"\n style={{\n fontSize: \"14px\",\n lineHeight: \"1.2\",\n opacity: 0.8,\n }}\n >\n {displayConfig.suffix}\n </div>\n )}\n\n {/* Variance Histogram */}\n {displayConfig.showHistogram !== false && values.length > 2 && (\n <div className=\"dc:mt-2 dc:w-full dc:flex dc:justify-center dc:overflow-hidden\">\n <VarianceHistogram\n values={values}\n lastValue={lastValue}\n positiveColor={positiveColor}\n negativeColor={negativeColor}\n formatValue={formatNumber}\n width={textWidth}\n height={64}\n />\n </div>\n )}\n </div>\n );\n})\n\nexport default KpiDelta\n"],"names":["VarianceHistogram","values","lastValue","positiveColor","negativeColor","formatValue","width","height","maxBars","limitedValues","variances","value","minVariance","maxVariance","jsx","barGap","availableWidth","barWidth","totalRange","zeroLinePercent","jsxs","variance","index","normalizedHeight","barHeight","isPositive","isLastValue","color","xPosition","KpiDelta","React","data","chartConfig","displayConfig","queryObject","colorPalette","fontSize","setFontSize","useState","textWidth","setTextWidth","containerRef","useRef","valueRef","getFieldLabel","useCubeFieldLabel","useEffect","updateDimensions","rect","containerWidth","containerHeight","widthBasedSize","heightBasedSize","baseFontSize","clampedFontSize","measuredWidth","maxHistogramWidth","effectiveWidth","timer","resizeObserver","valueFields","dimensionFields","valueField","dimensionField","sortedData","a","b","aVal","bVal","useLastCompletePeriod","skipLastPeriod","filteredData","excludedIncompletePeriod","skippedLastPeriod","granularity","filterIncompletePeriod","row","val","secondLastValue","absoluteChange","percentageChange","isPositiveChange","formatNumber","decimals","prefix","formattedValue","getPositiveColor","colorIndex","getNegativeColor","currentColor","label","Icon","infoCircleIcon"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsxs as x, jsx as c } from "react/jsx-runtime";
2
2
  import te, { useState as j, useRef as _, useMemo as N, useCallback as ne, useEffect as re } from "react";
3
3
  import { I as se, d as ce } from "./icons-DRreo6m8.js";
4
- import { u as ie, p as de, j as oe, k as ae } from "./charts-core-DAF1iT_h.js";
4
+ import { u as ie, p as de, j as oe, k as ae } from "./charts-core-T8UglYyq.js";
5
5
  function le(o, r) {
6
6
  const e = new Date(o);
7
7
  switch (r.toLowerCase()) {
@@ -473,4 +473,4 @@ export {
473
473
  le as g,
474
474
  ue as i
475
475
  };
476
- //# sourceMappingURL=chart-kpi-number-B3yvgbr-.js.map
476
+ //# sourceMappingURL=chart-kpi-number-D62PzKZ1.js.map