1 estrella en GitHub = 1 componente nuevo.

Dar una estrella
Gráficos y Estadísticas chartsvgzero-dependencyareabandrangepercentile

Range Area Chart

Banda de mínimo/máximo con línea de mediana, ideal para percentiles de latencia, temperaturas o rangos de precio.

Vista Previa en Vivo

API latency

p10–p90 band with median, last 10 days

  • p10–p90
  • Median
0ms 50ms 100ms 150ms 200ms MonTueWedThuFriSatSunMonTueWed
View data
PeriodMinMedianMax
Mon42ms74ms120ms
Tue45ms80ms135ms
Wed40ms70ms118ms
Thu48ms92ms160ms
Fri52ms101ms172ms
Sat38ms62ms98ms
Sun36ms58ms90ms
Mon44ms78ms126ms
Tue47ms86ms140ms
Wed41ms72ms115ms

Uso

Copia y pega este código directamente en tu página de Astro:

astro
---
import { RangeAreaChart } from 'astro-component-kit/charts';
---

<RangeAreaChart
  categories={['Mon', 'Tue', 'Wed', 'Thu', 'Fri']}
  min={[42, 45, 40, 48, 52]}
  avg={[74, 80, 70, 92, 101]}
  max={[120, 135, 118, 160, 172]}
  suffix="ms"
/>
--- import { RangeAreaChart } from 'astro-component-kit/charts'; --- <RangeAreaChart categories={['Mon', 'Tue', 'Wed', 'Thu', 'Fri']} min={[42, 45, 40, 48, 52]} avg={[74, 80, 70, 92, 101]} max={[120, 135, 118, 160, 172]} suffix="ms" />

Instalación Manual

Si no estás usando el paquete npm, crea un archivo nuevo y pega el siguiente código: src/components/lib/RangeAreaChart.astro:

astro
---
/**
 * RangeAreaChart — Min/max band with an average line. Ideal for temperatures,
 * latency percentiles or price ranges.
 *
 * @param {string[]} categories - X-axis labels.
 * @param {number[]} min - Lower bound per period.
 * @param {number[]} max - Upper bound per period.
 * @param {number[]} avg - Central value per period.
 * @param {string} bandLabel - Legend label for the band.
 * @param {string} lineLabel - Legend label for the line.
 * @param {string} suffix - Value suffix.
 * @param {string} color - Hue for band and line.
 * @param {string} variant - Card style (glass|solid|ghost).
 * @param {string} size - Density (sm|md|lg).
 */
import ChartFrame from './ChartFrame.astro';
import { scaleLinear, niceTicks, bandPath, smoothPath, formatValue, type Point } from './chart-utils';

interface Props {
  title?: string;
  subtitle?: string;
  categories?: string[];
  min?: number[];
  max?: number[];
  avg?: number[];
  bandLabel?: string;
  lineLabel?: string;
  prefix?: string;
  suffix?: string;
  height?: number;
  color?: string;
  variant?: 'glass' | 'solid' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  className?: string;
}

const {
  title = 'API latency',
  subtitle = 'p10–p90 band with median, last 10 days',
  categories = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Mon', 'Tue', 'Wed'],
  min = [42, 45, 40, 48, 52, 38, 36, 44, 47, 41],
  max = [120, 135, 118, 160, 172, 98, 90, 126, 140, 115],
  avg = [74, 80, 70, 92, 101, 62, 58, 78, 86, 72],
  bandLabel = 'p10–p90',
  lineLabel = 'Median',
  prefix = '',
  suffix = 'ms',
  height = 260,
  color = 'var(--chart-1, #3987e5)',
  variant = 'glass',
  size = 'md',
  className = '',
} = Astro.props;

const W = 640;
const H = height;
const m = { t: 12, r: 16, b: 28, l: 48 };
const n = categories.length;
const ticks = niceTicks(Math.min(...min), Math.max(...max), 4);
const y = scaleLinear(ticks[0], ticks[ticks.length - 1], H - m.b, m.t);
const x = (i: number) => m.l + (n <= 1 ? 0 : (i / (n - 1)) * (W - m.l - m.r));
const step = n > 1 ? (W - m.l - m.r) / (n - 1) : W;
const upper: Point[] = max.map((v, i) => [x(i), y(v)]);
const lower: Point[] = min.map((v, i) => [x(i), y(v)]);
const mid: Point[] = avg.map((v, i) => [x(i), y(v)]);
const fmt = (v: number) => formatValue(v, prefix, suffix);
---

<ChartFrame
  title={title}
  subtitle={subtitle}
  variant={variant}
  size={size}
  className={className}
  legend={[{ label: bandLabel, color: `color-mix(in srgb, ${color} 35%, transparent)` }, { label: lineLabel, color }]}
  table={{
    headers: ['Period', 'Min', lineLabel, 'Max'],
    rows: categories.map((c, i) => [c, fmt(min[i]), fmt(avg[i]), fmt(max[i])]),
  }}
>
  <svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={`${title}: range band chart`}>
    {ticks.map((t) => (
      <g>
        <line class="cf-grid" x1={m.l} x2={W - m.r} y1={y(t)} y2={y(t)} />
        <text class="cf-label" x={m.l - 8} y={y(t) + 4} text-anchor="end">{fmt(t)}</text>
      </g>
    ))}
    {categories.map((c, i) => <text class="cf-label" x={x(i)} y={H - 8} text-anchor="middle">{c}</text>)}

    <path d={bandPath(upper, lower)} fill={color} fill-opacity="0.18" aria-hidden="true" />
    <path d={smoothPath(mid)} fill="none" stroke={color} stroke-width="2" aria-hidden="true" />

    {categories.map((c, i) => (
      <g class="cf-hit-group">
        <line class="cf-cross" x1={x(i)} x2={x(i)} y1={y(max[i])} y2={y(min[i])} />
        <circle class="cf-cross" cx={x(i)} cy={y(avg[i])} r="4.5" fill={color} stroke="#fff" stroke-width="1.5" />
        <rect
          class="cf-hit"
          x={x(i) - step / 2}
          y={m.t}
          width={step}
          height={H - m.t - m.b}
          tabindex="0"
          role="img"
          aria-label={`${c}: min ${fmt(min[i])}, ${lineLabel} ${fmt(avg[i])}, max ${fmt(max[i])}`}
          data-tip-title={c}
          data-tip={`Max: ${fmt(max[i])}|${color}::${lineLabel}: ${fmt(avg[i])}|Min: ${fmt(min[i])}`}
        />
      </g>
    ))}
  </svg>
</ChartFrame>
--- /** * RangeAreaChart — Min/max band with an average line. Ideal for temperatures, * latency percentiles or price ranges. * * @param {string[]} categories - X-axis labels. * @param {number[]} min - Lower bound per period. * @param {number[]} max - Upper bound per period. * @param {number[]} avg - Central value per period. * @param {string} bandLabel - Legend label for the band. * @param {string} lineLabel - Legend label for the line. * @param {string} suffix - Value suffix. * @param {string} color - Hue for band and line. * @param {string} variant - Card style (glass|solid|ghost). * @param {string} size - Density (sm|md|lg). */ import ChartFrame from './ChartFrame.astro'; import { scaleLinear, niceTicks, bandPath, smoothPath, formatValue, type Point } from './chart-utils'; interface Props { title?: string; subtitle?: string; categories?: string[]; min?: number[]; max?: number[]; avg?: number[]; bandLabel?: string; lineLabel?: string; prefix?: string; suffix?: string; height?: number; color?: string; variant?: 'glass' | 'solid' | 'ghost'; size?: 'sm' | 'md' | 'lg'; className?: string; } const { title = 'API latency', subtitle = 'p10–p90 band with median, last 10 days', categories = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Mon', 'Tue', 'Wed'], min = [42, 45, 40, 48, 52, 38, 36, 44, 47, 41], max = [120, 135, 118, 160, 172, 98, 90, 126, 140, 115], avg = [74, 80, 70, 92, 101, 62, 58, 78, 86, 72], bandLabel = 'p10–p90', lineLabel = 'Median', prefix = '', suffix = 'ms', height = 260, color = 'var(--chart-1, #3987e5)', variant = 'glass', size = 'md', className = '', } = Astro.props; const W = 640; const H = height; const m = { t: 12, r: 16, b: 28, l: 48 }; const n = categories.length; const ticks = niceTicks(Math.min(...min), Math.max(...max), 4); const y = scaleLinear(ticks[0], ticks[ticks.length - 1], H - m.b, m.t); const x = (i: number) => m.l + (n <= 1 ? 0 : (i / (n - 1)) * (W - m.l - m.r)); const step = n > 1 ? (W - m.l - m.r) / (n - 1) : W; const upper: Point[] = max.map((v, i) => [x(i), y(v)]); const lower: Point[] = min.map((v, i) => [x(i), y(v)]); const mid: Point[] = avg.map((v, i) => [x(i), y(v)]); const fmt = (v: number) => formatValue(v, prefix, suffix); --- <ChartFrame title={title} subtitle={subtitle} variant={variant} size={size} className={className} legend={[{ label: bandLabel, color: `color-mix(in srgb, ${color} 35%, transparent)` }, { label: lineLabel, color }]} table={{ headers: ['Period', 'Min', lineLabel, 'Max'], rows: categories.map((c, i) => [c, fmt(min[i]), fmt(avg[i]), fmt(max[i])]), }} > <svg viewBox={`0 0 ${W} ${H}`} role="img" aria-label={`${title}: range band chart`}> {ticks.map((t) => ( <g> <line class="cf-grid" x1={m.l} x2={W - m.r} y1={y(t)} y2={y(t)} /> <text class="cf-label" x={m.l - 8} y={y(t) + 4} text-anchor="end">{fmt(t)}</text> </g> ))} {categories.map((c, i) => <text class="cf-label" x={x(i)} y={H - 8} text-anchor="middle">{c}</text>)} <path d={bandPath(upper, lower)} fill={color} fill-opacity="0.18" aria-hidden="true" /> <path d={smoothPath(mid)} fill="none" stroke={color} stroke-width="2" aria-hidden="true" /> {categories.map((c, i) => ( <g class="cf-hit-group"> <line class="cf-cross" x1={x(i)} x2={x(i)} y1={y(max[i])} y2={y(min[i])} /> <circle class="cf-cross" cx={x(i)} cy={y(avg[i])} r="4.5" fill={color} stroke="#fff" stroke-width="1.5" /> <rect class="cf-hit" x={x(i) - step / 2} y={m.t} width={step} height={H - m.t - m.b} tabindex="0" role="img" aria-label={`${c}: min ${fmt(min[i])}, ${lineLabel} ${fmt(avg[i])}, max ${fmt(max[i])}`} data-tip-title={c} data-tip={`Max: ${fmt(max[i])}|${color}::${lineLabel}: ${fmt(avg[i])}|Min: ${fmt(min[i])}`} /> </g> ))} </svg> </ChartFrame>

Información Rápida

Categoría
Gráficos y Estadísticas
Nombre de archivo
RangeAreaChart.astro
Dependencias
apexcharts (peer)
Etiquetas
chartsvgzero-dependencyareabandrangepercentile