mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
fix(charts): mark only the chart's own point days in the day rug
On an index or band axis the rug placed every day that holds anything by interpolating it between the two nearest points. A sparse series beside a daily one squeezed a week of other days into one gap, and the dots ran together into bars. A day now gets a dot on those axes only when it is one of the chart's points; a time axis keeps every day of its span.
This commit is contained in:
1 parent
9d1147a052
commit
d5ca594bd7
2 files changed
+72
-9
No files matched your search
@@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
chartPointDayKey,
|
||||
rugFraction,
|
||||
rugEntries,
|
||||
rugPosition,
|
||||
timeRugFraction,
|
||||
} from "../chart-day";
|
||||
@@ -191,3 +192,36 @@ describe("rug on a time axis drawn from real instants", () => {
|
||||
expect(timeRugFraction("2026-03-06", points, days)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("rugEntries", () => {
|
||||
const points = ["2026-09-01", "2026-09-04", "2026-09-08", "2026-09-12"].map(
|
||||
(day) => ({ timestamp: noon(day) }),
|
||||
);
|
||||
const everyDay = Array.from(
|
||||
{ length: 12 },
|
||||
(_, i) => `2026-09-${String(i + 1).padStart(2, "0")}`,
|
||||
);
|
||||
|
||||
it("marks only the chart's own points on an index axis", () => {
|
||||
// Days between two points have no place on an axis that spaces points;
|
||||
// interpolated, a week of them ran together into a bar in one gap.
|
||||
const entries = rugEntries(everyDay, points, "index");
|
||||
expect(entries.map((e) => e.day)).toEqual([
|
||||
"2026-09-01",
|
||||
"2026-09-04",
|
||||
"2026-09-08",
|
||||
"2026-09-12",
|
||||
]);
|
||||
expect(entries.map((e) => e.at)).toEqual([0, 1 / 3, 2 / 3, 1]);
|
||||
});
|
||||
|
||||
it("keeps every day of the span on a time axis", () => {
|
||||
expect(rugEntries(everyDay, points, "time")).toHaveLength(12);
|
||||
});
|
||||
|
||||
it("never puts two dots closer than one point step on an index axis", () => {
|
||||
const entries = rugEntries(everyDay, points, "index");
|
||||
const gaps = entries.slice(1).map((e, i) => e.at - entries[i]!.at);
|
||||
expect(Math.min(...gaps)).toBeCloseTo(1 / (points.length - 1));
|
||||
});
|
||||
});
|
||||
@@ -168,6 +168,38 @@ export function timeRugFraction(
|
||||
return Math.min(1, Math.max(0, (t - first) / (last - first)));
|
||||
}
|
||||
|
||||
/**
|
||||
* The rug's dots: each day that holds anything, where it sits along the plot.
|
||||
*
|
||||
* On an index or band axis a day has a place only if it is one of the
|
||||
* chart's points, because the axis spaces points, not days. A day between
|
||||
* two points used to be interpolated into the gap between them, so a sparse
|
||||
* series (blood pressure taken twice a week beside a mood logged every day)
|
||||
* squeezed a week of other days into one gap and their dots ran together
|
||||
* into bars. A time axis spaces days and keeps every day of its span.
|
||||
*/
|
||||
export function rugEntries(
|
||||
days: readonly string[],
|
||||
points: readonly RugPoint[],
|
||||
axis: ChartDayAxis,
|
||||
pointDays?: ReadonlyArray<DateKey | null>,
|
||||
): Array<{ day: string; at: number }> {
|
||||
const onPoints =
|
||||
axis === "time"
|
||||
? null
|
||||
: new Set(points.map((point) => chartPointDayKey(point.timestamp)));
|
||||
return days
|
||||
.filter((day) => onPoints === null || onPoints.has(day))
|
||||
.map((day) => ({
|
||||
day,
|
||||
at:
|
||||
axis === "time" && pointDays !== undefined
|
||||
? timeRugFraction(day as DateKey, points, pointDays)
|
||||
: rugFraction(day as DateKey, points, axis),
|
||||
}))
|
||||
.filter((entry): entry is { day: string; at: number } => entry.at !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The row of day dots under a daily chart. `insetLeft` / `insetRight` are the
|
||||
* pixels between the chart box and the first and last point (margin, axis,
|
||||
@@ -216,15 +248,12 @@ export function DayRug({
|
||||
const index = useDayIndex(from, to);
|
||||
const data = index.data;
|
||||
const notable = new Set(data?.notable ?? []);
|
||||
const days = Object.keys(data?.days ?? {})
|
||||
.filter((day) => isOpenableDay(day, today))
|
||||
.map((day) => ({
|
||||
day,
|
||||
at: byDays
|
||||
? timeRugFraction(day, points, pointDays)
|
||||
: rugFraction(day, points, axis),
|
||||
}))
|
||||
.filter((entry): entry is { day: string; at: number } => entry.at !== null);
|
||||
const days = rugEntries(
|
||||
Object.keys(data?.days ?? {}).filter((day) => isOpenableDay(day, today)),
|
||||
points,
|
||||
axis,
|
||||
byDays ? pointDays : undefined,
|
||||
);
|
||||
return (
|
||||
<div
|
||||
aria-hidden="true"
|
||||
|
||||
Reference in new issue
Block a user