metis/src/App.svelte

212 lines
5.3 KiB
Svelte
Raw Normal View History

2021-11-15 01:34:24 +01:00
<script>
2022-02-20 19:16:33 +01:00
import { onMount } from 'svelte';
import { writable } from 'svelte/store';
2021-11-15 01:34:24 +01:00
import FullCalendar from 'svelte-fullcalendar';
import timeGridPlugin from '@fullcalendar/timegrid';
import adaptivePlugin from '@fullcalendar/adaptive';
2022-02-20 22:29:15 +01:00
import rrulePlugin from '@fullcalendar/rrule';
import dayGridPlugin from '@fullcalendar/daygrid';
import listPlugin from '@fullcalendar/list';
import resourceTimelinePlugin from '@fullcalendar/resource-timeline';
2021-11-15 01:34:24 +01:00
import frLocale from '@fullcalendar/core/locales/fr';
2022-03-06 00:59:36 +01:00
import EventModal from './EventModal.svelte';
import FilterBar from './FilterBar.svelte';
import { mkSource, calendarTree, initialCalendars, getSubCalendars } from './calendar';
import { debounce } from 'lodash';
2022-03-13 18:35:47 +01:00
import Help from './Help.svelte';
2021-11-15 01:34:24 +01:00
2022-03-06 00:14:03 +01:00
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap-icons/font/bootstrap-icons.css';
import bootstrap5Plugin from '@fullcalendar/bootstrap5';
import { Tooltip } from 'bootstrap';
import { createPopper } from '@popperjs/core';
2022-03-06 00:14:03 +01:00
import ENSLocations from './static-ens-locations.json';
2022-03-06 00:59:36 +01:00
const event = writable(null);
let openModal = false;
const toggle = () => (openModal = !openModal);
2022-03-06 16:13:05 +01:00
const mobile = window.innerWidth < 765;
2022-03-07 11:41:32 +01:00
const now = new Date();
const scrollTo = (() => {
const time = new Date();
time.setHours(Math.max(0, time.getHours() - 2));
return time.toLocaleTimeString();
})();
2022-03-06 00:59:36 +01:00
const allowedViews = [
'resourceTimelineDay',
'dayGridMonth',
'timeGridWeek',
'timeGridDay',
'listWeek'
];
const search = new URL(document.location).searchParams;
const params = search.getAll('c');
const date = new Date(search.get('d'));
const view = search.get('v');
2022-07-26 00:09:19 +02:00
const headers = mobile
? {
left: 'title',
center: 'prev,today,next',
right: 'resourceTimelineDay dayGridMonth,timeGridWeek,timeGridDay,listWeek'
2022-07-26 00:09:19 +02:00
}
: {
left: 'prev,next today',
center: 'title',
right: 'resourceTimelineDay dayGridMonth,timeGridWeek,timeGridDay,listWeek'
2022-07-26 00:09:19 +02:00
};
let calendar;
let options = writable({
initialView: allowedViews.includes(view)
? view
: mobile
? 'listWeek'
: 'timeGridWeek',
initialDate: date.toString() === 'Invalid Date' ? now : date,
2022-03-06 00:14:03 +01:00
plugins: [
timeGridPlugin,
dayGridPlugin,
rrulePlugin,
listPlugin,
resourceTimelinePlugin,
2022-03-06 00:14:03 +01:00
adaptivePlugin,
bootstrap5Plugin
],
2021-11-15 01:34:24 +01:00
locale: frLocale,
2022-03-06 15:07:50 +01:00
allDayContent: '',
2022-07-26 00:09:19 +02:00
headerToolbar: headers,
buttonText: { resourceTimelineDay: 'Salles' },
scrollTime: '08:00:00',
resourceGroupField: 'building',
resourceAreaWidth: '27%',
2022-07-26 02:32:26 +02:00
resources: Object.entries(ENSLocations).flatMap(([building, rooms]) =>
rooms.map(room => ({
id: `${building}-${room}`,
building,
title: room
2022-07-26 02:32:26 +02:00
}))
),
2022-03-06 00:14:03 +01:00
height: '100%',
2022-02-20 22:29:15 +01:00
schedulerLicenseKey: 'CC-Attribution-NonCommercial-NoDerivatives',
nowIndicator: true,
2022-03-07 11:41:32 +01:00
now: now,
scrollTime: scrollTo,
scrollTimeReset: false,
eventClick: info => {
2022-03-06 00:59:36 +01:00
openModal = true;
event.set(info.event);
},
2022-03-06 16:13:05 +01:00
titleFormat: {
year: mobile ? '2-digit' : 'numeric',
month: mobile ? 'numeric' : 'long',
day: 'numeric'
},
eventSources: [],
themeSystem: 'bootstrap5',
nextDayThreshold: '05:00:00',
2022-07-26 02:32:26 +02:00
progressiveEventRendering: true,
expandRows: true,
eventDidMount: info => {
new Tooltip(info.el, {
title: info.event.extendedProps.short_name,
placement: 'top'
});
}
});
2022-02-20 19:16:33 +01:00
const flatten = d => {
let array = [];
if (!d) {
return [];
}
if (Array.isArray(d)) {
d.forEach(a => (array = array.concat(flatten(a))));
} else {
for (const [n, s] of Object.entries(d)) {
array = array.concat(n, flatten(s));
}
}
return array;
};
let selectedCalendars = [];
const initial =
params.length > 0
? flatten(params.map(cal => getSubCalendars(cal))).concat(params)
: initialCalendars;
const updateEvents = debounce(calendars => {
options.update(opts => ({
...opts,
eventSources: selectedCalendars.map(mkSource).filter(x => !!x)
}));
}, 300);
$: updateEvents(selectedCalendars);
2021-11-15 01:34:24 +01:00
</script>
2022-03-06 16:13:05 +01:00
<div class="h-100 d-flex flex-column">
<h1 class="mt-3 title text-center">Calendrier de la vie étudiante à l'ENS</h1>
2022-03-06 15:07:50 +01:00
2022-03-13 18:35:47 +01:00
<Help />
<FilterBar {calendarTree} bind:selected={selectedCalendars} {initial} />
2022-03-06 15:07:50 +01:00
<FullCalendar bind:this={calendar} options={$options} />
2022-03-06 15:07:50 +01:00
<EventModal event={$event} open={openModal} {toggle} />
2021-11-15 01:34:24 +01:00
</div>
<style>
2022-03-13 18:35:47 +01:00
:global(.fs-7) {
font-size: 0.9rem !important;
}
:global(.st-tentative) {
opacity: 40%;
}
:global(.st-cancelled) {
background: repeating-linear-gradient(45deg, #333, #333 10px, #950 10px, #950 20px);
}
2022-03-07 11:36:13 +01:00
:global(.modal-open) {
padding-right: 8px !important;
}
2022-03-06 21:21:31 +01:00
:global(.fc-event) {
cursor: pointer;
}
:global(.modal-body p:last-child, ul:last-child) {
margin-bottom: 0 !important;
}
2022-07-26 00:09:19 +02:00
:global(.fc-toolbar-chunk:not(:last-child)) {
margin-bottom: 0.25em;
}
2022-03-06 16:13:05 +01:00
@media (max-width: 765px) {
:global(.fc-header-toolbar) {
flex-direction: column;
}
:global(.fc-toolbar-title) {
margin-bottom: 0.25em !important;
}
:global(.fc-toolbar-chunk) {
display: flex;
justify-content: space-evenly;
width: 100%;
}
}
2021-11-15 01:34:24 +01:00
</style>