UNPKG

analytica-frontend-lib

Version:

Repositório público dos componentes utilizados nas plataformas da Analytica Ensino

1 lines 9.03 kB
{"version":3,"sources":["../src/components/VerticalBarChart/VerticalBarChart.tsx","../src/components/VerticalBarChart/utils.ts"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\nimport { calculateTicks } from './utils';\nimport type {\n VerticalBarChartProps,\n BarProps,\n YAxisProps,\n XAxisLabelsProps,\n} from './interfaces';\n\nexport type {\n VerticalBarChartDataItem,\n VerticalBarChartProps,\n} from './interfaces';\n\n// --- Sub-components ---\n\nconst YAxis = ({ ticks, chartHeight }: YAxisProps) => (\n <div\n // w-12 (48px) matches ml-12 on XAxisLabels for alignment\n className=\"flex flex-col justify-between text-right pr-2 w-12 shrink-0\"\n style={{ height: chartHeight }}\n aria-hidden=\"true\"\n >\n {ticks.map((tick, index) => (\n <Text\n key={`${tick}-${index}`}\n size=\"xs\"\n weight=\"medium\"\n className=\"text-text-500\"\n >\n {tick.toLocaleString('pt-BR')}\n </Text>\n ))}\n </div>\n);\n\nconst Bar = ({\n item,\n maxValue,\n chartHeight,\n barColor,\n isHovered,\n anyHovered,\n onMouseEnter,\n onMouseLeave,\n}: BarProps) => {\n const barHeight = maxValue === 0 ? 0 : (item.value / maxValue) * chartHeight;\n\n return (\n <div\n className=\"flex-1 flex flex-col items-center justify-end h-full relative group\"\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n >\n {/* Tooltip */}\n {isHovered && item.value > 0 && (\n <div className=\"absolute bottom-full mb-2 px-2 py-1 bg-text-950 text-white text-xs rounded shadow-lg whitespace-nowrap z-10\">\n {item.value.toLocaleString('pt-BR')}\n <div className=\"absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-text-950\" />\n </div>\n )}\n <div\n data-testid={`bar-${item.label}`}\n className=\"w-full max-w-[40px] rounded-t transition-all duration-300 cursor-pointer\"\n style={{\n height: `${(barHeight / chartHeight) * 100}%`,\n backgroundColor: barColor,\n minHeight: item.value > 0 ? '4px' : '0',\n opacity: !anyHovered || isHovered ? 1 : 0.5,\n }}\n aria-label={`${item.label}: ${item.value}`}\n />\n </div>\n );\n};\n\nconst XAxisLabels = ({ data }: XAxisLabelsProps) => (\n // ml-12 (48px) matches w-12 on YAxis for alignment\n <div className=\"flex gap-1 ml-12\">\n {data.map((item) => (\n <div key={item.label} className=\"flex-1 text-center\">\n <Text\n size=\"xs\"\n className=\"text-text-500\"\n data-testid={`label-${item.label}`}\n >\n {item.label}\n </Text>\n </div>\n ))}\n </div>\n);\n\n// --- Main component ---\n\n/**\n * VerticalBarChart component - displays a vertical bar chart with bars going upward.\n *\n * @example\n * ```tsx\n * <VerticalBarChart\n * data={[\n * { label: 'SEG', value: 150 },\n * { label: 'TER', value: 200 },\n * { label: 'QUA', value: 100 },\n * ]}\n * title=\"Atividades por período\"\n * barColor=\"#4a7c59\"\n * />\n * ```\n */\nexport const VerticalBarChart = ({\n data,\n title,\n chartHeight = 200,\n barColor = '#4a7c59',\n className,\n ...props\n}: VerticalBarChartProps) => {\n const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);\n const maxValue = Math.max(...data.map((item) => item.value), 0);\n const yAxisTicks = useMemo(() => calculateTicks(maxValue), [maxValue]);\n const chartMaxValue = yAxisTicks[0] || maxValue || 1;\n\n return (\n <div\n className={cn(\n 'flex flex-col gap-4 bg-background border border-border-50 rounded-xl p-5',\n className\n )}\n aria-label={title}\n {...props}\n >\n <Text\n as=\"h3\"\n size=\"lg\"\n weight=\"bold\"\n className=\"text-text-950 tracking-[0.2px]\"\n >\n {title}\n </Text>\n <div className=\"flex gap-2\">\n <YAxis ticks={yAxisTicks} chartHeight={chartHeight} />\n <div\n className=\"flex-1 flex items-end gap-1 border-l border-b border-border-100\"\n style={{ height: chartHeight }}\n >\n {data.map((item, index) => (\n <Bar\n key={`${item.label}-${index}`}\n item={item}\n maxValue={chartMaxValue}\n chartHeight={chartHeight}\n barColor={barColor}\n isHovered={hoveredIndex === index}\n anyHovered={hoveredIndex !== null}\n onMouseEnter={() => setHoveredIndex(index)}\n onMouseLeave={() => setHoveredIndex(null)}\n />\n ))}\n </div>\n </div>\n <XAxisLabels data={data} />\n </div>\n );\n};\n\nexport default VerticalBarChart;\n","/**\n * Chart axis utilities for VerticalBarChart\n */\n\n/**\n * Calculate a \"nice\" step value for Y-axis ticks.\n * Returns a round number (1, 2, 5, 10, 20, 50, 100, etc.) that divides\n * the range into approximately 4-5 intervals.\n */\nexport const niceStep = (maxValue: number): number => {\n const roughStep = maxValue / 4;\n const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep)));\n const normalized = roughStep / magnitude;\n\n let niceNormalized: number;\n if (normalized <= 1) niceNormalized = 1;\n else if (normalized <= 2) niceNormalized = 2;\n else if (normalized <= 5) niceNormalized = 5;\n else niceNormalized = 10;\n\n return Math.max(1, niceNormalized * magnitude);\n};\n\n/**\n * Calculate Y-axis tick values for count display.\n * Uses \"nice numbers\" to create evenly spaced, human-readable ticks.\n */\nexport const calculateTicks = (maxValue: number): number[] => {\n if (maxValue <= 0) return [0];\n\n const step = niceStep(maxValue);\n const ticks: number[] = [];\n\n for (let i = 0; i <= maxValue; i += step) {\n ticks.push(i);\n }\n\n if (ticks.at(-1)! < maxValue) {\n ticks.push(ticks.at(-1)! + step);\n }\n\n return ticks.reverse();\n};\n"],"mappings":"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","names":[]}