feature(Calendar Moodle): adds moodle svg and adopts month page view for moodle events

This commit is contained in:
SimonGschnell
2025-02-21 14:21:38 +01:00
parent 400ba2d7f9
commit ebbec03de4
7 changed files with 131 additions and 65 deletions
+34 -7
View File
@@ -69,25 +69,43 @@
display: grid;
}
.all-day-event-border{
.fhc-calendar-week-page .all-day-event-border{
box-shadow: 1px 1px 0 #dee2e6;
}
.all-day-event{
.fhc-calendar-week-page .all-day-event{
max-height: 75px;
overflow:scroll;
}
.all-day-event-container{
.fhc-calendar-week-page .all-day-event-container{
position: sticky;
top: 44px;
display:grid;
grid-template-columns:repeat(7,1fr);
width:100%;
box-shadow: 0px 1px 0px #dee2e6;
z-index:3;
}
.fhc-calendar-day-page .all-day-event-border {
box-shadow: 1px 1px 0 #dee2e6;
}
.fhc-calendar-day-page .all-day-event {
grid-column:2;
max-height: 75px;
overflow: scroll;
}
.fhc-calendar-day-page .all-day-event-container {
position: sticky;
top:0;
display: grid;
grid-template-columns: 3em 1fr;
width: 100%;
z-index: 3;
}
/* grid hour lines of the Stundenplan use box-shadow instead of border because box-shadow renders consistently on different viewports*/
.box-shadow-border{
box-shadow: 0 0 0 1px #dee2e6 !important;
@@ -113,8 +131,6 @@
min-height: 0;
}
.fhc-calendar-day-page > div {
/*transform: translate(-0.75em, -0.75em);*/
/*width: calc(100% + 1.5em);*/
height: calc(100% + 1.5em);
}
.fhc-calendar-day-page .flex-column > .flex-grow-1 {
@@ -126,7 +142,6 @@
.fhc-calendar-day-page .events {
display: grid;
grid-template-columns: 3em 1fr;
/*min-height: 266.6666666667%;*/
}
.fhc-calendar-day-page .events .day {
gap: 1px;
@@ -222,7 +237,19 @@
}
.fhc-highlight-day {
position:relative;
}
.fhc-highlight-day::before {
content:'';
position:absolute;
top:0;
bottom:0;
left:0;
right:0;
box-shadow: inset 0 0 0 2px black !important;
pointer-events: none;
z-index: 2;
}
.fhc-calendar-sm .fhc-calendar-month-page-day.active .no,
+41 -35
View File
@@ -61,13 +61,13 @@ export default {
},
immediate: true,
},
eventsPerDayAndHour: {
events: {
handler(newEvents) {
// if no event is selected, select the first event of the day
if (this.selectedEvent == null && newEvents[this.day.toDateString()]?.events.length > 0) {
let events = newEvents[this.day.toDateString()]?.events;
if (this.selectedEvent == null && newEvents[this.day.toDateString()].length > 0) {
let events = newEvents[this.day.toDateString()];
if (Array.isArray(events) && events.length > 0) {
this.setSelectedEvent(events[0].orig);
this.setSelectedEvent(events[0]);
}
}
},
@@ -298,8 +298,8 @@ export default {
this.$emit('input', event);
},
calcHourPosition(event) {
let height = this.$refs.eventcontainer.getBoundingClientRect().height;
let top = this.$refs.eventcontainer.getBoundingClientRect().top;
let height = this.$refs.events.getBoundingClientRect().height;
let top = this.$refs.events.getBoundingClientRect().top;
let position = event.clientY - top;
// position percentage of total height
let timePercentage = (position / height) * 100;
@@ -365,35 +365,18 @@ export default {
</div>
<div id="scroll g-0" style="height: 100%; overflow-y: scroll;">
<div ref="eventcontainer" class="position-relative flex-grow-1" @mousemove="calcHourPosition" @mouseleave="hourPosition = null" >
<div :id="hourGridIdentifier(hour)" v-for="hour in hours" :key="hour" class="position-absolute box-shadow-border" :style="hourGridStyle(hour)"></div>
<div ref="eventcontainer" class="position-relative flex-grow-1" >
<div class="all-day-event-container" >
<div class="all-day-event all-day-event-border" v-for="(day,dayindex) in eventsPerDayAndHour">
<template v-for="(events,_day) in allDayEvents" :key="_day">
<Transition>
<div v-if="hourPosition && !noEventsCondition" class="position-absolute border-top small" :style="indicatorStyle">
<span class="border border-top-0 px-2 bg-white">{{hourPositionTime}}</span>
</div>
</Transition>
<Transition>
<div v-if="lookingAtToday && !noEventsCondition" class="position-absolute border-top small" :style="curIndicatorStyle">
<span class="border border-top-0 px-2 bg-white">{{curTime}}</span>
</div>
</Transition>
<div>
<h1 v-if="noEventsCondition" class="m-0 text-secondary" ref="noEventsText" :style="noLvStyle">Keine Lehrveranstaltungen</h1>
<div :class="{'fhc-calendar-no-events-overlay':noEventsCondition, 'events':true}">
<div class="hours">
<div v-for="hour in hours" style="min-height:100px" :key="hour" class="text-muted text-end small" :ref="'hour' + hour">{{hour}}:00</div>
</div>
<div v-for="(day,dayindex) in eventsPerDayAndHour" :key="day" class=" day border-start" :style="dayGridStyle(day)">
<div class="position-absolute w-100" style="top:0; bottom:0;">
<div class="position-sticky d-grid " style="top:0;" v-for="(events,_day) in allDayEvents" :key="day">
<div v-if="dayindex == _day" v-for="event in events" :key="event" @click.prevent="eventClick(event)"
:selected="event == selectedEvent"
:style="{'background-color': event?.color, 'z-index':2, 'margin-bottom':'1px'}"
class="small rounded overflow-hidden fhc-entry"
v-contrast
>
<div class="d-none d-xl-block">
<div v-if="dayindex == _day" v-for="event in events" :key="event" class="d-grid m-1" style="top:0;" @click.prevent="eventClick(event)"
:selected="event == selectedEvent"
:style="{'background-color': event?.color, 'margin-bottom':'1px'}"
class="small rounded overflow-hidden fhc-entry"
v-contrast
>
<div class="d-none d-xl-block">
<slot name="dayPage" :event="event" :day="day" :mobile="false">
<p>this is a placeholder which means that no template was passed to the Calendar Page slot</p>
</slot>
@@ -403,9 +386,32 @@ export default {
<p>this is a placeholder which means that no template was passed to the Calendar Page slot</p>
</slot>
</div>
</div>
</div>
</template>
</div>
</div>
<div >
<h1 v-if="noEventsCondition" class="m-0 text-secondary" ref="noEventsText" :style="noLvStyle">Keine Lehrveranstaltungen</h1>
<div class="events position-relative" :class="{'fhc-calendar-no-events-overlay':noEventsCondition}" ref="events" @mousemove="calcHourPosition" @mouseleave="hourPosition = null">
<Transition>
<div v-if="hourPosition && !noEventsCondition" class="position-absolute border-top small" :style="indicatorStyle">
<span class="border border-top-0 px-2 bg-white">{{hourPositionTime}}</span>
</div>
</Transition>
<Transition>
<div v-if="lookingAtToday && !noEventsCondition" class="position-absolute border-top small" :style="curIndicatorStyle">
<span class="border border-top-0 px-2 bg-white">{{curTime}}</span>
</div>
</Transition>
<div :id="hourGridIdentifier(hour)" v-for="hour in hours" :key="hour" class="position-absolute box-shadow-border" :style="hourGridStyle(hour)"></div>
<div class="hours">
<div v-for="hour in hours" style="min-height:100px" :key="hour" class="text-muted text-end small" :ref="'hour' + hour">{{hour}}:00</div>
</div>
<div v-for="(day,dayindex) in eventsPerDayAndHour" :key="day" class=" day border-start" :style="dayGridStyle(day)">
<div v-if="lookingAtToday && !noEventsCondition" :style="overlayStyle"></div>
<div v-for="event in day.events" :key="event" :style="eventGridStyle(day,event)" v-contrast :selected="event.orig == selectedEvent" class="fhc-entry mx-2 small rounded overflow-hidden " >
<!-- desktop version of the page template, parent receives slotProp mobile = false -->
+5 -5
View File
@@ -138,15 +138,15 @@ export default {
<div v-for="day in weeks[0].days" :key="day" class="bg-light fw-bold border-top border-bottom text-center">
{{dayText[day]}}
</div>
<template v-for="week in weeks"
<template v-for="week in weeks"
:key="week.no">
<a href="#" v-if="showWeeks" class="fhc-calendar-month-page-weekday text-decoration-none text-end opacity-25"
@click.prevent="changeToWeek(week)">{{week.no}}</a>
<a href="#"
@click.prevent="clickEvent(day,week)"
@mouseover="highlight(week,day)"
<a href="#"
@click.prevent="clickEvent(day,week)"
@mouseover="highlight(week,day)"
@mouseleave="highlightedWeek = null; highlightedDay = null"
v-for="day in week.days"
v-for="day in week.days"
:key="day"
:class="getDayClass(week, day)"
>
+1 -1
View File
@@ -46,7 +46,7 @@ export default {
</template>
</calendar-header>
<calendar-pane ref="pane" v-slot="slot" @slid="paneChanged">
<calendar-week-page :year="focusDate.wYear" :week="focusDate.w+slot.offset" @updateMode="$emit('updateMode', $event)" @page:back="prev" @page:forward="next" @input="selectEvent" >
<calendar-week-page :active="slot.active" :year="focusDate.wYear" :week="focusDate.w+slot.offset" @updateMode="$emit('updateMode', $event)" @page:back="prev" @page:forward="next" @input="selectEvent" >
<template #weekPage="{event,day}">
<slot name="weekPage" :event="event" :day="day" ></slot>
</template>
+30 -7
View File
@@ -11,6 +11,7 @@ export default {
hourPositionTime:null,
resizeObserver: null,
width: 0,
scrollContainer: null,
}
},
inject: [
@@ -27,7 +28,8 @@ export default {
],
props: {
year: Number,
week: Number
week: Number,
active: Boolean
},
emits: [
'updateMode',
@@ -35,6 +37,23 @@ export default {
'page:forward',
'input',
],
watch: {
active: {
handler(value) {
const activeContainer = document.querySelector("#calendarContainer")
if(value){
Vue.nextTick(() => {
this.scrollContainer = activeContainer;
activeContainer.addEventListener('wheel', this.scrollFunction);
})
}else{
this.scrollContainer=null;
activeContainer?.removeEventListener('wheel', this.scrollFunction);
}
},
immediate: true,
}
},
computed: {
allDayEvents(){
let allDayEvents = {};
@@ -190,6 +209,10 @@ export default {
}
},
methods: {
scrollFunction(event){
event.preventDefault();
this.scrollContainer?.scrollBy({ top: Math.sign(event.deltaY) * 100, behavior: 'instant' });
},
hourGridIdentifier(hour) {
// this is the id attribute that is responsible to scroll the calender to the first event
return 'scroll' + hour + this.focusDate.d + this.week;
@@ -335,7 +358,7 @@ export default {
container.style['overflow-y'] = 'scroll'
container.style['overflow-x'] = 'auto'
}
this.initResizeObserver();
},
beforeUnmount() {
@@ -350,12 +373,12 @@ export default {
<a href="#" class="small text-secondary text-decoration-none" >{{dayText[day]?.datum}}</a>
</div>
</div>
<div ref="eventcontainer" class="position-relative flex-grow-1" >
<div ref="eventcontainer" class="position-relative flex-grow-1" >
<div class="all-day-event-container" >
<div class="all-day-event all-day-event-border" v-for="(day,dayindex) in eventsPerDayAndHour">
<div class="position-sticky d-grid mx-1" style="top:0;" v-for="(events,_day) in allDayEvents" :key="_day">
<div v-if="dayindex == _day" v-for="event in events" :key="event" @click.prevent="weekPageClick(event, _day)"
<template v-for="(events,_day) in allDayEvents" :key="_day">
<div v-if="dayindex == _day" v-for="event in events" :key="event" class="d-grid m-1" style="top:0;" @click.prevent="weekPageClick(event, _day)"
:selected="event == selectedEvent"
:style="{'background-color': event?.color, 'margin-bottom':'1px'}"
class="small rounded overflow-hidden fhc-entry"
@@ -365,7 +388,7 @@ export default {
<p>this is a placeholder which means that no template was passed to the Calendar Page slot</p>
</slot>
</div>
</div>
</template>
</div>
</div>
<div class="events position-relative" :ref="'eventsRef'+week" @mousemove="calcHourPosition" @mouseleave="hourPosition = null">
@@ -3,6 +3,7 @@ import CalendarDate from "../../../composables/CalendarDate.js";
import LvModal from "../Mylv/LvModal.js";
import LvInfo from "../Mylv/LvInfo.js"
import LvMenu from "../Mylv/LvMenu.js"
import moodleSvg from "../../../helpers/moodleSVG.js"
export const Stundenplan = {
name: 'Stundenplan',
@@ -36,7 +37,7 @@ export const Stundenplan = {
}
},
components: {
FhcCalendar, LvModal, LvMenu, LvInfo
FhcCalendar, LvModal, LvMenu, LvInfo, moodleSvg
},
computed:{
downloadLinks: function(){
@@ -221,9 +222,15 @@ export const Stundenplan = {
</div>
</template>
<template #monthPage="{event,day}">
<span class="fhc-entry" >
{{event.topic}}
</span>
<div class="p-1" v-if="event.type=='moodle'">
<div class="d-flex small w-100" >
<moodle-svg></moodle-svg>
<span class="flex-grow-1 text-center ">{{event.topic}}</span>
</div>
</div>
<div class="p-1" v-else>
<span>{{event.topic}}</span>
</div>
</template>
<template #weekPage="{event,day}">
<div @click="showModal(event); " type="button"
@@ -237,9 +244,7 @@ export const Stundenplan = {
</div>
</div>
<div v-if="event.type=='moodle'" class="d-flex small w-100" >
<span >
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"><radialGradient id="moodle-original-a" cx="532.855" cy="-537.557" r="209.76" gradientTransform="matrix(1 0 0 -1 -297.6 -460.9)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#FAAF40"/><stop offset=".043" stop-color="#F9A538"/><stop offset=".112" stop-color="#F89D31"/><stop offset=".227" stop-color="#F89A2F"/><stop offset=".528" stop-color="#F7922D"/><stop offset="1" stop-color="#F37B28"/></radialGradient><path fill="url(#moodle-original-a)" d="M106.259 105.754V66.975c0-8.164-3.397-12.244-10.034-12.244-6.629 0-10.034 4.08-10.034 12.244v38.779H66.294V66.975c0-8.164-3.228-12.244-9.862-12.244-6.633 0-10.036 4.08-10.036 12.244v38.779H26.667V64.768c0-8.504 2.891-14.801 8.844-19.223 5.102-3.91 12.246-5.777 20.92-5.777 9.015 0 15.478 2.207 19.727 6.801 3.57-4.594 10.207-6.801 19.897-6.801 8.844 0 15.819 1.867 20.922 5.777 5.951 4.422 8.843 10.719 8.843 19.223v41.152h-19.563v-.166z"/><path fill="#58595B" d="M28.539 49.627l-2.041 10.207c18.708 6.291 36.395.166 45.751-16.158-13.778-9.522-26.535.17-43.71 5.951"/><linearGradient id="moodle-original-b" gradientUnits="userSpaceOnUse" x1="324.268" y1="-509.952" x2="368.932" y2="-509.952" gradientTransform="matrix(1 0 0 -1 -297.6 -460.9)"><stop offset="0" stop-color="#929497"/><stop offset=".124" stop-color="#757578"/><stop offset=".279" stop-color="#575658"/><stop offset=".44" stop-color="#403E3F"/><stop offset=".609" stop-color="#302D2E"/><stop offset=".788" stop-color="#262223"/><stop offset="1" stop-color="#231F20"/></linearGradient><path fill="url(#moodle-original-b)" d="M28.539 47.08c-.681 3.91-1.192 7.65-1.872 11.563 17.857 6.125 35.375.85 44.73-15.137-11.909-13.776-25.17-2.383-42.858 3.574"/><linearGradient id="moodle-original-c" gradientUnits="userSpaceOnUse" x1="332.834" y1="-495.051" x2="351.377" y2="-521.534" gradientTransform="matrix(1 0 0 -1 -297.6 -460.9)"><stop offset="0" stop-color="#231F20"/><stop offset="1" stop-color="#231F20" stop-opacity="0"/></linearGradient><path fill="url(#moodle-original-c)" d="M49.799 51.668c-8.164-1.701-17.009 2.555-23.131 6.975-3.912-28.57 13.777-27.893 36.903-20.75-1.529 6.975-4.083 16.33-8.502 21.941-.169-3.744-1.869-6.293-5.27-8.166"/><linearGradient id="moodle-original-d" gradientUnits="userSpaceOnUse" x1="299.778" y1="-495.802" x2="381.412" y2="-495.802" gradientTransform="matrix(1 0 0 -1 -297.6 -460.9)"><stop offset="0" stop-color="#929497"/><stop offset=".124" stop-color="#757578"/><stop offset=".279" stop-color="#575658"/><stop offset=".44" stop-color="#403E3F"/><stop offset=".609" stop-color="#302D2E"/><stop offset=".788" stop-color="#262223"/><stop offset="1" stop-color="#231F20"/></linearGradient><path fill="url(#moodle-original-d)" d="M2.178 47.08c29.932-18.031 46.77-21.43 81.634-25-40.478 31.969-41.499 25-81.634 25"/><path stroke="#4A4A4C" stroke-width=".5" fill="none" d="M83.812 22.246L51.667 45.545"/><path opacity=".23" fill="#231F20" d="M45.545 34.66c.34 3.744-.511-3.572 0 0"/><path stroke="#A8ABAD" stroke-width=".5" fill="none" d="M2.178 47.08l49.489-1.535"/><path stroke="#F16922" stroke-width=".5" d="M42.484 35.002C33.98 37.383 6.09 43.506 5.747 47.08c-.849 6.631-.167 17.176-.167 17.176" fill="none"/><path fill="#F16922" d="M8.131 89.596c-3.063-7.652-6.804-16.158-2.384-26.703C8.64 72.756 8.131 80.24 8.131 89.596"/><path fill="#6D6E70" d="M41.076 33.844c.708-.25 1.384-.17 1.509.184.126.355-.344.846-1.052 1.096-.709.256-1.384.172-1.51-.184-.127-.352.344-.844 1.053-1.096z"/></svg>
</span>
<moodle-svg></moodle-svg>
<span class="flex-grow-1 text-center">{{event.topic}}</span>
</div>
<div v-else class="d-flex flex-column flex-grow-1 align-items-center small">
@@ -252,8 +257,8 @@ export const Stundenplan = {
<template #dayPage="{event,day,mobile}">
<div @click="mobile? showModal(event):null" type="button" class="fhc-entry border border-secondary border m-0 h-100 text-center">
<template v-if="event.type=='moodle'">
<div class="d-flex align-items-center w-100" >
<span class="p-2">moodle:</span>
<div class="d-flex small align-items-center w-100 p-1" >
<moodle-svg></moodle-svg>
<span class="flex-grow-1 text-center">{{event.topic}}</span>
</div>
</template>
@@ -282,7 +287,7 @@ export const Stundenplan = {
</div>
</template>
<template #pageMobilContent="{lvMenu, event}">
<h3 >{{$p.t('lvinfo','lehrveranstaltungsinformationen')}}</h3>
<h3 >{{event.type=='moodle'?$p.t('lvinfo','Moodleinformationen'):$p.t('lvinfo','lehrveranstaltungsinformationen')}}</h3>
<div class="w-100">
<lv-info :event="event" />
</div>
+5
View File
@@ -0,0 +1,5 @@
export default {
template:`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 20 130 90" style="height:1.5em" ><radialGradient id="moodle-original-a" cx="532.855" cy="-537.557" r="209.76" gradientTransform="matrix(1 0 0 -1 -297.6 -460.9)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#FAAF40" /><stop offset=".043" stop-color="#F9A538" /><stop offset=".112" stop-color="#F89D31" /><stop offset=".227" stop-color="#F89A2F" /><stop offset=".528" stop-color="#F7922D" /><stop offset="1" stop-color="#F37B28" /></radialGradient><path fill="white" stroke="black" stroke-width="1" d="M106.259 105.754V66.975c0-8.164-3.397-12.244-10.034-12.244-6.629 0-10.034 4.08-10.034 12.244v38.779H66.294V66.975c0-8.164-3.228-12.244-9.862-12.244-6.633 0-10.036 4.08-10.036 12.244v38.779H26.667V64.768c0-8.504 2.891-14.801 8.844-19.223 5.102-3.91 12.246-5.777 20.92-5.777 9.015 0 15.478 2.207 19.727 6.801 3.57-4.594 10.207-6.801 19.897-6.801 8.844 0 15.819 1.867 20.922 5.777 5.951 4.422 8.843 10.719 8.843 19.223v41.152h-19.563v-.166z" /><path fill="black" d="M28.539 49.627l-2.041 10.207c18.708 6.291 36.395.166 45.751-16.158-13.778-9.522-26.535.17-43.71 5.951" /><linearGradient id="moodle-original-b" gradientUnits="userSpaceOnUse" x1="324.268" y1="-509.952" x2="368.932" y2="-509.952" gradientTransform="matrix(1 0 0 -1 -297.6 -460.9)"><stop offset="0" stop-color="#929497" /><stop offset=".124" stop-color="#757578" /><stop offset=".279" stop-color="#575658" /><stop offset=".44" stop-color="#403E3F" /><stop offset=".609" stop-color="#302D2E" /><stop offset=".788" stop-color="#262223" /><stop offset="1" stop-color="#231F20" /></linearGradient><path fill="black" d="M28.539 47.08c-.681 3.91-1.192 7.65-1.872 11.563 17.857 6.125 35.375.85 44.73-15.137-11.909-13.776-25.17-2.383-42.858 3.574" /><linearGradient id="moodle-original-c" gradientUnits="userSpaceOnUse" x1="332.834" y1="-495.051" x2="351.377" y2="-521.534" gradientTransform="matrix(1 0 0 -1 -297.6 -460.9)"><stop offset="0" stop-color="#231F20" /><stop offset="1" stop-color="#231F20" stop-opacity="0" /></linearGradient><path fill="none" d="M49.799 51.668c-8.164-1.701-17.009 2.555-23.131 6.975-3.912-28.57 13.777-27.893 36.903-20.75-1.529 6.975-4.083 16.33-8.502 21.941-.169-3.744-1.869-6.293-5.27-8.166" /><linearGradient id="moodle-original-d" gradientUnits="userSpaceOnUse" x1="299.778" y1="-495.802" x2="381.412" y2="-495.802" gradientTransform="matrix(1 0 0 -1 -297.6 -460.9)"><stop offset="0" stop-color="#929497" /><stop offset=".124" stop-color="#757578" /><stop offset=".279" stop-color="#575658" /><stop offset=".44" stop-color="#403E3F" /><stop offset=".609" stop-color="#302D2E" /><stop offset=".788" stop-color="#262223" /><stop offset="1" stop-color="#231F20" /></linearGradient><path fill="black" d="M2.178 47.08c29.932-18.031 46.77-21.43 81.634-25-40.478 31.969-41.499 25-81.634 25" /><path stroke="#4A4A4C" stroke-width=".5" fill="black" d="M83.812 22.246L51.667 45.545" /><path opacity=".23" fill="black" d="M45.545 34.66c.34 3.744-.511-3.572 0 0" /><path stroke="#4A4A4C" stroke-width=".5" fill="black" d="M2.178 47.08l49.489-1.535" /><path stroke="#F16922" stroke-width=".5" d="M42.484 35.002C33.98 37.383 6.09 43.506 5.747 47.08c-.849 6.631-.167 17.176-.167 17.176" fill="none" /><path fill="#F16922" d="M8.131 89.596c-3.063-7.652-6.804-16.158-2.384-26.703C8.64 72.756 8.131 80.24 8.131 89.596" /><path fill="#6D6E70" d="M41.076 33.844c.708-.25 1.384-.17 1.509.184.126.355-.344.846-1.052 1.096-.709.256-1.384.172-1.51-.184-.127-.352.344-.844 1.053-1.096z" /></svg>
`
}