Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | 103x 103x 103x 103x 103x 5x 58x 58x 58x 58x 58x 5x 144x 144x 144x 144x 144x 144x 5x 55x 10x 72x 72x 72x 72x 72x 10x 58x 58x 58x 174x 174x 58x 58x 5x 5x 5x 5x 29x | import type { CSSProperties } from "react";
import type { ParsedChartDataset } from "../../model/types";
import { numberValue, stringField } from "../../model/valueParsing";
import { COMPARISON_CHART_COST_BREAKDOWN } from "../constants";
const COMPARISON_CHART_DEFAULT_HEIGHT = 320;
const COST_BREAKDOWN_CHART_PADDING_HEIGHT = 92;
const COST_BREAKDOWN_ROW_HEIGHT = 58;
const COST_BREAKDOWN_TICK_LINE_HEIGHT = 20;
const COST_BREAKDOWN_TICK_MAX_CHARS = 32;
export type ComparisonChartRow = {
label: string | number;
unit: string;
sourceRows: Record<string, { unit?: string }>;
[seriesKey: string]: unknown;
};
export function comparisonChartHeightStyle(
dataset: ParsedChartDataset,
): CSSProperties | undefined {
if (dataset.chart_key !== COMPARISON_CHART_COST_BREAKDOWN) {
return undefined;
}
return { height: comparisonChartHeight(dataset) };
}
export function costBreakdownTickLines(value: unknown) {
const rawLines = String(value ?? "")
.split(/\n+/)
.map((line) => line.trim())
.filter(Boolean);
return rawLines.flatMap((line, index) => {
if (index !== 0) {
return wrapCostBreakdownTickLine(line);
}
const separatorIndex = line.indexOf(": ");
if (separatorIndex < 0) {
return wrapCostBreakdownTickLine(line);
}
return [
line.slice(0, separatorIndex + 1),
...wrapCostBreakdownTickLine(line.slice(separatorIndex + 2)),
];
});
}
export function cashFlowOverlayRows(
dataset: ParsedChartDataset,
): ComparisonChartRow[] {
const rowsByLabel = new Map<number, ComparisonChartRow>();
for (const series of dataset.series) {
for (const point of series.points) {
const label = numberValue(point.metadata.year);
Iif (label == null) continue;
const row =
rowsByLabel.get(label) ??
({
label,
unit: point.unit || series.unit,
sourceRows: {},
} as ComparisonChartRow);
row[series.key] = point.value;
row.sourceRows[series.key] = { unit: point.unit || series.unit };
rowsByLabel.set(label, row);
}
}
return Array.from(rowsByLabel.values()).sort(
(left, right) => Number(left.label) - Number(right.label),
);
}
export function costBreakdownRows(
dataset: ParsedChartDataset,
): ComparisonChartRow[] {
const rowsByLabel = new Map<string, ComparisonChartRow>();
for (const series of dataset.series) {
for (const point of series.points) {
const label =
stringField(point.metadata.category_label) || point.label || point.key;
const row =
rowsByLabel.get(label) ??
({
label,
unit: point.unit || series.unit,
sourceRows: {},
} as ComparisonChartRow);
row[series.key] = point.value;
row.sourceRows[series.key] = { unit: point.unit || series.unit };
rowsByLabel.set(label, row);
}
}
return Array.from(rowsByLabel.values());
}
function wrapCostBreakdownTickLine(line: string) {
const words = line.split(/\s+/).filter(Boolean);
if (words.length === 0) {
return [];
}
const lines: string[] = [];
let current = "";
for (const word of words) {
const next = current ? `${current} ${word}` : word;
if (current && next.length > COST_BREAKDOWN_TICK_MAX_CHARS) {
lines.push(current);
current = word;
} else {
current = next;
}
}
if (current) {
lines.push(current);
}
return lines;
}
function comparisonChartHeight(dataset: ParsedChartDataset) {
const rows = costBreakdownRows(dataset);
const rowHeight = Math.max(
COST_BREAKDOWN_ROW_HEIGHT,
maxCostBreakdownTickLineCount(rows) * COST_BREAKDOWN_TICK_LINE_HEIGHT + 16,
);
return Math.max(
COMPARISON_CHART_DEFAULT_HEIGHT,
rows.length * rowHeight + COST_BREAKDOWN_CHART_PADDING_HEIGHT,
);
}
function maxCostBreakdownTickLineCount(rows: ComparisonChartRow[]) {
return Math.max(
1,
...rows.map((row) => costBreakdownTickLines(row.label).length),
);
}
|