@burdenoff/microfe-bigconsole 2026.613.2 → 2026.613.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.
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js +142 -135
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
- package/dist/bigconsole/components/dashboard/WidgetPalette.js +124 -120
- package/dist/bigconsole/components/dashboard/WidgetPalette.js.map +1 -1
- package/dist/bigconsole/components/widgets/PropertiesPanel.js +9 -9
- package/dist/bigconsole/components/widgets/WidgetRegistry.js +65 -8
- package/dist/bigconsole/components/widgets/WidgetRegistry.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetTypeSelector.js +32 -27
- package/dist/bigconsole/components/widgets/WidgetTypeSelector.js.map +1 -1
- package/dist/bigconsole/components/widgets/WidgetWrapper.js +9 -9
- package/dist/bigconsole/components/widgets/WidgetWrapper.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/ChartWidget.js +123 -91
- package/dist/bigconsole/components/widgets/chart/ChartWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/charts/AreaChart.js +40 -35
- package/dist/bigconsole/components/widgets/chart/charts/AreaChart.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/charts/BarChart.js +30 -25
- package/dist/bigconsole/components/widgets/chart/charts/BarChart.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/charts/ComboChart.js +139 -0
- package/dist/bigconsole/components/widgets/chart/charts/ComboChart.js.map +1 -0
- package/dist/bigconsole/components/widgets/chart/charts/LineChart.js +35 -30
- package/dist/bigconsole/components/widgets/chart/charts/LineChart.js.map +1 -1
- package/dist/bigconsole/components/widgets/chart/charts/ScatterChart.js +128 -0
- package/dist/bigconsole/components/widgets/chart/charts/ScatterChart.js.map +1 -0
- package/dist/bigconsole/components/widgets/chart/charts/WaterfallChart.js +111 -0
- package/dist/bigconsole/components/widgets/chart/charts/WaterfallChart.js.map +1 -0
- package/dist/bigconsole/components/widgets/chart/charts/index.js +4 -0
- package/dist/bigconsole/components/widgets/chart/charts/referenceElements.js +49 -0
- package/dist/bigconsole/components/widgets/chart/charts/referenceElements.js.map +1 -0
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js +72 -68
- package/dist/bigconsole/components/widgets/kpi-comparison/KPIComparisonWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js +74 -24
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js +87 -86
- package/dist/bigconsole/components/widgets/metric-card/MetricCardWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableCell.js +55 -26
- package/dist/bigconsole/components/widgets/table/TableCell.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableHeader.js +25 -18
- package/dist/bigconsole/components/widgets/table/TableHeader.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableWidget.js +255 -107
- package/dist/bigconsole/components/widgets/table/TableWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/utils/conditionalFormat.js +91 -0
- package/dist/bigconsole/components/widgets/utils/conditionalFormat.js.map +1 -0
- package/dist/bigconsole/hooks/index.js +2 -0
- package/dist/bigconsole/hooks/useDashboardImageExport.js +1 -0
- package/dist/bigconsole/hooks/useWidgetCatalog.js +82 -0
- package/dist/bigconsole/hooks/useWidgetCatalog.js.map +1 -0
- package/dist/bigconsole/pages/DashboardBuilderPage.js +172 -163
- package/dist/bigconsole/pages/DashboardBuilderPage.js.map +1 -1
- package/dist/bigconsole/utils/widgetTypeMapping.js +1 -0
- package/dist/bigconsole/utils/widgetTypeMapping.js.map +1 -1
- package/dist/generated/wspace-operations.js +17 -17
- package/dist/generated/wspace-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/chart/ChartWidget.tsx"],"sourcesContent":["/**\n * ChartWidget Component\n *\n * Wrapper component that renders the appropriate chart type.\n */\n\nimport { type FC, memo, useState, useMemo, useCallback } from 'react';\nimport type { Widget, ChartConfig as WidgetChartConfig } from '../../../types';\nimport { LineChart } from './charts/LineChart';\nimport { BarChart } from './charts/BarChart';\nimport { AreaChart } from './charts/AreaChart';\nimport { PieChart } from './charts/PieChart';\nimport { FunnelChart } from './charts/FunnelChart';\nimport { ChartLegend, type LegendItem } from './ChartLegend';\nimport type { ChartConfig, ChartData, ChartType, CurveType } from './charts/types';\n// Phase 2: Cross-filtering support\nimport { useWidgetCrossFilter, useCrossFilterContext } from '../../../contexts';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ChartWidgetProps {\n /** Widget configuration */\n widget: Widget;\n /** Chart data */\n data?: ChartData[];\n /** Click handler for drilldown */\n onClick?: (dataPoint: ChartData, index: number) => void;\n /** Drilldown handler - receives selected data point */\n onDrilldown?: (selectedData: Record<string, unknown>) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ChartWidget: FC<ChartWidgetProps> = memo(function ChartWidget({\n widget,\n data = [],\n onClick,\n onDrilldown,\n}) {\n const widgetConfig = widget.config as unknown as WidgetChartConfig | undefined;\n const [hiddenSeries, setHiddenSeries] = useState<string[]>([]);\n\n // Phase 2: Cross-filtering support\n const { handleDataClick: triggerCrossFilter, enabled: crossFilterEnabled } = useWidgetCrossFilter(\n widget.id,\n // Map chart data fields to filter IDs\n widgetConfig?.crossFilterMappings || [\n { sourceField: widgetConfig?.labelField || 'name', targetFilterId: 'category' },\n ]\n );\n const { clearAllCrossFilters, hasCrossFilters } = useCrossFilterContext();\n\n // Normalize widget config to chart config\n const chartConfig: ChartConfig = useMemo(\n () =>\n ({\n type: (widgetConfig?.chartType?.toLowerCase() || 'line') as ChartType,\n xAxisKey: widgetConfig?.xAxisField || 'name',\n xAxisLabel: widgetConfig?.xAxisLabel,\n yAxisLabel: widgetConfig?.yAxisLabel,\n showGrid: widgetConfig?.showGrid,\n showLegend: widgetConfig?.showLegend,\n legendPosition: widgetConfig?.legendPosition,\n showDots: widgetConfig?.showDataPoints,\n curveType: widgetConfig?.curveType as CurveType | undefined,\n colors: widgetConfig?.colors,\n seriesNames: widgetConfig?.seriesLabels,\n stacked: widgetConfig?.stacked,\n isDonut: widgetConfig?.showAsDonut,\n innerRadius: widgetConfig?.innerRadius,\n outerRadius: widgetConfig?.outerRadius,\n }) as ChartConfig,\n [widgetConfig]\n );\n\n // Extract legend items for cartesian charts\n const legendItems: LegendItem[] = useMemo(() => {\n if (!data.length) return [];\n\n // For pie/funnel charts, legend is handled internally\n if (chartConfig.type === 'pie' || chartConfig.type === 'funnel') {\n return [];\n }\n\n // Get selected fields from widget config (if specified)\n const selectedFields = widgetConfig?.selectedFields as string[] | undefined;\n\n // Filter to numeric fields, and apply selectedFields filter if configured\n const keys = Object.keys(data[0]).filter((key) => {\n // Must not be the x-axis key and must be numeric\n const isValidNumericField = key !== chartConfig.xAxisKey && typeof data[0][key] === 'number';\n\n // If selectedFields is configured and not empty, only include those fields\n if (selectedFields && selectedFields.length > 0) {\n return isValidNumericField && selectedFields.includes(key);\n }\n\n // Otherwise, show all numeric fields\n return isValidNumericField;\n });\n\n return keys.map((key, index) => ({\n dataKey: key,\n name: chartConfig.seriesNames?.[key] || key,\n color: chartConfig.colors?.[index] || DEFAULT_COLORS[index % DEFAULT_COLORS.length],\n hidden: hiddenSeries.includes(key),\n }));\n }, [data, chartConfig, hiddenSeries, widgetConfig?.selectedFields]);\n\n // Toggle series visibility\n const handleLegendClick = useCallback((dataKey: string) => {\n setHiddenSeries((prev) => (prev.includes(dataKey) ? prev.filter((k) => k !== dataKey) : [...prev, dataKey]));\n }, []);\n\n // Handle data point click\n // Clicking a bar/slice triggers BOTH cross-filtering AND drilldown:\n // 1. Cross-filter: highlights the clicked item across all widgets\n // 2. Drilldown: opens the detail modal/page for the clicked item\n const handleDataClick = useCallback(\n (dataPoint: ChartData, index: number) => {\n // Trigger cross-filtering on data click when enabled\n if (crossFilterEnabled) {\n const label = dataPoint[widgetConfig?.labelField || 'name'] as string;\n triggerCrossFilter(dataPoint as Record<string, unknown>, label);\n }\n\n // Trigger drilldown if configured\n if (onDrilldown) {\n onDrilldown(dataPoint as Record<string, unknown>);\n } else if (onClick && widget.drilldowns?.length) {\n onClick(dataPoint, index);\n }\n },\n [onClick, onDrilldown, widget.drilldowns, crossFilterEnabled, triggerCrossFilter, widgetConfig?.labelField]\n );\n\n // Render the appropriate chart\n const renderChart = () => {\n switch (chartConfig.type) {\n case 'bar':\n return <BarChart data={data} config={chartConfig} hiddenSeries={hiddenSeries} onDataClick={handleDataClick} />;\n\n case 'area':\n return <AreaChart data={data} config={chartConfig} hiddenSeries={hiddenSeries} onDataClick={handleDataClick} />;\n\n case 'pie':\n return (\n <PieChart\n data={data}\n config={chartConfig}\n dataKey={widgetConfig?.valueField || 'value'}\n nameKey={widgetConfig?.labelField || 'name'}\n onDataClick={handleDataClick}\n />\n );\n\n case 'funnel':\n return (\n <FunnelChart\n data={data}\n config={chartConfig}\n dataKey={widgetConfig?.valueField || 'value'}\n nameKey={widgetConfig?.labelField || 'name'}\n onDataClick={handleDataClick}\n />\n );\n\n case 'line':\n default:\n return <LineChart data={data} config={chartConfig} hiddenSeries={hiddenSeries} onDataClick={handleDataClick} />;\n }\n };\n\n return (\n <div className=\"flex flex-col h-full p-4\">\n {/* Cross-filter active indicator with clear button */}\n {hasCrossFilters && (\n <div className=\"flex items-center gap-2 mb-2 -mt-1\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n clearAllCrossFilters();\n }}\n className=\"\n inline-flex items-center gap-1.5\n px-2.5 py-1 rounded-md\n text-xs font-medium\n bg-action-primary-bg/10 text-action-primary-text\n hover:bg-action-primary-bg/20\n transition-colors\n \"\n >\n <svg className=\"w-3.5 h-3.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M10 19l-7-7m0 0l7-7m-7 7h18\" />\n </svg>\n Clear Filter\n </button>\n </div>\n )}\n\n {/* Custom legend (for line/bar/area charts with external legend) */}\n {chartConfig.legendPosition === 'top' && legendItems.length > 0 && (\n <ChartLegend items={legendItems} position=\"top\" onItemClick={handleLegendClick} />\n )}\n\n {/* Chart container */}\n <div className=\"flex-1 min-h-0\">{renderChart()}</div>\n\n {/* Custom legend (bottom) */}\n {chartConfig.legendPosition === 'bottom' && legendItems.length > 0 && (\n <ChartLegend items={legendItems} position=\"bottom\" onItemClick={handleLegendClick} />\n )}\n </div>\n );\n});\n\nexport default ChartWidget;\n"],"mappings":";;;;;;;;;;;AAqCA,IAAM,IAAiB;CACrB;CACA;CACA;CACA;CACA;CACD,EAMY,IAAoC,EAAK,SAAqB,EACzE,WACA,UAAO,EAAE,EACT,YACA,kBACC;CACD,IAAM,IAAe,EAAO,QACtB,CAAC,GAAc,KAAmB,EAAmB,EAAE,CAAC,EAGxD,EAAE,iBAAiB,GAAoB,SAAS,MAAuB,EAC3E,EAAO,IAEP,GAAc,uBAAuB,CACnC;EAAE,aAAa,GAAc,cAAc;EAAQ,gBAAgB;EAAY,CAChF,CACF,EACK,EAAE,yBAAsB,uBAAoB,GAAuB,EAGnE,IAA2B,SAE5B;EACC,MAAO,GAAc,WAAW,aAAa,IAAI;EACjD,UAAU,GAAc,cAAc;EACtC,YAAY,GAAc;EAC1B,YAAY,GAAc;EAC1B,UAAU,GAAc;EACxB,YAAY,GAAc;EAC1B,gBAAgB,GAAc;EAC9B,UAAU,GAAc;EACxB,WAAW,GAAc;EACzB,QAAQ,GAAc;EACtB,aAAa,GAAc;EAC3B,SAAS,GAAc;EACvB,SAAS,GAAc;EACvB,aAAa,GAAc;EAC3B,aAAa,GAAc;EAC5B,GACH,CAAC,EAAa,CACf,EAGK,IAA4B,QAAc;AAI9C,MAHI,CAAC,EAAK,UAGN,EAAY,SAAS,SAAS,EAAY,SAAS,SACrD,QAAO,EAAE;EAIX,IAAM,IAAiB,GAAc;AAgBrC,SAba,OAAO,KAAK,EAAK,GAAG,CAAC,QAAQ,MAAQ;GAEhD,IAAM,IAAsB,MAAQ,EAAY,YAAY,OAAO,EAAK,GAAG,MAAS;AAQpF,UALI,KAAkB,EAAe,SAAS,IACrC,KAAuB,EAAe,SAAS,EAAI,GAIrD;IACP,CAEU,KAAK,GAAK,OAAW;GAC/B,SAAS;GACT,MAAM,EAAY,cAAc,MAAQ;GACxC,OAAO,EAAY,SAAS,MAAU,EAAe,IAAQ,EAAe;GAC5E,QAAQ,EAAa,SAAS,EAAI;GACnC,EAAE;IACF;EAAC;EAAM;EAAa;EAAc,GAAc;EAAe,CAAC,EAG7D,IAAoB,GAAa,MAAoB;AACzD,KAAiB,MAAU,EAAK,SAAS,EAAQ,GAAG,EAAK,QAAQ,MAAM,MAAM,EAAQ,GAAG,CAAC,GAAG,GAAM,EAAQ,CAAE;IAC3G,EAAE,CAAC,EAMA,IAAkB,GACrB,GAAsB,MAAkB;AAEvC,MAAI,GAAoB;GACtB,IAAM,IAAQ,EAAU,GAAc,cAAc;AACpD,KAAmB,GAAsC,EAAM;;AAIjE,EAAI,IACF,EAAY,EAAqC,GACxC,KAAW,EAAO,YAAY,UACvC,EAAQ,GAAW,EAAM;IAG7B;EAAC;EAAS;EAAa,EAAO;EAAY;EAAoB;EAAoB,GAAc;EAAW,CAC5G;AAuCD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEG,KACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,UAAD;KACE,MAAK;KACL,UAAU,MAAM;AAEd,MADA,EAAE,iBAAiB,EACnB,GAAsB;;KAExB,WAAU;eANZ,CAeE,kBAAC,OAAD;MAAK,WAAU;MAAc,MAAK;MAAO,QAAO;MAAe,SAAQ;gBACrE,kBAAC,QAAD;OAAM,eAAc;OAAQ,gBAAe;OAAQ,aAAa;OAAG,GAAE;OAAgC,CAAA;MACjG,CAAA,EAAA,eAEC;;IACL,CAAA;GAIP,EAAY,mBAAmB,SAAS,EAAY,SAAS,KAC5D,kBAAC,GAAD;IAAa,OAAO;IAAa,UAAS;IAAM,aAAa;IAAqB,CAAA;GAIpF,kBAAC,OAAD;IAAK,WAAU;qBAtEO;AACxB,aAAQ,EAAY,MAApB;MACE,KAAK,MACH,QAAO,kBAAC,GAAD;OAAgB;OAAM,QAAQ;OAA2B;OAAc,aAAa;OAAmB,CAAA;MAEhH,KAAK,OACH,QAAO,kBAAC,GAAD;OAAiB;OAAM,QAAQ;OAA2B;OAAc,aAAa;OAAmB,CAAA;MAEjH,KAAK,MACH,QACE,kBAAC,GAAD;OACQ;OACN,QAAQ;OACR,SAAS,GAAc,cAAc;OACrC,SAAS,GAAc,cAAc;OACrC,aAAa;OACb,CAAA;MAGN,KAAK,SACH,QACE,kBAAC,GAAD;OACQ;OACN,QAAQ;OACR,SAAS,GAAc,cAAc;OACrC,SAAS,GAAc,cAAc;OACrC,aAAa;OACb,CAAA;MAIN,QACE,QAAO,kBAAC,GAAD;OAAiB;OAAM,QAAQ;OAA2B;OAAc,aAAa;OAAmB,CAAA;;QAsCnE;IAAO,CAAA;GAGpD,EAAY,mBAAmB,YAAY,EAAY,SAAS,KAC/D,kBAAC,GAAD;IAAa,OAAO;IAAa,UAAS;IAAS,aAAa;IAAqB,CAAA;GAEnF;;EAER"}
|
|
1
|
+
{"version":3,"file":"ChartWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/chart/ChartWidget.tsx"],"sourcesContent":["/**\n * ChartWidget Component\n *\n * Wrapper component that renders the appropriate chart type.\n */\n\nimport { type FC, memo, useState, useMemo, useCallback } from 'react';\nimport type { Widget, ChartConfig as WidgetChartConfig } from '../../../types';\nimport { LineChart } from './charts/LineChart';\nimport { BarChart } from './charts/BarChart';\nimport { AreaChart } from './charts/AreaChart';\nimport { PieChart } from './charts/PieChart';\nimport { FunnelChart } from './charts/FunnelChart';\nimport { ComboChart } from './charts/ComboChart';\nimport { WaterfallChart } from './charts/WaterfallChart';\nimport { ScatterChart } from './charts/ScatterChart';\nimport { ChartLegend, type LegendItem } from './ChartLegend';\nimport type { ChartConfig, ChartData, ChartType, CurveType, ReferenceLineConfig, SeriesConfig } from './charts/types';\n// Phase 2: Cross-filtering support\nimport { useWidgetCrossFilter, useCrossFilterContext } from '../../../contexts';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ChartWidgetProps {\n /** Widget configuration */\n widget: Widget;\n /** Chart data */\n data?: ChartData[];\n /** Click handler for drilldown */\n onClick?: (dataPoint: ChartData, index: number) => void;\n /** Drilldown handler - receives selected data point */\n onDrilldown?: (selectedData: Record<string, unknown>) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ChartWidget: FC<ChartWidgetProps> = memo(function ChartWidget({\n widget,\n data = [],\n onClick,\n onDrilldown,\n}) {\n const widgetConfig = widget.config as unknown as WidgetChartConfig | undefined;\n const [hiddenSeries, setHiddenSeries] = useState<string[]>([]);\n\n // Phase 2: Cross-filtering support\n const { handleDataClick: triggerCrossFilter, enabled: crossFilterEnabled } = useWidgetCrossFilter(\n widget.id,\n // Map chart data fields to filter IDs\n widgetConfig?.crossFilterMappings || [\n { sourceField: widgetConfig?.labelField || 'name', targetFilterId: 'category' },\n ]\n );\n const { clearAllCrossFilters, hasCrossFilters } = useCrossFilterContext();\n\n // Normalize widget config to chart config\n const chartConfig: ChartConfig = useMemo(\n () =>\n ({\n type: (widgetConfig?.chartType?.toLowerCase() || 'line') as ChartType,\n xAxisKey: widgetConfig?.xAxisField || 'name',\n xAxisLabel: widgetConfig?.xAxisLabel,\n yAxisLabel: widgetConfig?.yAxisLabel,\n showGrid: widgetConfig?.showGrid,\n showLegend: widgetConfig?.showLegend,\n legendPosition: widgetConfig?.legendPosition,\n showDots: widgetConfig?.showDataPoints,\n curveType: widgetConfig?.curveType as CurveType | undefined,\n colors: widgetConfig?.colors,\n seriesNames: widgetConfig?.seriesLabels,\n stacked: widgetConfig?.stacked,\n isDonut: widgetConfig?.showAsDonut,\n innerRadius: widgetConfig?.innerRadius,\n outerRadius: widgetConfig?.outerRadius,\n referenceLines: widgetConfig?.referenceLines as ReferenceLineConfig[] | undefined,\n series: widgetConfig?.series as SeriesConfig[] | undefined,\n dualAxis: widgetConfig?.dualAxis,\n yAxisRightLabel: widgetConfig?.yAxisRightLabel,\n logScale: (widgetConfig as { logScale?: boolean } | undefined)?.logScale,\n valueField: widgetConfig?.valueField,\n labelField: widgetConfig?.labelField,\n showTotal: (widgetConfig as { showTotal?: boolean } | undefined)?.showTotal,\n totalLabel: (widgetConfig as { totalLabel?: string } | undefined)?.totalLabel,\n xField: (widgetConfig as { xField?: string } | undefined)?.xField,\n yField: (widgetConfig as { yField?: string } | undefined)?.yField,\n sizeField: (widgetConfig as { sizeField?: string } | undefined)?.sizeField,\n colorField: (widgetConfig as { colorField?: string } | undefined)?.colorField,\n }) as ChartConfig,\n [widgetConfig]\n );\n\n // Extract legend items for cartesian charts\n const legendItems: LegendItem[] = useMemo(() => {\n if (!data.length) return [];\n\n // For pie/funnel/scatter/waterfall charts, legend is handled internally\n if (\n chartConfig.type === 'pie' ||\n chartConfig.type === 'funnel' ||\n chartConfig.type === 'scatter' ||\n chartConfig.type === 'waterfall'\n ) {\n return [];\n }\n\n // Get selected fields from widget config (if specified)\n const selectedFields = widgetConfig?.selectedFields as string[] | undefined;\n\n // Filter to numeric fields, and apply selectedFields filter if configured\n const keys = Object.keys(data[0]).filter((key) => {\n // Must not be the x-axis key and must be numeric\n const isValidNumericField = key !== chartConfig.xAxisKey && typeof data[0][key] === 'number';\n\n // If selectedFields is configured and not empty, only include those fields\n if (selectedFields && selectedFields.length > 0) {\n return isValidNumericField && selectedFields.includes(key);\n }\n\n // Otherwise, show all numeric fields\n return isValidNumericField;\n });\n\n return keys.map((key, index) => ({\n dataKey: key,\n name: chartConfig.seriesNames?.[key] || key,\n color: chartConfig.colors?.[index] || DEFAULT_COLORS[index % DEFAULT_COLORS.length],\n hidden: hiddenSeries.includes(key),\n }));\n }, [data, chartConfig, hiddenSeries, widgetConfig?.selectedFields]);\n\n // Toggle series visibility\n const handleLegendClick = useCallback((dataKey: string) => {\n setHiddenSeries((prev) => (prev.includes(dataKey) ? prev.filter((k) => k !== dataKey) : [...prev, dataKey]));\n }, []);\n\n // Handle data point click\n // Clicking a bar/slice triggers BOTH cross-filtering AND drilldown:\n // 1. Cross-filter: highlights the clicked item across all widgets\n // 2. Drilldown: opens the detail modal/page for the clicked item\n const handleDataClick = useCallback(\n (dataPoint: ChartData, index: number) => {\n // Trigger cross-filtering on data click when enabled\n if (crossFilterEnabled) {\n const label = dataPoint[widgetConfig?.labelField || 'name'] as string;\n triggerCrossFilter(dataPoint as Record<string, unknown>, label);\n }\n\n // Trigger drilldown if configured\n if (onDrilldown) {\n onDrilldown(dataPoint as Record<string, unknown>);\n } else if (onClick && widget.drilldowns?.length) {\n onClick(dataPoint, index);\n }\n },\n [onClick, onDrilldown, widget.drilldowns, crossFilterEnabled, triggerCrossFilter, widgetConfig?.labelField]\n );\n\n // A combo chart is requested explicitly, or implied by a dual axis / a\n // per-series config that mixes bar+line or binds a series to the right axis.\n const isCombo =\n chartConfig.type === 'combo' ||\n chartConfig.dualAxis === true ||\n (chartConfig.series?.some((s) => s.axis === 'right' || s.type === 'bar') &&\n chartConfig.series?.some((s) => s.type === 'line')) ||\n false;\n\n // Render the appropriate chart\n const renderChart = () => {\n if (isCombo) {\n return <ComboChart data={data} config={chartConfig} hiddenSeries={hiddenSeries} onDataClick={handleDataClick} />;\n }\n switch (chartConfig.type) {\n case 'waterfall':\n return <WaterfallChart data={data} config={chartConfig} onDataClick={handleDataClick} />;\n\n case 'scatter':\n return <ScatterChart data={data} config={chartConfig} onDataClick={handleDataClick} />;\n\n case 'bar':\n return <BarChart data={data} config={chartConfig} hiddenSeries={hiddenSeries} onDataClick={handleDataClick} />;\n\n case 'area':\n return <AreaChart data={data} config={chartConfig} hiddenSeries={hiddenSeries} onDataClick={handleDataClick} />;\n\n case 'pie':\n return (\n <PieChart\n data={data}\n config={chartConfig}\n dataKey={widgetConfig?.valueField || 'value'}\n nameKey={widgetConfig?.labelField || 'name'}\n onDataClick={handleDataClick}\n />\n );\n\n case 'funnel':\n return (\n <FunnelChart\n data={data}\n config={chartConfig}\n dataKey={widgetConfig?.valueField || 'value'}\n nameKey={widgetConfig?.labelField || 'name'}\n onDataClick={handleDataClick}\n />\n );\n\n case 'line':\n default:\n return <LineChart data={data} config={chartConfig} hiddenSeries={hiddenSeries} onDataClick={handleDataClick} />;\n }\n };\n\n return (\n <div className=\"flex flex-col h-full p-4\">\n {/* Cross-filter active indicator with clear button */}\n {hasCrossFilters && (\n <div className=\"flex items-center gap-2 mb-2 -mt-1\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n clearAllCrossFilters();\n }}\n className=\"\n inline-flex items-center gap-1.5\n px-2.5 py-1 rounded-md\n text-xs font-medium\n bg-action-primary-bg/10 text-action-primary-text\n hover:bg-action-primary-bg/20\n transition-colors\n \"\n >\n <svg className=\"w-3.5 h-3.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M10 19l-7-7m0 0l7-7m-7 7h18\" />\n </svg>\n Clear Filter\n </button>\n </div>\n )}\n\n {/* Custom legend (for line/bar/area charts with external legend) */}\n {chartConfig.legendPosition === 'top' && legendItems.length > 0 && (\n <ChartLegend items={legendItems} position=\"top\" onItemClick={handleLegendClick} />\n )}\n\n {/* Chart container */}\n <div className=\"flex-1 min-h-0\">{renderChart()}</div>\n\n {/* Custom legend (bottom) */}\n {chartConfig.legendPosition === 'bottom' && legendItems.length > 0 && (\n <ChartLegend items={legendItems} position=\"bottom\" onItemClick={handleLegendClick} />\n )}\n </div>\n );\n});\n\nexport default ChartWidget;\n"],"mappings":"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"}
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import e from "../ChartTooltip.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { buildReferenceElements as t } from "./referenceElements.js";
|
|
3
|
+
import { memo as n, useMemo as r } from "react";
|
|
4
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
5
|
+
import { Area as o, AreaChart as s, CartesianGrid as c, Legend as l, ResponsiveContainer as u, Tooltip as d, XAxis as f, YAxis as p } from "recharts";
|
|
5
6
|
//#region src/bigconsole/components/widgets/chart/charts/AreaChart.tsx
|
|
6
|
-
var
|
|
7
|
+
var m = [
|
|
7
8
|
"var(--color-chart-1)",
|
|
8
9
|
"var(--color-chart-2)",
|
|
9
10
|
"var(--color-chart-3)",
|
|
10
11
|
"var(--color-chart-4)",
|
|
11
12
|
"var(--color-chart-5)"
|
|
12
|
-
],
|
|
13
|
-
let
|
|
13
|
+
], h = n(function({ data: n = [], config: h, hiddenSeries: g = [], onDataClick: _ }) {
|
|
14
|
+
let v = r(() => n?.length ? Object.keys(n[0]).filter((e) => e !== h.xAxisKey && typeof n[0][e] == "number") : [], [n, h.xAxisKey]), y = (e) => typeof e == "number" ? new Intl.NumberFormat("en-US", {
|
|
14
15
|
notation: e >= 1e4 ? "compact" : "standard",
|
|
15
16
|
maximumFractionDigits: 2
|
|
16
|
-
}).format(e) : String(e),
|
|
17
|
-
return
|
|
17
|
+
}).format(e) : String(e), b = (e) => `gradient-${e}`;
|
|
18
|
+
return n?.length ? /* @__PURE__ */ i(u, {
|
|
18
19
|
width: "100%",
|
|
19
20
|
height: "100%",
|
|
20
|
-
children: /* @__PURE__ */
|
|
21
|
-
data:
|
|
21
|
+
children: /* @__PURE__ */ a(s, {
|
|
22
|
+
data: n,
|
|
22
23
|
margin: {
|
|
23
24
|
top: 10,
|
|
24
25
|
right: 10,
|
|
@@ -26,35 +27,35 @@ var p = [
|
|
|
26
27
|
bottom: 0
|
|
27
28
|
},
|
|
28
29
|
onClick: (e) => {
|
|
29
|
-
e?.activePayload?.[0] &&
|
|
30
|
+
e?.activePayload?.[0] && _ && _(e.activePayload[0].payload, e.activeTooltipIndex || 0);
|
|
30
31
|
},
|
|
31
32
|
children: [
|
|
32
|
-
/* @__PURE__ */
|
|
33
|
-
let n =
|
|
34
|
-
return /* @__PURE__ */
|
|
35
|
-
id:
|
|
33
|
+
/* @__PURE__ */ i("defs", { children: v.map((e, t) => {
|
|
34
|
+
let n = h.colors?.[t] || m[t % m.length];
|
|
35
|
+
return /* @__PURE__ */ a("linearGradient", {
|
|
36
|
+
id: b(e),
|
|
36
37
|
x1: "0",
|
|
37
38
|
y1: "0",
|
|
38
39
|
x2: "0",
|
|
39
40
|
y2: "1",
|
|
40
|
-
children: [/* @__PURE__ */
|
|
41
|
+
children: [/* @__PURE__ */ i("stop", {
|
|
41
42
|
offset: "0%",
|
|
42
43
|
stopColor: n,
|
|
43
44
|
stopOpacity: .4
|
|
44
|
-
}), /* @__PURE__ */
|
|
45
|
+
}), /* @__PURE__ */ i("stop", {
|
|
45
46
|
offset: "95%",
|
|
46
47
|
stopColor: n,
|
|
47
48
|
stopOpacity: .05
|
|
48
49
|
})]
|
|
49
50
|
}, e);
|
|
50
51
|
}) }),
|
|
51
|
-
|
|
52
|
+
h.showGrid !== !1 && /* @__PURE__ */ i(c, {
|
|
52
53
|
strokeDasharray: "3 3",
|
|
53
54
|
stroke: "var(--color-border-default)",
|
|
54
55
|
opacity: .5
|
|
55
56
|
}),
|
|
56
|
-
/* @__PURE__ */
|
|
57
|
-
dataKey:
|
|
57
|
+
/* @__PURE__ */ i(f, {
|
|
58
|
+
dataKey: h.xAxisKey || "name",
|
|
58
59
|
tick: {
|
|
59
60
|
fill: "var(--color-text-secondary)",
|
|
60
61
|
fontSize: 12
|
|
@@ -62,43 +63,47 @@ var p = [
|
|
|
62
63
|
tickLine: { stroke: "var(--color-border-default)" },
|
|
63
64
|
axisLine: { stroke: "var(--color-border-default)" }
|
|
64
65
|
}),
|
|
65
|
-
/* @__PURE__ */
|
|
66
|
+
/* @__PURE__ */ i(p, {
|
|
66
67
|
tick: {
|
|
67
68
|
fill: "var(--color-text-secondary)",
|
|
68
69
|
fontSize: 12
|
|
69
70
|
},
|
|
70
71
|
tickLine: { stroke: "var(--color-border-default)" },
|
|
71
72
|
axisLine: { stroke: "var(--color-border-default)" },
|
|
72
|
-
tickFormatter:
|
|
73
|
+
tickFormatter: y,
|
|
74
|
+
scale: h.logScale ? "log" : "auto",
|
|
75
|
+
domain: h.logScale ? ["auto", "auto"] : void 0,
|
|
76
|
+
allowDataOverflow: h.logScale || void 0
|
|
73
77
|
}),
|
|
74
|
-
/* @__PURE__ */
|
|
75
|
-
content: /* @__PURE__ */
|
|
78
|
+
/* @__PURE__ */ i(d, {
|
|
79
|
+
content: /* @__PURE__ */ i(e, { valueFormatter: y }),
|
|
76
80
|
cursor: { stroke: "var(--color-border-default)" }
|
|
77
81
|
}),
|
|
78
|
-
|
|
82
|
+
h.showLegend !== !1 && /* @__PURE__ */ i(l, {
|
|
79
83
|
wrapperStyle: { paddingTop: 20 },
|
|
80
|
-
formatter: (e) => /* @__PURE__ */
|
|
84
|
+
formatter: (e) => /* @__PURE__ */ i("span", {
|
|
81
85
|
style: { color: "var(--color-text-primary)" },
|
|
82
86
|
children: e
|
|
83
87
|
})
|
|
84
88
|
}),
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
t(h.referenceLines),
|
|
90
|
+
v.map((e, t) => /* @__PURE__ */ i(o, {
|
|
91
|
+
type: h.curveType || "monotone",
|
|
87
92
|
dataKey: e,
|
|
88
|
-
name:
|
|
89
|
-
stroke:
|
|
93
|
+
name: h.seriesNames?.[e] || e,
|
|
94
|
+
stroke: h.colors?.[t] || m[t % m.length],
|
|
90
95
|
strokeWidth: 2,
|
|
91
|
-
fill: `url(#${
|
|
92
|
-
dot:
|
|
96
|
+
fill: `url(#${b(e)})`,
|
|
97
|
+
dot: h.showDots,
|
|
93
98
|
activeDot: { r: 6 },
|
|
94
|
-
hide:
|
|
95
|
-
stackId:
|
|
99
|
+
hide: g.includes(e),
|
|
100
|
+
stackId: h.stacked ? "stack" : void 0
|
|
96
101
|
}, e))
|
|
97
102
|
]
|
|
98
103
|
})
|
|
99
104
|
}) : null;
|
|
100
105
|
});
|
|
101
106
|
//#endregion
|
|
102
|
-
export {
|
|
107
|
+
export { h as default };
|
|
103
108
|
|
|
104
109
|
//# sourceMappingURL=AreaChart.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AreaChart.js","names":[],"sources":["../../../../../../src/bigconsole/components/widgets/chart/charts/AreaChart.tsx"],"sourcesContent":["/**\n * AreaChart Component\n *\n * Area chart using Recharts.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport {\n AreaChart as RechartsAreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveContainer,\n Legend,\n} from 'recharts';\nimport { ChartTooltip } from '../ChartTooltip';\nimport type { ChartConfig, ChartData } from './types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface AreaChartProps {\n /** Chart data */\n data: ChartData[];\n /** Chart configuration */\n config: ChartConfig;\n /** Series to hide */\n hiddenSeries?: string[];\n /** Click handler for data points */\n onDataClick?: (data: ChartData, index: number) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const AreaChart: FC<AreaChartProps> = memo(function AreaChart({\n data = [],\n config,\n hiddenSeries = [],\n onDataClick,\n}) {\n // Extract data keys (series)\n const dataKeys = useMemo(() => {\n if (!data?.length) return [];\n const keys = Object.keys(data[0]).filter((key) => key !== config.xAxisKey && typeof data[0][key] === 'number');\n return keys;\n }, [data, config.xAxisKey]);\n\n // Format value\n const formatValue = (value: number | string): string => {\n if (typeof value !== 'number') return String(value);\n return new Intl.NumberFormat('en-US', {\n notation: value >= 10000 ? 'compact' : 'standard',\n maximumFractionDigits: 2,\n }).format(value);\n };\n\n // Generate gradient ID\n const getGradientId = (key: string) => `gradient-${key}`;\n\n if (!data?.length) {\n return null;\n }\n\n return (\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <RechartsAreaChart\n data={data}\n margin={{ top: 10, right: 10, left: 0, bottom: 0 }}\n onClick={(e) => {\n if (e?.activePayload?.[0] && onDataClick) {\n onDataClick(e.activePayload[0].payload, e.activeTooltipIndex || 0);\n }\n }}\n >\n {/* Gradients */}\n <defs>\n {dataKeys.map((key, index) => {\n const color = config.colors?.[index] || DEFAULT_COLORS[index % DEFAULT_COLORS.length];\n return (\n <linearGradient key={key} id={getGradientId(key)} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={color} stopOpacity={0.4} />\n <stop offset=\"95%\" stopColor={color} stopOpacity={0.05} />\n </linearGradient>\n );\n })}\n </defs>\n\n {config.showGrid !== false && (\n <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--color-border-default)\" opacity={0.5} />\n )}\n\n <XAxis\n dataKey={config.xAxisKey || 'name'}\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n tickLine={{ stroke: 'var(--color-border-default)' }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n />\n\n <YAxis\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n tickLine={{ stroke: 'var(--color-border-default)' }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n tickFormatter={formatValue}\n />\n\n <Tooltip\n content={<ChartTooltip valueFormatter={formatValue} />}\n cursor={{ stroke: 'var(--color-border-default)' }}\n />\n\n {config.showLegend !== false && (\n <Legend\n wrapperStyle={{ paddingTop: 20 }}\n formatter={(value) => <span style={{ color: 'var(--color-text-primary)' }}>{value}</span>}\n />\n )}\n\n {dataKeys.map((key, index) => (\n <Area\n key={key}\n type={config.curveType || 'monotone'}\n dataKey={key}\n name={config.seriesNames?.[key] || key}\n stroke={config.colors?.[index] || DEFAULT_COLORS[index % DEFAULT_COLORS.length]}\n strokeWidth={2}\n fill={`url(#${getGradientId(key)})`}\n dot={config.showDots}\n activeDot={{ r: 6 }}\n hide={hiddenSeries.includes(key)}\n stackId={config.stacked ? 'stack' : undefined}\n />\n ))}\n </RechartsAreaChart>\n </ResponsiveContainer>\n );\n});\n\nexport default AreaChart;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"AreaChart.js","names":[],"sources":["../../../../../../src/bigconsole/components/widgets/chart/charts/AreaChart.tsx"],"sourcesContent":["/**\n * AreaChart Component\n *\n * Area chart using Recharts.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport {\n AreaChart as RechartsAreaChart,\n Area,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveContainer,\n Legend,\n} from 'recharts';\nimport { ChartTooltip } from '../ChartTooltip';\nimport type { ChartConfig, ChartData } from './types';\nimport { buildReferenceElements } from './referenceElements';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface AreaChartProps {\n /** Chart data */\n data: ChartData[];\n /** Chart configuration */\n config: ChartConfig;\n /** Series to hide */\n hiddenSeries?: string[];\n /** Click handler for data points */\n onDataClick?: (data: ChartData, index: number) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const AreaChart: FC<AreaChartProps> = memo(function AreaChart({\n data = [],\n config,\n hiddenSeries = [],\n onDataClick,\n}) {\n // Extract data keys (series)\n const dataKeys = useMemo(() => {\n if (!data?.length) return [];\n const keys = Object.keys(data[0]).filter((key) => key !== config.xAxisKey && typeof data[0][key] === 'number');\n return keys;\n }, [data, config.xAxisKey]);\n\n // Format value\n const formatValue = (value: number | string): string => {\n if (typeof value !== 'number') return String(value);\n return new Intl.NumberFormat('en-US', {\n notation: value >= 10000 ? 'compact' : 'standard',\n maximumFractionDigits: 2,\n }).format(value);\n };\n\n // Generate gradient ID\n const getGradientId = (key: string) => `gradient-${key}`;\n\n if (!data?.length) {\n return null;\n }\n\n return (\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <RechartsAreaChart\n data={data}\n margin={{ top: 10, right: 10, left: 0, bottom: 0 }}\n onClick={(e) => {\n if (e?.activePayload?.[0] && onDataClick) {\n onDataClick(e.activePayload[0].payload, e.activeTooltipIndex || 0);\n }\n }}\n >\n {/* Gradients */}\n <defs>\n {dataKeys.map((key, index) => {\n const color = config.colors?.[index] || DEFAULT_COLORS[index % DEFAULT_COLORS.length];\n return (\n <linearGradient key={key} id={getGradientId(key)} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={color} stopOpacity={0.4} />\n <stop offset=\"95%\" stopColor={color} stopOpacity={0.05} />\n </linearGradient>\n );\n })}\n </defs>\n\n {config.showGrid !== false && (\n <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--color-border-default)\" opacity={0.5} />\n )}\n\n <XAxis\n dataKey={config.xAxisKey || 'name'}\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n tickLine={{ stroke: 'var(--color-border-default)' }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n />\n\n <YAxis\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n tickLine={{ stroke: 'var(--color-border-default)' }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n tickFormatter={formatValue}\n scale={config.logScale ? 'log' : 'auto'}\n domain={config.logScale ? ['auto', 'auto'] : undefined}\n allowDataOverflow={config.logScale || undefined}\n />\n\n <Tooltip\n content={<ChartTooltip valueFormatter={formatValue} />}\n cursor={{ stroke: 'var(--color-border-default)' }}\n />\n\n {config.showLegend !== false && (\n <Legend\n wrapperStyle={{ paddingTop: 20 }}\n formatter={(value) => <span style={{ color: 'var(--color-text-primary)' }}>{value}</span>}\n />\n )}\n\n {buildReferenceElements(config.referenceLines)}\n\n {dataKeys.map((key, index) => (\n <Area\n key={key}\n type={config.curveType || 'monotone'}\n dataKey={key}\n name={config.seriesNames?.[key] || key}\n stroke={config.colors?.[index] || DEFAULT_COLORS[index % DEFAULT_COLORS.length]}\n strokeWidth={2}\n fill={`url(#${getGradientId(key)})`}\n dot={config.showDots}\n activeDot={{ r: 6 }}\n hide={hiddenSeries.includes(key)}\n stackId={config.stacked ? 'stack' : undefined}\n />\n ))}\n </RechartsAreaChart>\n </ResponsiveContainer>\n );\n});\n\nexport default AreaChart;\n"],"mappings":"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"}
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import e from "../ChartTooltip.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { buildReferenceElements as t } from "./referenceElements.js";
|
|
3
|
+
import { memo as n, useMemo as r } from "react";
|
|
4
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
5
|
+
import { Bar as o, BarChart as s, CartesianGrid as c, Legend as l, ResponsiveContainer as u, Tooltip as d, XAxis as f, YAxis as p } from "recharts";
|
|
5
6
|
//#region src/bigconsole/components/widgets/chart/charts/BarChart.tsx
|
|
6
|
-
var
|
|
7
|
+
var m = [
|
|
7
8
|
"var(--color-chart-1)",
|
|
8
9
|
"var(--color-chart-2)",
|
|
9
10
|
"var(--color-chart-3)",
|
|
10
11
|
"var(--color-chart-4)",
|
|
11
12
|
"var(--color-chart-5)"
|
|
12
|
-
],
|
|
13
|
-
let
|
|
13
|
+
], h = n(function({ data: n = [], config: h, hiddenSeries: g = [], onDataClick: _ }) {
|
|
14
|
+
let v = r(() => n?.length ? Object.keys(n[0]).filter((e) => e !== h.xAxisKey && typeof n[0][e] == "number") : [], [n, h.xAxisKey]), y = (e) => typeof e == "number" ? new Intl.NumberFormat("en-US", {
|
|
14
15
|
notation: e >= 1e4 ? "compact" : "standard",
|
|
15
16
|
maximumFractionDigits: 2
|
|
16
17
|
}).format(e) : String(e);
|
|
17
|
-
return
|
|
18
|
+
return n?.length ? /* @__PURE__ */ i(u, {
|
|
18
19
|
width: "100%",
|
|
19
20
|
height: "100%",
|
|
20
|
-
children: /* @__PURE__ */
|
|
21
|
-
data:
|
|
21
|
+
children: /* @__PURE__ */ a(s, {
|
|
22
|
+
data: n,
|
|
22
23
|
margin: {
|
|
23
24
|
top: 10,
|
|
24
25
|
right: 10,
|
|
@@ -26,17 +27,17 @@ var p = [
|
|
|
26
27
|
bottom: 0
|
|
27
28
|
},
|
|
28
29
|
onClick: (e) => {
|
|
29
|
-
e?.activePayload?.[0] &&
|
|
30
|
+
e?.activePayload?.[0] && _ && _(e.activePayload[0].payload, e.activeTooltipIndex || 0);
|
|
30
31
|
},
|
|
31
32
|
children: [
|
|
32
|
-
|
|
33
|
+
h.showGrid !== !1 && /* @__PURE__ */ i(c, {
|
|
33
34
|
strokeDasharray: "3 3",
|
|
34
35
|
stroke: "var(--color-border-default)",
|
|
35
36
|
opacity: .5,
|
|
36
37
|
vertical: !1
|
|
37
38
|
}),
|
|
38
|
-
/* @__PURE__ */
|
|
39
|
-
dataKey:
|
|
39
|
+
/* @__PURE__ */ i(f, {
|
|
40
|
+
dataKey: h.xAxisKey || "name",
|
|
40
41
|
tick: {
|
|
41
42
|
fill: "var(--color-text-secondary)",
|
|
42
43
|
fontSize: 12
|
|
@@ -44,47 +45,51 @@ var p = [
|
|
|
44
45
|
tickLine: { stroke: "var(--color-border-default)" },
|
|
45
46
|
axisLine: { stroke: "var(--color-border-default)" }
|
|
46
47
|
}),
|
|
47
|
-
/* @__PURE__ */
|
|
48
|
+
/* @__PURE__ */ i(p, {
|
|
48
49
|
tick: {
|
|
49
50
|
fill: "var(--color-text-secondary)",
|
|
50
51
|
fontSize: 12
|
|
51
52
|
},
|
|
52
53
|
tickLine: { stroke: "var(--color-border-default)" },
|
|
53
54
|
axisLine: { stroke: "var(--color-border-default)" },
|
|
54
|
-
tickFormatter:
|
|
55
|
+
tickFormatter: y,
|
|
56
|
+
scale: h.logScale ? "log" : "auto",
|
|
57
|
+
domain: h.logScale ? ["auto", "auto"] : void 0,
|
|
58
|
+
allowDataOverflow: h.logScale || void 0
|
|
55
59
|
}),
|
|
56
|
-
/* @__PURE__ */
|
|
57
|
-
content: /* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ i(d, {
|
|
61
|
+
content: /* @__PURE__ */ i(e, { valueFormatter: y }),
|
|
58
62
|
cursor: {
|
|
59
63
|
fill: "var(--color-bg-muted)",
|
|
60
64
|
opacity: .5
|
|
61
65
|
}
|
|
62
66
|
}),
|
|
63
|
-
|
|
67
|
+
h.showLegend !== !1 && /* @__PURE__ */ i(l, {
|
|
64
68
|
wrapperStyle: { paddingTop: 20 },
|
|
65
|
-
formatter: (e) => /* @__PURE__ */
|
|
69
|
+
formatter: (e) => /* @__PURE__ */ i("span", {
|
|
66
70
|
style: { color: "var(--color-text-primary)" },
|
|
67
71
|
children: e
|
|
68
72
|
})
|
|
69
73
|
}),
|
|
70
|
-
|
|
74
|
+
t(h.referenceLines),
|
|
75
|
+
v.map((e, t) => /* @__PURE__ */ i(o, {
|
|
71
76
|
dataKey: e,
|
|
72
|
-
name:
|
|
73
|
-
fill:
|
|
77
|
+
name: h.seriesNames?.[e] || e,
|
|
78
|
+
fill: h.colors?.[t] || m[t % m.length],
|
|
74
79
|
radius: [
|
|
75
80
|
4,
|
|
76
81
|
4,
|
|
77
82
|
0,
|
|
78
83
|
0
|
|
79
84
|
],
|
|
80
|
-
hide:
|
|
81
|
-
stackId:
|
|
85
|
+
hide: g.includes(e),
|
|
86
|
+
stackId: h.stacked ? "stack" : void 0
|
|
82
87
|
}, e))
|
|
83
88
|
]
|
|
84
89
|
})
|
|
85
90
|
}) : null;
|
|
86
91
|
});
|
|
87
92
|
//#endregion
|
|
88
|
-
export {
|
|
93
|
+
export { h as default };
|
|
89
94
|
|
|
90
95
|
//# sourceMappingURL=BarChart.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BarChart.js","names":[],"sources":["../../../../../../src/bigconsole/components/widgets/chart/charts/BarChart.tsx"],"sourcesContent":["/**\n * BarChart Component\n *\n * Bar chart using Recharts.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport {\n BarChart as RechartsBarChart,\n Bar,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveContainer,\n Legend,\n} from 'recharts';\nimport { ChartTooltip } from '../ChartTooltip';\nimport type { ChartConfig, ChartData } from './types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BarChartProps {\n /** Chart data */\n data: ChartData[];\n /** Chart configuration */\n config: ChartConfig;\n /** Series to hide */\n hiddenSeries?: string[];\n /** Click handler for bars */\n onDataClick?: (data: ChartData, index: number) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const BarChart: FC<BarChartProps> = memo(function BarChart({\n data = [],\n config,\n hiddenSeries = [],\n onDataClick,\n}) {\n // Extract data keys (series)\n const dataKeys = useMemo(() => {\n if (!data?.length) return [];\n const keys = Object.keys(data[0]).filter((key) => key !== config.xAxisKey && typeof data[0][key] === 'number');\n return keys;\n }, [data, config.xAxisKey]);\n\n // Format value\n const formatValue = (value: number | string): string => {\n if (typeof value !== 'number') return String(value);\n return new Intl.NumberFormat('en-US', {\n notation: value >= 10000 ? 'compact' : 'standard',\n maximumFractionDigits: 2,\n }).format(value);\n };\n\n if (!data?.length) {\n return null;\n }\n\n return (\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <RechartsBarChart\n data={data}\n margin={{ top: 10, right: 10, left: 0, bottom: 0 }}\n onClick={(e) => {\n if (e?.activePayload?.[0] && onDataClick) {\n onDataClick(e.activePayload[0].payload, e.activeTooltipIndex || 0);\n }\n }}\n >\n {config.showGrid !== false && (\n <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--color-border-default)\" opacity={0.5} vertical={false} />\n )}\n\n <XAxis\n dataKey={config.xAxisKey || 'name'}\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n tickLine={{ stroke: 'var(--color-border-default)' }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n />\n\n <YAxis\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n tickLine={{ stroke: 'var(--color-border-default)' }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n tickFormatter={formatValue}\n />\n\n <Tooltip\n content={<ChartTooltip valueFormatter={formatValue} />}\n cursor={{ fill: 'var(--color-bg-muted)', opacity: 0.5 }}\n />\n\n {config.showLegend !== false && (\n <Legend\n wrapperStyle={{ paddingTop: 20 }}\n formatter={(value) => <span style={{ color: 'var(--color-text-primary)' }}>{value}</span>}\n />\n )}\n\n {dataKeys.map((key, index) => (\n <Bar\n key={key}\n dataKey={key}\n name={config.seriesNames?.[key] || key}\n fill={config.colors?.[index] || DEFAULT_COLORS[index % DEFAULT_COLORS.length]}\n radius={[4, 4, 0, 0]}\n hide={hiddenSeries.includes(key)}\n stackId={config.stacked ? 'stack' : undefined}\n />\n ))}\n </RechartsBarChart>\n </ResponsiveContainer>\n );\n});\n\nexport default BarChart;\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"BarChart.js","names":[],"sources":["../../../../../../src/bigconsole/components/widgets/chart/charts/BarChart.tsx"],"sourcesContent":["/**\n * BarChart Component\n *\n * Bar chart using Recharts.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport {\n BarChart as RechartsBarChart,\n Bar,\n XAxis,\n YAxis,\n CartesianGrid,\n Tooltip,\n ResponsiveContainer,\n Legend,\n} from 'recharts';\nimport { ChartTooltip } from '../ChartTooltip';\nimport type { ChartConfig, ChartData } from './types';\nimport { buildReferenceElements } from './referenceElements';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface BarChartProps {\n /** Chart data */\n data: ChartData[];\n /** Chart configuration */\n config: ChartConfig;\n /** Series to hide */\n hiddenSeries?: string[];\n /** Click handler for bars */\n onDataClick?: (data: ChartData, index: number) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const BarChart: FC<BarChartProps> = memo(function BarChart({\n data = [],\n config,\n hiddenSeries = [],\n onDataClick,\n}) {\n // Extract data keys (series)\n const dataKeys = useMemo(() => {\n if (!data?.length) return [];\n const keys = Object.keys(data[0]).filter((key) => key !== config.xAxisKey && typeof data[0][key] === 'number');\n return keys;\n }, [data, config.xAxisKey]);\n\n // Format value\n const formatValue = (value: number | string): string => {\n if (typeof value !== 'number') return String(value);\n return new Intl.NumberFormat('en-US', {\n notation: value >= 10000 ? 'compact' : 'standard',\n maximumFractionDigits: 2,\n }).format(value);\n };\n\n if (!data?.length) {\n return null;\n }\n\n return (\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <RechartsBarChart\n data={data}\n margin={{ top: 10, right: 10, left: 0, bottom: 0 }}\n onClick={(e) => {\n if (e?.activePayload?.[0] && onDataClick) {\n onDataClick(e.activePayload[0].payload, e.activeTooltipIndex || 0);\n }\n }}\n >\n {config.showGrid !== false && (\n <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--color-border-default)\" opacity={0.5} vertical={false} />\n )}\n\n <XAxis\n dataKey={config.xAxisKey || 'name'}\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n tickLine={{ stroke: 'var(--color-border-default)' }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n />\n\n <YAxis\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n tickLine={{ stroke: 'var(--color-border-default)' }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n tickFormatter={formatValue}\n scale={config.logScale ? 'log' : 'auto'}\n domain={config.logScale ? ['auto', 'auto'] : undefined}\n allowDataOverflow={config.logScale || undefined}\n />\n\n <Tooltip\n content={<ChartTooltip valueFormatter={formatValue} />}\n cursor={{ fill: 'var(--color-bg-muted)', opacity: 0.5 }}\n />\n\n {config.showLegend !== false && (\n <Legend\n wrapperStyle={{ paddingTop: 20 }}\n formatter={(value) => <span style={{ color: 'var(--color-text-primary)' }}>{value}</span>}\n />\n )}\n\n {buildReferenceElements(config.referenceLines)}\n\n {dataKeys.map((key, index) => (\n <Bar\n key={key}\n dataKey={key}\n name={config.seriesNames?.[key] || key}\n fill={config.colors?.[index] || DEFAULT_COLORS[index % DEFAULT_COLORS.length]}\n radius={[4, 4, 0, 0]}\n hide={hiddenSeries.includes(key)}\n stackId={config.stacked ? 'stack' : undefined}\n />\n ))}\n </RechartsBarChart>\n </ResponsiveContainer>\n );\n});\n\nexport default BarChart;\n"],"mappings":";;;;;;AAwCA,IAAM,IAAiB;CACrB;CACA;CACA;CACA;CACA;CACD,EAMY,IAA8B,EAAK,SAAkB,EAChE,UAAO,EAAE,EACT,WACA,kBAAe,EAAE,EACjB,kBACC;CAED,IAAM,IAAW,QACV,GAAM,SACE,OAAO,KAAK,EAAK,GAAG,CAAC,QAAQ,MAAQ,MAAQ,EAAO,YAAY,OAAO,EAAK,GAAG,MAAS,SAAS,GADpF,EAAE,EAG3B,CAAC,GAAM,EAAO,SAAS,CAAC,EAGrB,KAAe,MACf,OAAO,KAAU,WACd,IAAI,KAAK,aAAa,SAAS;EACpC,UAAU,KAAS,MAAQ,YAAY;EACvC,uBAAuB;EACxB,CAAC,CAAC,OAAO,EAAM,GAJsB,OAAO,EAAM;AAWrD,QAJK,GAAM,SAKT,kBAAC,GAAD;EAAqB,OAAM;EAAO,QAAO;YACvC,kBAAC,GAAD;GACQ;GACN,QAAQ;IAAE,KAAK;IAAI,OAAO;IAAI,MAAM;IAAG,QAAQ;IAAG;GAClD,UAAU,MAAM;AACd,IAAI,GAAG,gBAAgB,MAAM,KAC3B,EAAY,EAAE,cAAc,GAAG,SAAS,EAAE,sBAAsB,EAAE;;aALxE;IASG,EAAO,aAAa,MACnB,kBAAC,GAAD;KAAe,iBAAgB;KAAM,QAAO;KAA8B,SAAS;KAAK,UAAU;KAAS,CAAA;IAG7G,kBAAC,GAAD;KACE,SAAS,EAAO,YAAY;KAC5B,MAAM;MAAE,MAAM;MAA+B,UAAU;MAAI;KAC3D,UAAU,EAAE,QAAQ,+BAA+B;KACnD,UAAU,EAAE,QAAQ,+BAA+B;KACnD,CAAA;IAEF,kBAAC,GAAD;KACE,MAAM;MAAE,MAAM;MAA+B,UAAU;MAAI;KAC3D,UAAU,EAAE,QAAQ,+BAA+B;KACnD,UAAU,EAAE,QAAQ,+BAA+B;KACnD,eAAe;KACf,OAAO,EAAO,WAAW,QAAQ;KACjC,QAAQ,EAAO,WAAW,CAAC,QAAQ,OAAO,GAAG,KAAA;KAC7C,mBAAmB,EAAO,YAAY,KAAA;KACtC,CAAA;IAEF,kBAAC,GAAD;KACE,SAAS,kBAAC,GAAD,EAAc,gBAAgB,GAAe,CAAA;KACtD,QAAQ;MAAE,MAAM;MAAyB,SAAS;MAAK;KACvD,CAAA;IAED,EAAO,eAAe,MACrB,kBAAC,GAAD;KACE,cAAc,EAAE,YAAY,IAAI;KAChC,YAAY,MAAU,kBAAC,QAAD;MAAM,OAAO,EAAE,OAAO,6BAA6B;gBAAG;MAAa,CAAA;KACzF,CAAA;IAGH,EAAuB,EAAO,eAAe;IAE7C,EAAS,KAAK,GAAK,MAClB,kBAAC,GAAD;KAEE,SAAS;KACT,MAAM,EAAO,cAAc,MAAQ;KACnC,MAAM,EAAO,SAAS,MAAU,EAAe,IAAQ,EAAe;KACtE,QAAQ;MAAC;MAAG;MAAG;MAAG;MAAE;KACpB,MAAM,EAAa,SAAS,EAAI;KAChC,SAAS,EAAO,UAAU,UAAU,KAAA;KACpC,EAPK,EAOL,CACF;IACe;;EACC,CAAA,GA7Df;EA+DT"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import e from "../ChartTooltip.js";
|
|
2
|
+
import { buildReferenceElements as t } from "./referenceElements.js";
|
|
3
|
+
import { memo as n, useMemo as r } from "react";
|
|
4
|
+
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
5
|
+
import { Bar as o, CartesianGrid as s, ComposedChart as c, Legend as l, Line as u, ResponsiveContainer as d, Tooltip as f, XAxis as p, YAxis as m } from "recharts";
|
|
6
|
+
//#region src/bigconsole/components/widgets/chart/charts/ComboChart.tsx
|
|
7
|
+
var h = [
|
|
8
|
+
"var(--color-chart-1)",
|
|
9
|
+
"var(--color-chart-2)",
|
|
10
|
+
"var(--color-chart-3)",
|
|
11
|
+
"var(--color-chart-4)",
|
|
12
|
+
"var(--color-chart-5)"
|
|
13
|
+
], g = n(function({ data: n = [], config: g, hiddenSeries: _ = [], onDataClick: v }) {
|
|
14
|
+
let y = r(() => g.series?.length ? g.series : n.length ? Object.keys(n[0]).filter((e) => e !== g.xAxisKey && typeof n[0][e] == "number").map((e, t) => ({
|
|
15
|
+
field: e,
|
|
16
|
+
type: t === 0 ? "bar" : "line",
|
|
17
|
+
axis: t === 0 ? "left" : "right"
|
|
18
|
+
})) : [], [
|
|
19
|
+
g.series,
|
|
20
|
+
n,
|
|
21
|
+
g.xAxisKey
|
|
22
|
+
]), b = r(() => y.some((e) => e.axis === "right"), [y]), x = (e) => typeof e == "number" ? new Intl.NumberFormat("en-US", {
|
|
23
|
+
notation: e >= 1e4 ? "compact" : "standard",
|
|
24
|
+
maximumFractionDigits: 2
|
|
25
|
+
}).format(e) : String(e);
|
|
26
|
+
return n?.length ? /* @__PURE__ */ i(d, {
|
|
27
|
+
width: "100%",
|
|
28
|
+
height: "100%",
|
|
29
|
+
children: /* @__PURE__ */ a(c, {
|
|
30
|
+
data: n,
|
|
31
|
+
margin: {
|
|
32
|
+
top: 10,
|
|
33
|
+
right: 10,
|
|
34
|
+
left: 0,
|
|
35
|
+
bottom: 0
|
|
36
|
+
},
|
|
37
|
+
onClick: (e) => {
|
|
38
|
+
e?.activePayload?.[0] && v && v(e.activePayload[0].payload, e.activeTooltipIndex || 0);
|
|
39
|
+
},
|
|
40
|
+
children: [
|
|
41
|
+
g.showGrid !== !1 && /* @__PURE__ */ i(s, {
|
|
42
|
+
strokeDasharray: "3 3",
|
|
43
|
+
stroke: "var(--color-border-default)",
|
|
44
|
+
opacity: .5,
|
|
45
|
+
vertical: !1
|
|
46
|
+
}),
|
|
47
|
+
/* @__PURE__ */ i(p, {
|
|
48
|
+
dataKey: g.xAxisKey || "name",
|
|
49
|
+
tick: {
|
|
50
|
+
fill: "var(--color-text-secondary)",
|
|
51
|
+
fontSize: 12
|
|
52
|
+
},
|
|
53
|
+
tickLine: { stroke: "var(--color-border-default)" },
|
|
54
|
+
axisLine: { stroke: "var(--color-border-default)" }
|
|
55
|
+
}),
|
|
56
|
+
/* @__PURE__ */ i(m, {
|
|
57
|
+
yAxisId: "left",
|
|
58
|
+
tick: {
|
|
59
|
+
fill: "var(--color-text-secondary)",
|
|
60
|
+
fontSize: 12
|
|
61
|
+
},
|
|
62
|
+
tickLine: { stroke: "var(--color-border-default)" },
|
|
63
|
+
axisLine: { stroke: "var(--color-border-default)" },
|
|
64
|
+
tickFormatter: x,
|
|
65
|
+
label: g.yAxisLabel ? {
|
|
66
|
+
value: g.yAxisLabel,
|
|
67
|
+
angle: -90,
|
|
68
|
+
position: "insideLeft",
|
|
69
|
+
fill: "var(--color-text-secondary)"
|
|
70
|
+
} : void 0
|
|
71
|
+
}),
|
|
72
|
+
b && /* @__PURE__ */ i(m, {
|
|
73
|
+
yAxisId: "right",
|
|
74
|
+
orientation: "right",
|
|
75
|
+
tick: {
|
|
76
|
+
fill: "var(--color-text-secondary)",
|
|
77
|
+
fontSize: 12
|
|
78
|
+
},
|
|
79
|
+
tickLine: { stroke: "var(--color-border-default)" },
|
|
80
|
+
axisLine: { stroke: "var(--color-border-default)" },
|
|
81
|
+
tickFormatter: x,
|
|
82
|
+
label: g.yAxisRightLabel ? {
|
|
83
|
+
value: g.yAxisRightLabel,
|
|
84
|
+
angle: 90,
|
|
85
|
+
position: "insideRight",
|
|
86
|
+
fill: "var(--color-text-secondary)"
|
|
87
|
+
} : void 0
|
|
88
|
+
}),
|
|
89
|
+
/* @__PURE__ */ i(f, {
|
|
90
|
+
content: /* @__PURE__ */ i(e, { valueFormatter: x }),
|
|
91
|
+
cursor: {
|
|
92
|
+
fill: "var(--color-bg-muted)",
|
|
93
|
+
opacity: .5
|
|
94
|
+
}
|
|
95
|
+
}),
|
|
96
|
+
g.showLegend !== !1 && /* @__PURE__ */ i(l, {
|
|
97
|
+
wrapperStyle: { paddingTop: 20 },
|
|
98
|
+
formatter: (e) => /* @__PURE__ */ i("span", {
|
|
99
|
+
style: { color: "var(--color-text-primary)" },
|
|
100
|
+
children: e
|
|
101
|
+
})
|
|
102
|
+
}),
|
|
103
|
+
y.map((e, t) => {
|
|
104
|
+
let n = e.color || g.colors?.[t] || h[t % h.length], r = e.axis === "right" && b ? "right" : "left", a = e.label || g.seriesNames?.[e.field] || e.field, s = _.includes(e.field);
|
|
105
|
+
return e.type === "line" ? /* @__PURE__ */ i(u, {
|
|
106
|
+
yAxisId: r,
|
|
107
|
+
type: g.curveType || "monotone",
|
|
108
|
+
dataKey: e.field,
|
|
109
|
+
name: a,
|
|
110
|
+
stroke: n,
|
|
111
|
+
strokeWidth: 2,
|
|
112
|
+
dot: g.showDots !== !1,
|
|
113
|
+
activeDot: { r: 6 },
|
|
114
|
+
hide: s,
|
|
115
|
+
connectNulls: !0
|
|
116
|
+
}, e.field) : /* @__PURE__ */ i(o, {
|
|
117
|
+
yAxisId: r,
|
|
118
|
+
dataKey: e.field,
|
|
119
|
+
name: a,
|
|
120
|
+
fill: n,
|
|
121
|
+
radius: [
|
|
122
|
+
4,
|
|
123
|
+
4,
|
|
124
|
+
0,
|
|
125
|
+
0
|
|
126
|
+
],
|
|
127
|
+
hide: s,
|
|
128
|
+
stackId: g.stacked ? "stack" : void 0
|
|
129
|
+
}, e.field);
|
|
130
|
+
}),
|
|
131
|
+
t(g.referenceLines, { yAxisId: "left" })
|
|
132
|
+
]
|
|
133
|
+
})
|
|
134
|
+
}) : null;
|
|
135
|
+
});
|
|
136
|
+
//#endregion
|
|
137
|
+
export { g as default };
|
|
138
|
+
|
|
139
|
+
//# sourceMappingURL=ComboChart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ComboChart.js","names":[],"sources":["../../../../../../src/bigconsole/components/widgets/chart/charts/ComboChart.tsx"],"sourcesContent":["/**\n * ComboChart Component\n *\n * Combo / dual-axis chart (mix of bars and lines, optional secondary Y-axis)\n * using Recharts `ComposedChart`. Drives the marketed\n * \"spend (bars) vs conversions (line)\" dual-axis view.\n */\n\nimport { type FC, memo, useMemo } from 'react';\nimport { ComposedChart, Bar, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts';\nimport { ChartTooltip } from '../ChartTooltip';\nimport type { ChartConfig, ChartData, SeriesConfig } from './types';\nimport { buildReferenceElements } from './referenceElements';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ComboChartProps {\n data: ChartData[];\n config: ChartConfig;\n hiddenSeries?: string[];\n onDataClick?: (data: ChartData, index: number) => void;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ComboChart: FC<ComboChartProps> = memo(function ComboChart({\n data = [],\n config,\n hiddenSeries = [],\n onDataClick,\n}) {\n // Resolve series: explicit config wins; otherwise derive from numeric keys\n // (first → bar on the left axis, the rest → lines on the right axis).\n const series: SeriesConfig[] = useMemo(() => {\n if (config.series?.length) return config.series;\n if (!data.length) return [];\n const keys = Object.keys(data[0]).filter((k) => k !== config.xAxisKey && typeof data[0][k] === 'number');\n return keys.map((field, i) => ({\n field,\n type: i === 0 ? 'bar' : 'line',\n axis: i === 0 ? 'left' : 'right',\n }));\n }, [config.series, data, config.xAxisKey]);\n\n const hasRightAxis = useMemo(() => series.some((s) => s.axis === 'right'), [series]);\n\n const formatValue = (value: number | string): string => {\n if (typeof value !== 'number') return String(value);\n return new Intl.NumberFormat('en-US', {\n notation: value >= 10000 ? 'compact' : 'standard',\n maximumFractionDigits: 2,\n }).format(value);\n };\n\n if (!data?.length) {\n return null;\n }\n\n return (\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <ComposedChart\n data={data}\n margin={{ top: 10, right: 10, left: 0, bottom: 0 }}\n onClick={(e) => {\n if (e?.activePayload?.[0] && onDataClick) {\n onDataClick(e.activePayload[0].payload, e.activeTooltipIndex || 0);\n }\n }}\n >\n {config.showGrid !== false && (\n <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--color-border-default)\" opacity={0.5} vertical={false} />\n )}\n\n <XAxis\n dataKey={config.xAxisKey || 'name'}\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n tickLine={{ stroke: 'var(--color-border-default)' }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n />\n\n <YAxis\n yAxisId=\"left\"\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n tickLine={{ stroke: 'var(--color-border-default)' }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n tickFormatter={formatValue}\n label={\n config.yAxisLabel\n ? { value: config.yAxisLabel, angle: -90, position: 'insideLeft', fill: 'var(--color-text-secondary)' }\n : undefined\n }\n />\n\n {hasRightAxis && (\n <YAxis\n yAxisId=\"right\"\n orientation=\"right\"\n tick={{ fill: 'var(--color-text-secondary)', fontSize: 12 }}\n tickLine={{ stroke: 'var(--color-border-default)' }}\n axisLine={{ stroke: 'var(--color-border-default)' }}\n tickFormatter={formatValue}\n label={\n config.yAxisRightLabel\n ? {\n value: config.yAxisRightLabel,\n angle: 90,\n position: 'insideRight',\n fill: 'var(--color-text-secondary)',\n }\n : undefined\n }\n />\n )}\n\n <Tooltip\n content={<ChartTooltip valueFormatter={formatValue} />}\n cursor={{ fill: 'var(--color-bg-muted)', opacity: 0.5 }}\n />\n\n {config.showLegend !== false && (\n <Legend\n wrapperStyle={{ paddingTop: 20 }}\n formatter={(value) => <span style={{ color: 'var(--color-text-primary)' }}>{value}</span>}\n />\n )}\n\n {series.map((s, index) => {\n const color = s.color || config.colors?.[index] || DEFAULT_COLORS[index % DEFAULT_COLORS.length];\n const yAxisId = s.axis === 'right' && hasRightAxis ? 'right' : 'left';\n const name = s.label || config.seriesNames?.[s.field] || s.field;\n const hidden = hiddenSeries.includes(s.field);\n\n if (s.type === 'line') {\n return (\n <Line\n key={s.field}\n yAxisId={yAxisId}\n type={config.curveType || 'monotone'}\n dataKey={s.field}\n name={name}\n stroke={color}\n strokeWidth={2}\n dot={config.showDots !== false}\n activeDot={{ r: 6 }}\n hide={hidden}\n connectNulls\n />\n );\n }\n return (\n <Bar\n key={s.field}\n yAxisId={yAxisId}\n dataKey={s.field}\n name={name}\n fill={color}\n radius={[4, 4, 0, 0]}\n hide={hidden}\n stackId={config.stacked ? 'stack' : undefined}\n />\n );\n })}\n\n {buildReferenceElements(config.referenceLines, { yAxisId: 'left' })}\n </ComposedChart>\n </ResponsiveContainer>\n );\n});\n\nexport default ComboChart;\n"],"mappings":";;;;;;AA6BA,IAAM,IAAiB;CACrB;CACA;CACA;CACA;CACA;CACD,EAMY,IAAkC,EAAK,SAAoB,EACtE,UAAO,EAAE,EACT,WACA,kBAAe,EAAE,EACjB,kBACC;CAGD,IAAM,IAAyB,QACzB,EAAO,QAAQ,SAAe,EAAO,SACpC,EAAK,SACG,OAAO,KAAK,EAAK,GAAG,CAAC,QAAQ,MAAM,MAAM,EAAO,YAAY,OAAO,EAAK,GAAG,MAAO,SAAS,CAC5F,KAAK,GAAO,OAAO;EAC7B;EACA,MAAM,MAAM,IAAI,QAAQ;EACxB,MAAM,MAAM,IAAI,SAAS;EAC1B,EAAE,GANsB,EAAE,EAO1B;EAAC,EAAO;EAAQ;EAAM,EAAO;EAAS,CAAC,EAEpC,IAAe,QAAc,EAAO,MAAM,MAAM,EAAE,SAAS,QAAQ,EAAE,CAAC,EAAO,CAAC,EAE9E,KAAe,MACf,OAAO,KAAU,WACd,IAAI,KAAK,aAAa,SAAS;EACpC,UAAU,KAAS,MAAQ,YAAY;EACvC,uBAAuB;EACxB,CAAC,CAAC,OAAO,EAAM,GAJsB,OAAO,EAAM;AAWrD,QAJK,GAAM,SAKT,kBAAC,GAAD;EAAqB,OAAM;EAAO,QAAO;YACvC,kBAAC,GAAD;GACQ;GACN,QAAQ;IAAE,KAAK;IAAI,OAAO;IAAI,MAAM;IAAG,QAAQ;IAAG;GAClD,UAAU,MAAM;AACd,IAAI,GAAG,gBAAgB,MAAM,KAC3B,EAAY,EAAE,cAAc,GAAG,SAAS,EAAE,sBAAsB,EAAE;;aALxE;IASG,EAAO,aAAa,MACnB,kBAAC,GAAD;KAAe,iBAAgB;KAAM,QAAO;KAA8B,SAAS;KAAK,UAAU;KAAS,CAAA;IAG7G,kBAAC,GAAD;KACE,SAAS,EAAO,YAAY;KAC5B,MAAM;MAAE,MAAM;MAA+B,UAAU;MAAI;KAC3D,UAAU,EAAE,QAAQ,+BAA+B;KACnD,UAAU,EAAE,QAAQ,+BAA+B;KACnD,CAAA;IAEF,kBAAC,GAAD;KACE,SAAQ;KACR,MAAM;MAAE,MAAM;MAA+B,UAAU;MAAI;KAC3D,UAAU,EAAE,QAAQ,+BAA+B;KACnD,UAAU,EAAE,QAAQ,+BAA+B;KACnD,eAAe;KACf,OACE,EAAO,aACH;MAAE,OAAO,EAAO;MAAY,OAAO;MAAK,UAAU;MAAc,MAAM;MAA+B,GACrG,KAAA;KAEN,CAAA;IAED,KACC,kBAAC,GAAD;KACE,SAAQ;KACR,aAAY;KACZ,MAAM;MAAE,MAAM;MAA+B,UAAU;MAAI;KAC3D,UAAU,EAAE,QAAQ,+BAA+B;KACnD,UAAU,EAAE,QAAQ,+BAA+B;KACnD,eAAe;KACf,OACE,EAAO,kBACH;MACE,OAAO,EAAO;MACd,OAAO;MACP,UAAU;MACV,MAAM;MACP,GACD,KAAA;KAEN,CAAA;IAGJ,kBAAC,GAAD;KACE,SAAS,kBAAC,GAAD,EAAc,gBAAgB,GAAe,CAAA;KACtD,QAAQ;MAAE,MAAM;MAAyB,SAAS;MAAK;KACvD,CAAA;IAED,EAAO,eAAe,MACrB,kBAAC,GAAD;KACE,cAAc,EAAE,YAAY,IAAI;KAChC,YAAY,MAAU,kBAAC,QAAD;MAAM,OAAO,EAAE,OAAO,6BAA6B;gBAAG;MAAa,CAAA;KACzF,CAAA;IAGH,EAAO,KAAK,GAAG,MAAU;KACxB,IAAM,IAAQ,EAAE,SAAS,EAAO,SAAS,MAAU,EAAe,IAAQ,EAAe,SACnF,IAAU,EAAE,SAAS,WAAW,IAAe,UAAU,QACzD,IAAO,EAAE,SAAS,EAAO,cAAc,EAAE,UAAU,EAAE,OACrD,IAAS,EAAa,SAAS,EAAE,MAAM;AAmB7C,YAjBI,EAAE,SAAS,SAEX,kBAAC,GAAD;MAEW;MACT,MAAM,EAAO,aAAa;MAC1B,SAAS,EAAE;MACL;MACN,QAAQ;MACR,aAAa;MACb,KAAK,EAAO,aAAa;MACzB,WAAW,EAAE,GAAG,GAAG;MACnB,MAAM;MACN,cAAA;MACA,EAXK,EAAE,MAWP,GAIJ,kBAAC,GAAD;MAEW;MACT,SAAS,EAAE;MACL;MACN,MAAM;MACN,QAAQ;OAAC;OAAG;OAAG;OAAG;OAAE;MACpB,MAAM;MACN,SAAS,EAAO,UAAU,UAAU,KAAA;MACpC,EARK,EAAE,MAQP;MAEJ;IAED,EAAuB,EAAO,gBAAgB,EAAE,SAAS,QAAQ,CAAC;IACrD;;EACI,CAAA,GA9Gf;EAgHT"}
|