diff --git a/src/components/Icicle.astro b/src/components/Icicle.astro index b1da80c..6581a9d 100644 --- a/src/components/Icicle.astro +++ b/src/components/Icicle.astro @@ -14,9 +14,9 @@ import type { BothFlowsLayout, IcicleNode, } from "../lib/icicle.ts"; -import { VB_H, VB_W } from "../lib/icicle.ts"; +import { COL_RECTS as COL_RECTS_SSR, VB_H, VB_W } from "../lib/icicle.ts"; import { tileColor } from "../lib/colors.ts"; -import { fmtEuroCompact, fmtPercentage } from "../lib/format.ts"; +import { fmtEuroCompact } from "../lib/format.ts"; import type { Flow } from "../data/types.ts"; interface Props { @@ -104,6 +104,44 @@ const DEPTH_STYLE: Record = { const PAD_X = 18; // viewBox units const PAD_TOP = 12; // viewBox units, breathing room above the name const NAME_META_GAP = 8; + +// Absolute sibling gap (viewBox units). Applied as an inset on top +// and bottom of each bar AFTER the zoom transform, so the visible +// gap between adjacent siblings stays the same size at every zoom +// state instead of ballooning when bars magnify. +const GAP_Y = 2; + +// Per-depth max characters per name line. Computed from the +// narrowest column width (across all zoom states where labels +// render for that depth) divided by the nameSize × an avg-char +// fraction for Figtree (~0.55em). Slightly conservative so wraps +// still fit at the tightest state. Used by wrapText below. +const MAX_CHARS_PER_LINE: Record = { + 1: 30, + 2: 30, + 3: 22, + 4: 30, +}; + +/** Greedy word-wrap to a max character count. Keeps single + * oversized words on their own line rather than splitting them. */ +function wrapText(text: string, maxChars: number): string[] { + if (text.length <= maxChars) return [text]; + const words = text.split(/\s+/); + const lines: string[] = []; + let current = ""; + for (const word of words) { + const next = current ? `${current} ${word}` : word; + if (next.length <= maxChars) { + current = next; + } else { + if (current) lines.push(current); + current = word; + } + } + if (current) lines.push(current); + return lines; +} --- + {/* Sticky year axis — a thin row of year labels aligned + under the timeline bars. The CURRENT year's tick also + shows the same two-flow mini-histogram as the bars + above, so when the user scrolls past the full + histogram the active year's reading stays visible as + a compact indicator pinned to the top of the + viewport. */} + +