85 lines
2.0 KiB
Svelte
85 lines
2.0 KiB
Svelte
<script lang="ts">
|
|
import * as Plot from '@observablehq/plot';
|
|
import type { Attachment } from 'svelte/attachments';
|
|
import type { ClassValue } from 'svelte/elements';
|
|
import * as race from '$lib/race';
|
|
import * as racecourse from '$lib/data/racecourse';
|
|
|
|
export interface DrawSpan {
|
|
fy: string;
|
|
x1: number;
|
|
x2: number;
|
|
color: string;
|
|
label?: string;
|
|
}
|
|
|
|
interface Props {
|
|
course: racecourse.Course;
|
|
title?: string;
|
|
class?: ClassValue | null;
|
|
plotOptions?: Omit<Plot.PlotOptions, 'marks' | 'x' | 'y'>;
|
|
draw: DrawSpan[];
|
|
}
|
|
|
|
let { course, title, class: className, plotOptions, draw }: Props = $props();
|
|
|
|
let width = $state(0);
|
|
let height = $state(0);
|
|
|
|
const phases = $derived(race.phases(course.distance));
|
|
const phaseX = $derived([
|
|
0,
|
|
phases[race.Phase.EarlyRace],
|
|
phases[race.Phase.MidRace],
|
|
phases[race.Phase.LateRace],
|
|
phases[race.Phase.LastSpurt],
|
|
]);
|
|
const sectionX = $derived([0, ...racecourse.SECTIONS.map((s) => course.distance * s.endRate)]);
|
|
|
|
const drawTitles = $derived(draw.map((d) => d.fy));
|
|
|
|
const makeChart: Attachment = (el) => {
|
|
$effect(() => {
|
|
el?.firstChild?.remove();
|
|
el?.append(
|
|
Plot.plot({
|
|
width: width,
|
|
height,
|
|
clip: true,
|
|
title,
|
|
marginLeft: 50,
|
|
x: {
|
|
domain: [0, course.distance],
|
|
ticks: phaseX,
|
|
},
|
|
fy: {
|
|
label: null,
|
|
padding: 0,
|
|
domain: drawTitles,
|
|
},
|
|
...plotOptions,
|
|
marks: [
|
|
Plot.barX(draw, {
|
|
x1: 'x1',
|
|
x2: 'x2',
|
|
fy: 'fy',
|
|
fill: 'color',
|
|
fillOpacity: 0.8,
|
|
title: 'label',
|
|
}),
|
|
Plot.text(draw, { x: 'x1', fy: 'fy', text: 'label', dx: 2, textAnchor: 'start', fontSize: 16, fill: 'black' }),
|
|
Plot.ruleX(sectionX, { opacity: 0.25 }),
|
|
Plot.ruleX([phases[0], phases[1]], { opacity: 0.5 }),
|
|
],
|
|
}),
|
|
);
|
|
});
|
|
};
|
|
</script>
|
|
|
|
<div bind:clientWidth={width} bind:clientHeight={height} class={['flex h-full w-full flex-col md:flex-row', className]}>
|
|
<div role="img" {@attach makeChart}>
|
|
<span>the chart seems to have didn't</span>
|
|
</div>
|
|
</div>
|