Skip to content

Commit

Permalink
proper activity rendering in calendar grid (vertically)
Browse files Browse the repository at this point in the history
  • Loading branch information
JulienR1 committed Feb 4, 2025
1 parent 1c639a7 commit 26c47dd
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 45 deletions.
11 changes: 10 additions & 1 deletion packages/common/src/models/activities.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { z } from "zod";

export const Activity = z.object({
export const Time = z.object({ hour: z.number(), minute: z.number() })

export const Activity = z.object({
type: z.number(),
title: z.string(),
subtitle: z.string().optional(),
time: z.object({ start: Time, end: Time }),
lessons: z.object({ count: z.number(), first: z.date(), last: z.date() }),
price: z.number(),
color: z.string()
})

export type Time = z.infer<typeof Time>
export type Activity = z.infer<typeof Activity>
44 changes: 40 additions & 4 deletions packages/web/src/components/Calendar.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
import type { Activity } from "common";
import { gcd } from "../lib/maths";
interface Props {
activities: Activity[][];
Expand All @@ -9,7 +10,25 @@ const { activities } = Astro.props;
const timeMin = 8;
const timeMax = 22;
const timeStep = 1;
const cellSize = 4;
const minutesPerTick = 30;
// NOTE: CSS grid does not allow to have fractional spans.
// Therefore, it is needed to scale it by the smallest time interval in every activity.
const minutes = [
...activities.flat(2).reduce(
(set, activity) => {
set.add(activity.time.start.minute);
set.add(activity.time.end.minute);
return set;
},
new Set<number>([minutesPerTick]),
),
].filter((min) => min !== 0);
const minuteScale = gcd(minutes);
const gridScale = minutesPerTick / minuteScale;
const timeStep = minutesPerTick / 60;
const cellScale = gridScale / timeStep;
function formatTime(time: number): string {
const hour = Math.floor(time);
Expand All @@ -22,7 +41,7 @@ function formatTime(time: number): string {
<div
class="grid grid-cols-[auto_repeat(7,1fr)]"
style={{
"grid-template-rows": `auto repeat(${(timeMax - timeMin) / timeStep}, 4rem)`,
"grid-template-rows": `auto repeat(${(timeMax - timeMin) * cellScale}, ${cellSize / cellScale}rem)`,
}}
>
{
Expand Down Expand Up @@ -51,7 +70,7 @@ function formatTime(time: number): string {
(_, row) => (
<div
class="col-span-full gridline"
style={{ width: "100%", "grid-row": row + 2 }}
style={{ width: "100%", "grid-row": row * gridScale + 2 }}
/>
),
)
Expand All @@ -61,14 +80,31 @@ function formatTime(time: number): string {
Array.from({ length: (timeMax - timeMin) / timeStep + 1 }).map((_, i) => (
<div
class="flex items-center col-start-1 -translate-y-1/2 pr-2"
style={{ "grid-row": i + 2 }}
style={{ "grid-row": i * gridScale + 2 }}
>
<span class="text-sm bg-white pr-1">
{formatTime(timeMin + i * timeStep)}
</span>
</div>
))
}

{
// --- Activity squares ---
activities.map((list, weekday) =>
list.map((activity) => (
<div
style={{
background: activity.color,
"grid-row": `${(activity.time.start.hour - timeMin) * cellScale + activity.time.start.minute / minuteScale + 2} / span ${(activity.time.end.hour - activity.time.start.hour) * cellScale + (activity.time.end.minute - activity.time.start.minute) / minuteScale}`,
"grid-column": ((weekday + 6) % 7) + 2,
}}
>
{activity.title} - {activity.subtitle}
</div>
)),
)
}
</div>
</div>

Expand Down
17 changes: 1 addition & 16 deletions packages/web/src/lib/maths.test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,7 @@
import { describe, expect, test, } from "vitest";
import { gcd, lcm } from "./maths";
import { gcd, } from "./maths";

describe("maths", function() {
test("lcm", function() {
const tests = [
{ inputs: [1], expected: 1 },
{ inputs: [1, 2], expected: 2 },
{ inputs: [2, 3], expected: 6 },
{ inputs: [2, 4], expected: 4 },
{ inputs: [2, 3, 4], expected: 12 },
{ inputs: [1, 3, 5, 8], expected: 120 },
]

tests.forEach((test, i) => {
expect(lcm(test.inputs)).to.equal(test.expected, `tests[${i}]: expected ${test.inputs} to have ${test.expected} as lcm`);
})
})

test("gcd", function() {
const tests = [
{ inputs: [1], expected: 1 },
Expand Down
22 changes: 0 additions & 22 deletions packages/web/src/lib/maths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,3 @@ export function gcd(n: number[]): number {
}
return stack[0]
}

/**
* Lowest Common Multiple
*/
export function lcm(n: number[]): number {
if (n.length === 0) {
return 0
}

if (n.length <= 2) {
const product = n.reduce((p, curr) => p * curr, 1)
return product / gcd(n)
}

const stack = [...n]
while (stack.length >= 2) {
let a = stack.pop()!
let b = stack.pop()!
stack.push(lcm([a, b]))
}
return stack[0]
}
19 changes: 17 additions & 2 deletions packages/web/src/pages/horaire.astro
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,34 @@ const activities: Activity[][] = [
type: 0,
title: "Récréatif",
subtitle: "6 ans et plus",
time: { start: 9, end: 10.5 },
time: { start: { hour: 8, minute: 0 }, end: { hour: 10, minute: 45 } },
lessons: {
count: 8,
first: new Date(2025, 1, 6),
last: new Date(2025, 5, 5),
},
price: 120,
color: "#ff00ff",
},
],
[],
[],
[],
[],
[
{
type: 0,
title: "Récréatif",
subtitle: "12 ans et plus",
time: { start: { hour: 10, minute: 15 }, end: { hour: 12, minute: 45 } },
lessons: {
count: 8,
first: new Date(2025, 1, 6),
last: new Date(2025, 5, 5),
},
price: 120,
color: "#ff0000",
},
],
[],
];
---
Expand Down

0 comments on commit 26c47dd

Please sign in to comment.