dc78d8fea3
Componentes React reutilizables: card, dialog, tabs, select, alert, badge, button, input, label, skeleton, tooltip, progress_bar, page_header, form_field, settings_page, crud_page, analytics_page, dashboard_layout. Charts: area, bar, line, sparkline, kpi_card, chart_container. Hooks Wails: use_wails_query, use_wails_mutation, use_wails_stream, use_wails_event, use_animated_canvas. Funciones core: cn, format_compact, chart_colors, get_series_color, wails_cache, theme_config_to_colors. Tipos: chart_series, wails_ipc, theme_config, component_variants. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import * as React from 'react'
|
|
import { cn } from '../core/cn'
|
|
import { ResponsiveContainer, Tooltip as RechartsTooltip, Legend as RechartsLegend } from 'recharts'
|
|
|
|
export const chartColors = [
|
|
'hsl(var(--chart-1, 220 70% 50%))',
|
|
'hsl(var(--chart-2, 160 60% 45%))',
|
|
'hsl(var(--chart-3, 30 80% 55%))',
|
|
'hsl(var(--chart-4, 280 65% 60%))',
|
|
'hsl(var(--chart-5, 340 75% 55%))',
|
|
]
|
|
|
|
export const defaultColors = ['#3b82f6', '#22c55e', '#f59e0b', '#8b5cf6', '#ec4899']
|
|
|
|
export interface Series {
|
|
key: string
|
|
name: string
|
|
color?: string
|
|
}
|
|
|
|
export function getSeriesColor(index: number, color?: string): string {
|
|
return color || defaultColors[index % defaultColors.length]
|
|
}
|
|
|
|
interface ChartContainerProps {
|
|
children: React.ReactNode
|
|
className?: string
|
|
height?: number | string
|
|
}
|
|
|
|
export function ChartContainer({ children, className, height = 300 }: ChartContainerProps) {
|
|
return (
|
|
<div
|
|
className={cn('w-full', className)}
|
|
style={{ height, minWidth: 100, minHeight: typeof height === 'number' ? Math.min(height, 100) : 100 }}
|
|
>
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
{children as React.ReactElement}
|
|
</ResponsiveContainer>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
interface ChartTooltipContentProps {
|
|
active?: boolean
|
|
payload?: Array<{ name: string; value: number; color: string; dataKey: string }>
|
|
label?: string
|
|
labelFormatter?: (label: string) => string
|
|
valueFormatter?: (value: number) => string
|
|
}
|
|
|
|
export function ChartTooltipContent({
|
|
active, payload, label,
|
|
labelFormatter = (l) => l,
|
|
valueFormatter = (v) => v.toLocaleString(),
|
|
}: ChartTooltipContentProps) {
|
|
if (!active || !payload?.length) return null
|
|
return (
|
|
<div className="rounded-lg border bg-background p-2 shadow-md">
|
|
<p className="mb-1 text-sm font-medium">{labelFormatter(label || '')}</p>
|
|
<div className="space-y-0.5">
|
|
{payload.map((entry, index) => (
|
|
<div key={index} className="flex items-center gap-2 text-sm">
|
|
<div className="size-2.5 rounded-full" style={{ backgroundColor: entry.color }} />
|
|
<span className="text-muted-foreground">{entry.name}:</span>
|
|
<span className="font-medium">{valueFormatter(entry.value)}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function ChartTooltip(props: React.ComponentProps<typeof RechartsTooltip>) {
|
|
return <RechartsTooltip content={<ChartTooltipContent />} cursor={{ fill: 'hsl(var(--muted) / 0.3)' }} {...props} />
|
|
}
|
|
|
|
export function ChartLegend(props: React.ComponentProps<typeof RechartsLegend>) {
|
|
return <RechartsLegend wrapperStyle={{ paddingTop: 16 }} {...props} />
|
|
}
|