--- name: heatmap_grid kind: component lang: ts domain: ui version: "1.0.0" purity: impure signature: "HeatmapGrid(props: HeatmapGridProps): JSX.Element" description: "Matriz rows × columns con intensidad de color proporcional al valor. Genérico — casos típicos: day×hour, cohort retention, matriz de correlación, heatmap geográfico." tags: [heatmap, matrix, dashboard, component, ui, chart, retention, cohort] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "" imports: ["@mantine/core", react] output: "Componente HeatmapGrid que renderiza una tabla de celdas coloreadas por intensidad con labels formateados." tested: false tests: [] test_file_path: "" file_path: "frontend/functions/ui/heatmap_grid.tsx" props: - name: rows type: "Record[]" required: true description: "Filas de datos. Cada objeto contiene el label de fila (bajo rowKey) y los valores (bajo las keys de columns)." - name: rowKey type: "string" required: true description: "Key en cada row donde está el label de la fila (ej. 'day', 'cohort')." - name: columns type: "HeatmapColumn[]" required: true description: "Columnas a visualizar, en orden. Cada una: { key, label? }." - name: valueFormatter type: "(v: number) => string" required: false description: "Formateador del valor numérico. Default: int o 2 decimales." - name: rowLabelFormatter type: "(row: Record) => string" required: false description: "Formatear el label de fila. Default: String(row[rowKey])." - name: tooltip type: "(row, column, value) => string" required: false description: "Generador del tooltip (title HTML nativo)." - name: cellLabelThreshold type: "number" required: false description: "Mostrar el valor dentro de la celda solo si |v| ≥ threshold. Default: siempre mostrar." - name: cellSize type: "number" required: false description: "Tamaño de cada celda en px. Default 22." - name: colLabelEvery type: "number" required: false description: "Mostrar label de columna cada N columnas (grids densos). Default 1." - name: intensityRange type: "[number, number]" required: false description: "Min/max % del baseColor aplicado vía color-mix. Default [6, 84]." - name: baseColor type: "string" required: false description: "Color base. Default 'var(--mantine-primary-color-filled)'." emits: [] has_state: false framework: react variant: [default] source_repo: "claude.ai/design" source_license: "" source_file: "sources/frontend_designs/Ads Analytics Dashboard _standalone_.html" --- ## Ejemplo ```tsx import { HeatmapGrid } from '@fn_library' // CTR por día × hora (caso del export original) const hours = Array.from({ length: 24 }, (_, i) => ({ key: 'h' + i, label: String(i).padStart(2, '0') })) const rows = [ { day: 'Mon', h0: 0.8, h1: 0.7, /* ... */ h23: 1.4 }, { day: 'Tue', h0: 0.9, h1: 0.8, /* ... */ h23: 1.3 }, // ... ] v.toFixed(2)} cellLabelThreshold={1.7} tooltip={(r, c, v) => `${r.day} ${c.label}:00 — CTR ${v}%`} /> // Cohort retention (semana 0-12) const cohortCols = Array.from({ length: 13 }, (_, i) => ({ key: 'w' + i, label: 'W' + i })) v.toFixed(0) + '%'} intensityRange={[10, 90]} /> // Matriz de correlación ({ key: v, label: v }))} valueFormatter={(v) => v.toFixed(2)} cellSize={40} baseColor="var(--mantine-color-cyan-6)" /> ``` ## Notas - El color de cada celda es `color-mix(in oklab, baseColor N%, transparent)` donde N interpola linealmente entre `intensityRange[0]` y `intensityRange[1]` según el valor normalizado en `[min, max]`. - El color del texto en la celda cambia automáticamente a blanco si `v > mid` (mid-point del rango) para contraste. - Para grids densos (ej. 24 horas × 7 días) usar `colLabelEvery={2}` o mayor para que los headers no se solapen. - Extraído de un export de Claude Design (Ads Analytics Dashboard). El `Heatmap` original hardcodeaba `h0..h23`; aquí se generaliza aceptando cualquier array de columnas.