|
1 | | -import React, { useEffect, useState } from 'react'; |
| 1 | +/* |
| 2 | + * Copyright (c) 2025 Tuomas Ahola <taahol@utu.fi> |
| 3 | + * |
| 4 | + * Permission to use, copy, modify, and distribute this software for any |
| 5 | + * purpose with or without fee is hereby granted, provided that the above |
| 6 | + * copyright notice and this permission notice appear in all copies. |
| 7 | + * |
| 8 | + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 9 | + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 10 | + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 11 | + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 12 | + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 13 | + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 14 | + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 15 | + */ |
2 | 16 |
|
3 | | -import { Calendar, dateFnsLocalizer } from 'react-big-calendar'; |
| 17 | +import { addDays, addMonths, startOfDay, calendarPageRange } from '../../lib/dateUtils.js'; |
| 18 | +import { getCalendarEventsByDay } from '../../lib/calendarUtils.js'; |
4 | 19 |
|
5 | | -import { getCalendarEvents } from '../../lib/calendarUtils.js'; |
6 | | - |
7 | | -import format from 'date-fns/format'; |
8 | | -import parse from 'date-fns/parse'; |
9 | | -import startOfWeek from 'date-fns/startOfWeek'; |
10 | | -import getDay from 'date-fns/getDay'; |
11 | | -import { fi } from 'date-fns/locale' |
12 | | -const locales = { |
13 | | - 'fi': fi, |
| 20 | +const calendarPage = (date) => { |
| 21 | + const pageRange = calendarPageRange(date); |
| 22 | + const page = []; |
| 23 | + for (var i=0, tmp=[], day=new Date(pageRange[0]); day<=pageRange[1]; addDays(day, 1)) { |
| 24 | + tmp.push(new Date(day)); |
| 25 | + if (++i % 7 == 0) { |
| 26 | + page.push(Array(...tmp)); |
| 27 | + tmp.length=0; |
| 28 | + } |
| 29 | + } |
| 30 | + return page; |
14 | 31 | } |
15 | | -const localizer = dateFnsLocalizer({ |
16 | | - format, |
17 | | - parse, |
18 | | - startOfWeek, |
19 | | - getDay, |
20 | | - locales, |
21 | | -}); |
22 | 32 |
|
23 | | -function GoogleCalendar() { |
24 | | - const [events, setEvents] = useState([]); |
| 33 | +export const renderCalendar = (events, date=new Date(), document=window.document) => { |
| 34 | + const page = calendarPage(new Date(date)); |
| 35 | + const wrapper = document.createElement("div"); |
| 36 | + const table = document.createElement("table"); |
| 37 | + const caption = document.createElement("caption"); |
| 38 | + const tableHeader = document.createElement("tr"); |
| 39 | + caption.appendChild(document.createTextNode(new Intl.DateTimeFormat("fi-FI", { |
| 40 | + month: "long", |
| 41 | + year: "numeric", |
| 42 | + }).format(date))); |
| 43 | + table.appendChild(caption); |
| 44 | + Array("ma", "ti", "ke", "to", "pe", "la", "su").forEach((day) => { |
| 45 | + const th = document.createElement("th"); |
| 46 | + th.appendChild(document.createTextNode(day)); |
| 47 | + tableHeader.appendChild(th); |
| 48 | + }); |
| 49 | + table.appendChild(tableHeader); |
| 50 | + page.forEach((week) => { |
| 51 | + const row = document.createElement("tr"); |
| 52 | + week.forEach((day) => { |
| 53 | + const cell = document.createElement("td"); |
| 54 | + cell.id = day.toJSON(); |
| 55 | + cell.appendChild(document.createTextNode(day.getDate())); |
| 56 | + (events[cell.id] || []).forEach((event) => { |
| 57 | + const entry = document.createElement("div"); |
| 58 | + entry.title = Intl.DateTimeFormat("fi-FI", { |
| 59 | + dateStyle: "short", |
| 60 | + ...(!event.allDay && { |
| 61 | + timeStyle: "short" |
| 62 | + }), |
| 63 | + timeZone: "Europe/Helsinki", |
| 64 | + }).formatRange(new Date(event.start), new Date(event.end)); |
| 65 | + |
| 66 | + entry.appendChild(document.createTextNode(event.title)); |
| 67 | + cell.appendChild(entry); |
| 68 | + }); |
| 69 | + row.appendChild(cell); |
| 70 | + }); |
| 71 | + table.appendChild(row); |
| 72 | + }); |
| 73 | + if (document === window?.document) { |
| 74 | + renderButtons(events, date, wrapper, document).forEach(button => { |
| 75 | + wrapper.appendChild(button); |
| 76 | + }); |
| 77 | + } |
| 78 | + wrapper.appendChild(table); |
| 79 | + wrapper.id = "calendar"; |
| 80 | + return wrapper; |
| 81 | +} |
25 | 82 |
|
26 | | - useEffect(() => { |
27 | | - const callApi = async () => { |
28 | | - setEvents(await getCalendarEvents()); |
29 | | - } |
30 | | - callApi(); |
31 | | - }, []); |
| 83 | +const renderButtons = (events, date, element, document) => Array( |
| 84 | + { label: "Edellinen kuukausi", add: -1 }, |
| 85 | + { label: "Seuraava kuukausi", add: 1 } |
| 86 | +).map(b => { |
| 87 | + const button = document.createElement("button"); |
| 88 | + button.appendChild(document.createTextNode(b.label)); |
| 89 | + button.onclick = calendarBuilder(addMonths(new Date(date), b.add), element); |
| 90 | + return button; |
| 91 | +}); |
32 | 92 |
|
33 | | - return ( |
34 | | - <div className="CalendarWrapper"> |
35 | | - <Calendar |
36 | | - popup |
37 | | - localizer={localizer} |
38 | | - culture={'fi'} |
39 | | - events={events.map((event) => ({ |
40 | | - ...event, |
41 | | - start: new Date(event.start), |
42 | | - end: new Date(event.end), |
43 | | - }))} |
44 | | - startAccessor="start" |
45 | | - endAccessor="end" |
46 | | - style={{height: 755}} |
47 | | - /> |
48 | | - </div> |
49 | | - ); |
| 93 | +export const calendarBuilder = ( |
| 94 | + date = new Date(), |
| 95 | + element = document.getElementById("calendar"), |
| 96 | + events = getCalendarEventsByDay(date), |
| 97 | +) => async () => { |
| 98 | + element.replaceWith(renderCalendar(await events, date)); |
| 99 | + setBgColor(document.getElementById(startOfDay(new Date()).toJSON()), |
| 100 | + "var(--color-pink)"); |
50 | 101 | } |
51 | 102 |
|
52 | | -export default GoogleCalendar; |
| 103 | +const setBgColor = (element, color) => { |
| 104 | + if (element) |
| 105 | + element.style.backgroundColor = color; |
| 106 | +} |
0 commit comments