Zum Inhalt springen
UI Kit
Accent

Kreisdiagramm für Zyklen

Daten, die sich im Kreis drehen: 24 Stunden als Uhr, zwölf Monate als Rose mit ehrlichen Flächen, Wochentage nach Stunde als Ring-Heatmap und Gruppen mit ihren Teilen als Sunburst.

Eine von 7 Components in Charts

4 Variants

Variants

Stundenuhr

Vierundzwanzig Balken um ein Zifferblatt ab Mitternacht, die stärkste Stunde im Akzent und in der Mitte benannt.

Im Studio öffnen

Monatsrose

Zwölf Keile ab Januar, die Fläche jedes Keils ist der Wert, so wirkt der Sommer nicht größer, als er ist.

Im Studio öffnen

Ring-Heatmap

Sieben Ringe für die Wochentage, 24 Segmente für die Stunden, fünf Akzentstufen für den Wert. Pfeiltasten gehen im Kreis und zwischen den Ringen.

Im Studio öffnen

Sunburst

Gruppen innen, ihre Teile außen. Ein Teil hebt seinen ganzen Pfad hervor, die Liste daneben wählt eine Gruppe.

Im Studio öffnen

Details

Was Kreisdiagramm für Zyklen mitbringt, aus Registry und Quellcode gelesen.

Kategorie
Charts
Bereich
Elements
Variants
4
Version
1.0.0 · seit 1.1.0
Datei
components/ui/radial-chart.tsx
Bringt mit
utilskitchartchart-figure
npm-Pakete
Keine
Sprachen
Englisch und Deutsch, 13 eigene Texte

Props

Die Schnittstelle RadialChartProps aus components/ui/radial-chart.tsx. Beschreibungen stammen aus den Kommentaren im Code und sind auf Englisch. Nimmt außerdem alle Props von Omit<ChartA11y, 'label'>, ChartFigureProps an.

  • labeloptional
    string

    Accessible name of the chart. Default: the title.

  • variantoptional
    RadialVariant

    hoursroseclock-heatmapsunburst

  • valuesoptional
    number[]

    hours: 24 values from midnight. rose: 12 values from January.

  • rowsoptional
    number[][]

    clock-heatmap: one row of 24 values per day, Monday first

  • rowLabelsoptional
    string[]

    clock-heatmap: names of the rows. Default: the weekdays of the language.

  • groupsoptional
    RadialGroup[]

    sunburst: groups with their parts

  • formatoptional
    Intl.NumberFormatOptions
  • unitLabeloptional
    string

    Name of the value, e.g. "Visits"

Eingebaute Texte

Diese Texte schreibt Kreisdiagramm für Zyklen selbst. Die Sprache folgt dem KitProvider, über die Prop labels ersetzt du einzelne.

Alle 13 Texte anzeigen
ringsEnglisch {a} inside, {b} outsideDeutsch {a} innen, {b} außen
totalEnglisch TotalDeutsch Gesamt
hourEnglisch {h} to {n}Deutsch {h} bis {n} Uhr
peakEnglisch Busiest hourDeutsch Stärkste Stunde
monthEnglisch MonthDeutsch Monat
valueEnglisch ValueDeutsch Wert
groupEnglisch GroupDeutsch Gruppe
partEnglisch PartDeutsch Teil
shareEnglisch {share} of allDeutsch {share} von allem
dayEnglisch DayDeutsch Tag
hourShortEnglisch HourDeutsch Stunde
lessEnglisch LessDeutsch Weniger
moreEnglisch MoreDeutsch Mehr

Barrierefreiheit

Aus dem Quellcode der Component abgeleitet.

  • Kreisdiagramm für Zyklen reagiert selbst auf die Tasten Escape, Pfeil hoch, Pfeil runter, Pfeil links, Pfeil rechts, Pos1 und Ende.
  • Kommt ohne Client-State aus. Der Server schreibt sie vollständig, sie ist auch ohne JavaScript lesbar.

Bewegung

  • Ohne Animationen, sie steht sofort ruhig da.

Quellcode

Die Datei, die aiw add in dein Projekt legt. Sie gehört danach dir.

Datei anzeigen, 310 Zeilen
components/ui/radial-chart.tsx
'use client';

import { useState } from 'react';
import { cn, round, useTexts } from '@/lib/kit';
import { ChartRoot, ChartSurface, ChartTable, polar, numberFormatter, rampColor, useEntrance, useIntlLocale, useWidth, type ChartA11y } from '@/components/ui/chart';
import { ChartFigure, pairColor, type ChartFigureProps } from '@/components/ui/chart-figure';

/*
 * Data that goes round: hours of a day, months of a year, parts of parts.
 *   hours          24 bars around a clock face, the busiest hour in the accent
 *   rose           twelve wedges, one per month, the area is the value
 *   clock-heatmap  rings for the weekdays, segments for the hours, the tint is the value
 *   sunburst       groups inside, their parts outside, a part lights its whole path
 *
 * Every wedge is reachable with the arrow keys (up and down change the ring
 * in the heatmap and the level in the sunburst). The center names what is
 * picked, the table holds every value.
 */

export type RadialVariant = 'hours' | 'rose' | 'clock-heatmap' | 'sunburst';

export interface RadialGroup {
  label: string;
  value?: number;
  children?: { label: string; value: number }[];
}

export interface RadialChartProps extends Omit<ChartA11y, 'label'>, ChartFigureProps {
  /** Accessible name of the chart. Default: the title. */
  label?: string;
  variant?: RadialVariant;
  /** hours: 24 values from midnight. rose: 12 values from January. */
  values?: number[];
  /** clock-heatmap: one row of 24 values per day, Monday first */
  rows?: number[][];
  /** clock-heatmap: names of the rows. Default: the weekdays of the language. */
  rowLabels?: string[];
  /** sunburst: groups with their parts */
  groups?: RadialGroup[];
  format?: Intl.NumberFormatOptions;
  /** Name of the value, e.g. "Visits" */
  unitLabel?: string;
  className?: string;
}

const TEXTS = {
  en: { rings: '{a} inside, {b} outside', total: 'Total', hour: '{h} to {n}', peak: 'Busiest hour', month: 'Month', value: 'Value', group: 'Group', part: 'Part', share: '{share} of all', day: 'Day', hourShort: 'Hour', less: 'Less', more: 'More' },
  de: { rings: '{a} innen, {b} außen', total: 'Gesamt', hour: '{h} bis {n} Uhr', peak: 'Stärkste Stunde', month: 'Monat', value: 'Wert', group: 'Gruppe', part: 'Teil', share: '{share} von allem', day: 'Tag', hourShort: 'Stunde', less: 'Weniger', more: 'Mehr' },
};

/** A ring sector from a0 to a1 degrees (clockwise from twelve), between radius r0 and r1 */
function sector(cx: number, cy: number, r0: number, r1: number, a0: number, a1: number) {
  const large = a1 - a0 > 180 ? 1 : 0;
  const [x1, y1] = polar(cx, cy, r1, a0);
  const [x2, y2] = polar(cx, cy, r1, a1);
  const [x3, y3] = polar(cx, cy, r0, a1);
  const [x4, y4] = polar(cx, cy, r0, a0);
  if (r0 <= 0.5) return `M${cx} ${cy}L${x1} ${y1}A${round(r1)} ${round(r1)} 0 ${large} 1 ${x2} ${y2}Z`;
  return `M${x1} ${y1}A${round(r1)} ${round(r1)} 0 ${large} 1 ${x2} ${y2}L${x3} ${y3}A${round(r0)} ${round(r0)} 0 ${large} 0 ${x4} ${y4}Z`;
}

/**
 * Round data: a 24 hour clock, a rose of months, a weekday by hour ring heatmap or a sunburst.
 *   <RadialChart variant="hours" values={visitsPerHour} unitLabel="Visits" />
 */
export function RadialChart(props: RadialChartProps) {
  const { variant = 'hours', values = [], rows = [], groups = [], format, unitLabel = '', label, summary, labels, animate = true, title, value, delta, meta, source, framed, className } = props;
  const t = useTexts(TEXTS, undefined);
  const intl = useIntlLocale();
  const fmt = numberFormatter(intl, format);
  const pct = numberFormatter(intl, { style: 'percent', maximumFractionDigits: 0 });
  const [wrapRef, width] = useWidth<HTMLDivElement>(560);
  const [rootRef, anim] = useEntrance<HTMLDivElement>(animate);
  const [active, setActive] = useState<number | null>(null);
  const name = label ?? title ?? unitLabel;
  const size = Math.round(Math.max(240, Math.min(width, 440)));
  const cx = size / 2;
  const cy = size / 2;
  const R = size / 2 - 26;
  const d = (ms: number) => (anim ? ({ '--kitch-d': `${round(ms, 0)}ms` } as React.CSSProperties) : undefined);
  const dayNames = props.rowLabels ?? Array.from({ length: 7 }, (_, i) => new Intl.DateTimeFormat(intl, { weekday: 'short', timeZone: 'UTC' }).format(new Date(Date.UTC(2026, 9, 19 + i))));
  const monthName = (i: number, style: 'short' | 'long' = 'short') => new Intl.DateTimeFormat(intl, { month: style, timeZone: 'UTC' }).format(new Date(Date.UTC(2026, i, 1)));
  // English reads the hour as 2 PM, German as 14 (Uhr)
  const en = intl === 'en-US';
  const clock = (h: number) => (en ? `${h % 12 || 12} ${h < 12 ? 'AM' : 'PM'}` : String(h));
  const hourText = (h: number) => t.hour.replace('{h}', clock(h)).replace('{n}', clock((h + 1) % 24));
  /** A label outside the ring: right of the clock it starts there, left of it it ends there */
  const anchorAt = (deg: number) => (deg > 30 && deg < 150 ? 'start' : deg > 210 && deg < 330 ? 'end' : 'middle');

  // Wedges per variant: { a0, a1, r0, r1, fill, label, value, ring }
  type Wedge = { a0: number; a1: number; r0: number; r1: number; fill: string; title: string; value: number; ring: number; share?: number; parent?: number };
  const wedges: Wedge[] = [];
  const inner = R * 0.3;
  if (variant === 'hours' || variant === 'rose') {
    const n = variant === 'hours' ? 24 : 12;
    const vals = values.slice(0, n);
    const max = Math.max(...vals, 1);
    const peak = vals.indexOf(max);
    vals.forEach((v, i) => {
      const a0 = (360 * i) / n + (variant === 'hours' ? 1.2 : 1);
      const a1 = (360 * (i + 1)) / n - (variant === 'hours' ? 1.2 : 1);
      // rose: area true, the radius grows with the square root
      const r1 = variant === 'rose' ? inner + Math.sqrt(v / max) * (R - inner) : inner + (v / max) * (R - inner);
      wedges.push({ a0, a1, r0: inner, r1: Math.max(inner + 2, r1), fill: i === peak ? 'var(--primary)' : variant === 'rose' ? 'color-mix(in oklab, var(--primary) 34%, var(--card))' : 'color-mix(in oklab, var(--primary) 45%, var(--card))', title: variant === 'hours' ? hourText(i) : monthName(i, 'long'), value: v, ring: 0 });
    });
  } else if (variant === 'clock-heatmap') {
    const all = rows.flat();
    const lo = Math.min(...all);
    const hi = Math.max(...all);
    const band = (R - inner) / Math.max(1, rows.length);
    rows.forEach((row, ri) => {
      row.slice(0, 24).forEach((v, h) => {
        const step = Math.min(4, Math.floor(((v - lo) / (hi - lo || 1)) * 5));
        wedges.push({ a0: (360 * h) / 24 + 0.6, a1: (360 * (h + 1)) / 24 - 0.6, r0: inner + ri * band + 1, r1: inner + (ri + 1) * band - 1, fill: v === 0 ? 'var(--kitch-empty)' : rampColor(step), title: `${dayNames[ri]}, ${hourText(h)}`, value: v, ring: ri });
      });
    });
  } else {
    const total = groups.reduce((a, g) => a + (g.children?.reduce((s, c) => s + c.value, 0) ?? g.value ?? 0), 0) || 1;
    let at = 0;
    const r1 = inner + (R - inner) * 0.48;
    groups.forEach((g, gi) => {
      const gv = g.children?.reduce((s, c) => s + c.value, 0) ?? g.value ?? 0;
      const span = (gv / total) * 360;
      const color = pairColor(gi);
      const gIndex = wedges.length;
      wedges.push({ a0: at + 0.5, a1: at + span - 0.5, r0: inner, r1: r1 - 1, fill: color, title: g.label, value: gv, ring: 0, share: gv / total });
      let c0 = at;
      (g.children ?? []).forEach((c, ci) => {
        const cs = (c.value / total) * 360;
        wedges.push({ a0: c0 + 0.5, a1: c0 + cs - 0.5, r0: r1 + 1, r1: R, fill: `color-mix(in oklab, ${color} ${Math.max(28, 70 - ci * 12)}%, var(--card))`, title: c.label, value: c.value, ring: 1, share: c.value / total, parent: gIndex });
        c0 += cs;
      });
      at += span;
    });
  }

  const n = wedges.length;
  const total = variant === 'sunburst' ? wedges.filter((w) => w.ring === 0).reduce((a, w) => a + w.value, 0) : wedges.reduce((a, w) => a + w.value, 0);
  const lit = (i: number) => active === null || active === i || (variant === 'sunburst' && (wedges[active]?.parent === i || wedges[i]?.parent === active));

  const pick = (clientX: number, clientY: number, el: HTMLElement) => {
    const svg = el.querySelector('svg');
    if (!svg) return;
    const b = svg.getBoundingClientRect();
    const k = size / (b.width || size);
    const dx = (clientX - b.left) * k - cx;
    const dy = (clientY - b.top) * k - cy;
    const r = Math.hypot(dx, dy);
    const deg = ((Math.atan2(dy, dx) * 180) / Math.PI + 90 + 360) % 360;
    const hit = wedges.findIndex((w) => deg >= w.a0 - 0.8 && deg <= w.a1 + 0.8 && r >= w.r0 - 2 && r <= (variant === 'hours' || variant === 'rose' ? R : w.r1) + 2);
    setActive(hit >= 0 ? hit : null);
  };
  const onKeyDown = (e: React.KeyboardEvent) => {
    const cur = active ?? -1;
    let next: number | null = cur;
    if (variant === 'clock-heatmap' && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
      const w = wedges[cur < 0 ? 0 : cur];
      const h = cur < 0 ? 0 : cur % 24;
      const r2 = Math.max(0, Math.min(rows.length - 1, (w?.ring ?? 0) + (e.key === 'ArrowUp' ? 1 : -1)));
      next = r2 * 24 + h;
    } else if (variant === 'sunburst' && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
      const w = wedges[cur];
      if (e.key === 'ArrowUp') next = w && w.ring === 0 ? wedges.findIndex((x) => x.parent === cur) : cur;
      else next = w && w.parent !== undefined ? w.parent : cur;
      if (next === -1) next = cur;
    } else if (e.key === 'ArrowRight') next = (cur + 1) % n;
    else if (e.key === 'ArrowLeft') next = cur <= 0 ? n - 1 : cur - 1;
    else if (e.key === 'Home') next = 0;
    else if (e.key === 'End') next = n - 1;
    else if (e.key === 'Escape') next = null;
    else return;
    e.preventDefault();
    setActive(next);
  };
  const a = active !== null ? wedges[active] : null;
  const live = a ? `${a.title}. ${fmt(a.value)}${a.share !== undefined ? `, ${t.share.replace('{share}', pct(a.share))}` : ''}` : '';
  const table =
    variant === 'clock-heatmap' ? (
      <ChartTable caption={name} head={[t.day, ...Array.from({ length: 24 }, (_, h) => String(h))]} rows={rows.map((r, i) => [dayNames[i], ...r.map(fmt)])} />
    ) : variant === 'sunburst' ? (
      <ChartTable caption={name} head={[t.part, t.group, t.value]} rows={groups.flatMap((g) => (g.children ?? []).map((c) => [c.label, g.label, fmt(c.value)]))} />
    ) : (
      <ChartTable caption={name} head={[variant === 'hours' ? t.hourShort : t.month, unitLabel || t.value]} rows={wedges.map((w) => [w.title, fmt(w.value)])} />
    );

  const peakW = variant === 'hours' ? wedges.reduce((m, w, i) => (w.value > (wedges[m]?.value ?? -1) ? i : m), 0) : -1;

  return (
    <ChartFigure title={title} value={value} delta={delta} meta={meta} source={source} framed={framed} className={className}>
      <ChartRoot anim={anim} rootRef={rootRef}>
        <div ref={wrapRef} className={cn('flex flex-col items-center gap-6', variant === 'sunburst' && width >= 640 && 'grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)] items-center')}>
          <ChartSurface
            label={name}
            summary={summary ?? `${t.total} ${fmt(total)}`}
            labels={labels}
            live={live}
            table={table}
            onKeyDown={onKeyDown}
            onFocus={(e) => e.target === e.currentTarget && active === null && e.currentTarget.matches(':focus-visible') && setActive(0)}
            onBlur={() => setActive(null)}
            onPointerMove={(e) => pick(e.clientX, e.clientY, e.currentTarget)}
            onPointerDown={(e) => pick(e.clientX, e.clientY, e.currentTarget)}
            onPointerLeave={(e) => e.pointerType === 'mouse' && setActive(null)}
            className="mx-auto w-full"
            style={{ maxWidth: size }}
          >
            <svg width="100%" viewBox={`0 0 ${size} ${size}`} aria-hidden className="block overflow-visible">
              {/* Guides */}
              {(variant === 'hours' || variant === 'rose') && (
                <g>
                  {[0.25, 0.5, 0.75, 1].map((q) => (
                    <circle key={q} cx={cx} cy={cy} r={round(variant === 'rose' ? inner + Math.sqrt(q) * (R - inner) : inner + q * (R - inner))} fill="none" stroke="var(--kitch-grid)" strokeWidth="1" />
                  ))}
                  <circle cx={cx} cy={cy} r={round(inner)} fill="none" stroke="var(--kitch-axis)" strokeWidth="1" />
                </g>
              )}
              {wedges.map((w, i) => (
                <path
                  key={i}
                  d={sector(cx, cy, w.r0, w.r1, w.a0, w.a1)}
                  fill={w.fill}
                  opacity={lit(i) ? 1 : 0.32}
                  stroke={active === i ? 'var(--foreground)' : 'none'}
                  strokeWidth={active === i ? 1.5 : 0}
                  className={cn('transition-opacity duration-200', anim && (variant === 'clock-heatmap' ? 'kitch-cell' : 'kitch-pop'))}
                  style={{ ...d(variant === 'clock-heatmap' ? 60 + (i % 24) * 18 + w.ring * 40 : 80 + i * (variant === 'sunburst' ? 50 : 26)), transformOrigin: `${cx}px ${cy}px`, transformBox: 'view-box' }}
                />
              ))}
              {/* Clock and month labels */}
              {variant === 'hours' &&
                [0, 3, 6, 9, 12, 15, 18, 21].map((h) => {
                  const deg = (360 * h) / 24;
                  const [x, y] = polar(cx, cy, R + (anchorAt(deg) === 'middle' ? 14 : 8), deg);
                  return (
                    <text key={h} x={x} y={y} dy="0.34em" textAnchor={anchorAt(deg)} className={cn('text-[11px] tabular-nums', h % 6 === 0 ? 'font-medium' : '')} fill={h % 6 === 0 ? 'var(--foreground)' : 'var(--muted-foreground)'}>
                      {en && h % 6 !== 0 ? h % 12 : clock(h)}
                    </text>
                  );
                })}
              {variant === 'rose' &&
                wedges.map((_, i) => {
                  const [x, y] = polar(cx, cy, R + 14, (360 * (i + 0.5)) / 12);
                  return (
                    <text key={i} x={x} y={y} dy="0.34em" textAnchor="middle" className="text-[11px]" fill={active === i ? 'var(--foreground)' : 'var(--muted-foreground)'}>
                      {monthName(i).slice(0, 3)}
                    </text>
                  );
                })}
              {variant === 'clock-heatmap' &&
                [0, 6, 12, 18].map((h) => {
                  const deg = (360 * h) / 24;
                  const [x, y] = polar(cx, cy, R + (anchorAt(deg) === 'middle' ? 14 : 8), deg);
                  return (
                    <text key={h} x={x} y={y} dy="0.34em" textAnchor={anchorAt(deg)} className="text-[11px] font-medium tabular-nums" fill="var(--foreground)">
                      {clock(h)}
                    </text>
                  );
                })}
              {/* Center: what is picked, else the total */}
              <circle cx={cx} cy={cy} r={round(inner - 3)} fill="var(--kitch-surface)" />
              <text x={cx} y={cy - 7} textAnchor="middle" className="text-[11px]" fill="var(--muted-foreground)">
                {a ? (a.title.length > 18 ? `${a.title.slice(0, 17)}…` : a.title) : variant === 'hours' && peakW >= 0 ? t.peak : t.total}
              </text>
              <text x={cx} y={cy + 13} textAnchor="middle" className="text-[16px] font-semibold tabular-nums" fill="var(--foreground)">
                {a ? fmt(a.value) : variant === 'hours' && peakW >= 0 ? (en ? clock(peakW) : `${peakW} Uhr`) : fmt(total)}
              </text>
            </svg>
          </ChartSurface>
          {variant === 'clock-heatmap' && (
            <div className="flex w-full max-w-sm flex-wrap items-center justify-between gap-x-6 gap-y-2 text-[12px] text-muted-foreground">
              <span>{t.rings.replace('{a}', dayNames[0]).replace('{b}', dayNames[dayNames.length - 1])}</span>
              <span className="flex items-center gap-1.5">
                {t.less}
                {[0, 1, 2, 3, 4].map((s) => (
                  <span key={s} aria-hidden className="size-3 rounded-[3px]" style={{ background: rampColor(s) }} />
                ))}
                {t.more}
              </span>
            </div>
          )}
          {variant === 'sunburst' && (
            <ul className="grid w-full gap-1">
              {wedges.map((w, i) =>
                w.ring === 0 ? (
                  <li key={i}>
                    <button
                      type="button"
                      onPointerEnter={(e) => e.pointerType === 'mouse' && setActive(i)}
                      onPointerLeave={(e) => e.pointerType === 'mouse' && setActive(null)}
                      onFocus={() => setActive(i)}
                      onBlur={() => setActive(null)}
                      onClick={() => setActive(active === i ? null : i)}
                      className={cn('flex min-h-11 w-full items-center gap-2.5 rounded-md px-2.5 text-left text-[13px] transition-colors', active === i || wedges[active ?? -1]?.parent === i ? 'bg-accent' : 'hover:bg-accent/60')}
                    >
                      <span aria-hidden className="size-2.5 shrink-0 rounded-[3px]" style={{ background: w.fill }} />
                      <span className="min-w-0 flex-1 truncate text-foreground">{w.title}</span>
                      <span className="font-medium tabular-nums text-foreground">{fmt(w.value)}</span>
                      <span className="w-10 text-right tabular-nums text-muted-foreground">{pct(w.share ?? 0)}</span>
                    </button>
                  </li>
                ) : null,
              )}
            </ul>
          )}
          {variant === 'hours' && <p className="text-[12px] text-muted-foreground">{unitLabel}</p>}
        </div>
      </ChartRoot>
    </ChartFigure>
  );
}

Häufige Fragen

Wie installiere ich Kreisdiagramm für Zyklen?

Mit aiw add @ai-website/radial-chart. Der Befehl legt die Datei components/ui/radial-chart.tsx in dein Projekt, der Code gehört danach dir. Mit dabei sind chart und chart-figure. Weitere npm-Pakete braucht sie nicht.

Welche Varianten hat Kreisdiagramm für Zyklen?

4 Varianten, Stundenuhr, Monatsrose, Ring-Heatmap und Sunburst. Du wählst sie mit der Prop variant, zum Beispiel variant="rose".

Kann ich die Texte von Kreisdiagramm für Zyklen übersetzen?

Ja. Kreisdiagramm für Zyklen bringt 13 eigene Texte auf Deutsch und Englisch mit, etwa „Gesamt“, „Stärkste Stunde“ und „Monat“. Die Sprache folgt dem KitProvider, einzelne Texte ersetzt du über die Prop labels.

Kann ein KI-Assistent Kreisdiagramm für Zyklen einbauen?

Ja. Über den MCP-Server der aiw CLI finden Claude, Cursor, GitHub Copilot, Windsurf, Codex und Gemini Kreisdiagramm für Zyklen mit aiw_components und installieren sie mit aiw_add @ai-website/radial-chart. Nenn im Prompt die Variante, etwa variant="hours", dann schreibt der Assistent deine Inhalte direkt in die Props.

Kann ich Kreisdiagramm für Zyklen ohne Code anpassen?

Ja, im Studio. Alle 4 Varianten lassen sich dort bearbeiten, Texte, Listen und Schalter. Das Studio schreibt den passenden RadialChart-Code zum Kopieren.

Oft gesucht

Direkt zu

In die Zwischenablage kopiert