feat(web): Kalender som menupunkt, med Googles begivenheder lagt over egne opgaver

Samme gitter som Kommende, givet en liste mere at tegne. Ikke et gitter nummer
to, og grunden er den del, der er nem at få næsten rigtig: et mål skal afgøre
ud fra `dataTransfer.types` alene, om det tager imod, og to kopier af det er to
steder at være næsten rigtig. En opgave trukket her flyttes præcis som alle
andre steder, fordi det er den samme kode, der gør det.

En begivenhed er ikke en opgave, og cellen siger det før et eneste ord er
læst: en flade af farve, hvor en opgave er et kort med en farvet kant. Den kan
ikke trækkes — forbindelsen er læsende, og en chip, der letter fra siden og så
ikke vil lande, læses som en fejl frem for som en regel — og den er ikke en
knap, for der er ikke noget at lukke ved en andens møde. Farven er Googles egen
for den kalender, så to kalendere i ét gitter kendes fra hinanden på den farve,
personen allerede kender dem på.

Farven tjekkes frem for at blive troet: den ender i en inline `style`, og en
værdi, der ikke er en farve, kan lukke erklæringen og åbne en anden. Linket
tegnes kun, hvis det begynder med `https://` — et href er dér, hvor en
`javascript:`-adresse bliver til et klik, der kører.

Prøven bruger uge-visningen med vilje: en månedscelle skæres af ved tre chips,
og resten af suiten lægger opgaver på andre dage. Datoen læses ud af siden, ikke
ud af testprocessen, som de øvrige har lært at gøre.
This commit is contained in:
Kristian Wind
2026-08-20 07:22:25 +02:00
parent 987d4edfce
commit c3f2181efe
6 changed files with 857 additions and 8 deletions
+37
View File
@@ -835,6 +835,43 @@ function read() {
export const upcomingView = new UpcomingView();
/**
* Month or week on Kalender.
*
* Its own key rather than Kommende's, even though the two switchers look alike.
* They are not the same choice: Kommende opens on a list of the next seven days
* and offers a grid, and Kalender is the grid — sharing the value would mean
* choosing a week on one and finding the other had changed, and one of them would
* have to answer for the `list` mode the other does not have.
*
* In localStorage rather than on the account, and for the same reason as the
* sidebar's width: a grid of seven columns needs room, and the answer on a phone
* is not the answer on a wide monitor.
*/
const CALENDAR_MODES = ['week', 'month'];
class CalendarViewMode {
mode = $state(readCalendarMode());
set(next) {
if (!CALENDAR_MODES.includes(next)) return;
this.mode = next;
try {
localStorage.setItem('verdande:calendar', next);
} catch {
// Private browsing; the choice simply will not persist.
}
}
}
function readCalendarMode() {
if (typeof localStorage === 'undefined') return 'month';
const stored = localStorage.getItem('verdande:calendar');
return CALENDAR_MODES.includes(stored) ? stored : 'month';
}
export const calendarView = new CalendarViewMode();
/**
* Whether the lists show what has already been finished.
*