import { escapeHtml } from '../shared/html';
/**
* Static SVG/HTML chart primitives of the Technical Report in the ASSESSOR chart language: full
* 360-degree donuts with a neutral track, flat arc ends from 12 o'clock and a narrow mark for a
* positive share below 2 %; flat bars without gradients or shadows. Colours are token references only.
*/
const esc = escapeHtml;
const RING_RADIUS = 48;
const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS;
export const formatCount = (value: number): string => value.toLocaleString('de-DE');
/** Whole percentage from the unrounded share; a positive share below 1 % reads `<1 %`. */
export function formatShare(part: number, whole: number): string {
if (whole <= 0) return '—';
const share = 100 * part / whole;
if (part > 0 && Math.round(share) === 0) return '<1 %';
return `${formatCount(Math.round(share))} %`;
}
function ringArc(share: number, tone: string): string {
if (share <= 0) return '';
const clamped = Math.min(1, share);
const arc = ``;
// A positive share below 2 % keeps a narrow radial mark so it stays visible at the compact ring size.
const mark = clamped < 0.02 ? `` : '';
return arc + mark;
}
/** Count/percentage donut: bold absolute count above the regular-weight share. */
export function countDonut(title: string, part: number, whole: number, tone: string, basis: string): string {
return `${esc(title)}
${formatCount(part)}${formatShare(part, whole)}
${esc(basis)}
`;
}
export interface DonutSegment { value: number; tone: string; label: string }
/**
* Segmented distribution donut: categorical arcs in the given order, clockwise from 12 o'clock on the
* same track and stroke as every other donut, closing the ring when they sum to `whole`. The readout
* shows the given count and share; a legend lists every segment with its count.
*/
export function segmentDonut(title: string, segments: DonutSegment[], whole: number, readout: { value: number; share: string }): string {
let start = 0;
const arcs = whole > 0 ? segments.filter(item => item.value > 0).map(item => {
const length = item.value / whole * RING_CIRCUMFERENCE;
const arc = `${esc(`${item.label}: ${formatCount(item.value)}`)}`;
start += length;
return arc;
}).join('') : '';
return `${esc(title)}