deactivate old month page css for day number and events in favor of new computed style (can be redesigned but is being handled in js instead of css class); save & inject/provide observed resize from already existing ResizeObserver to be able to access concrete container width/height values we already had all the time anyway; added some comment to 2year old TODO concerning calendar breakpoints; check if navbar is showing before toggling programmatically; remove some unused code; week time line spawns in dayLane which is representing today to avoid caluclating left/right offsets -> WIP accurately retrieving lane width somehow else, currently approximated;

This commit is contained in:
Johann Hoffmann
2025-02-05 16:03:20 +01:00
parent d70cd58698
commit b8823052f5
9 changed files with 90 additions and 115 deletions
+32 -32
View File
@@ -131,19 +131,19 @@
.fhc-calendar-md .fhc-calendar-month-page-day.active {
border-color: var(--bs-secondary);
}
.fhc-calendar-lg .fhc-calendar-month-page-day .events,
.fhc-calendar-md .fhc-calendar-month-page-day .events {
display: block;
overflow: auto;
font-size: 0.7em;
}
.fhc-calendar-lg .fhc-calendar-month-page-day .events span,
.fhc-calendar-md .fhc-calendar-month-page-day .events span {
display: block;
margin: 0.2em;
padding: 0.1em 0.4em;
border-radius: 0.1em;
}
/*.fhc-calendar-lg .fhc-calendar-month-page-day .events,*/
/*.fhc-calendar-md .fhc-calendar-month-page-day .events {*/
/* display: block;*/
/* overflow: auto;*/
/* font-size: 0.7em;*/
/*}*/
/*.fhc-calendar-lg .fhc-calendar-month-page-day .events span,*/
/*.fhc-calendar-md .fhc-calendar-month-page-day .events span {*/
/* display: block;*/
/* margin: 0.2em;*/
/* padding: 0.1em 0.4em;*/
/* border-radius: 0.1em;*/
/*}*/
.fhc-calendar-lg .fhc-calendar-years .col-4,
.fhc-calendar-md .fhc-calendar-years .col-4 {
padding: 0.09375em 0;
@@ -217,25 +217,25 @@
background-color: rgba(var(--bs-secondary-rgb), 0.25);
border-radius: 50%;
}
.fhc-calendar-sm .fhc-calendar-month-page-day .no,
.fhc-calendar-xs .fhc-calendar-month-page-day .no {
display: flex;
align-items: center;
justify-content: center;
width: 80%;
height: 80%;
margin: 10%;
}
.fhc-calendar-sm .fhc-calendar-month-page-day .events,
.fhc-calendar-xs .fhc-calendar-month-page-day .events {
position: absolute;
bottom: 0;
left: 10%;
width: 80%;
height: 10%;
overflow: hidden;
display: flex;
}
/*.fhc-calendar-sm .fhc-calendar-month-page-day .no,*/
/*.fhc-calendar-xs .fhc-calendar-month-page-day .no {*/
/* display: flex;*/
/* align-items: center;*/
/* justify-content: center;*/
/* width: 80%;*/
/* height: 80%;*/
/* margin: 10%;*/
/*}*/
/*.fhc-calendar-sm .fhc-calendar-month-page-day .events,*/
/*.fhc-calendar-xs .fhc-calendar-month-page-day .events {*/
/* position: absolute;*/
/* bottom: 0;*/
/* left: 10%;*/
/* width: 80%;*/
/* height: 10%;*/
/* overflow: hidden;*/
/* display: flex;*/
/*}*/
.fhc-calendar-sm .fhc-calendar-month-page-day .events span,
.fhc-calendar-xs .fhc-calendar-month-page-day .events span {
overflow: hidden;
+3 -1
View File
@@ -104,7 +104,9 @@ const app = Vue.createApp({
event.preventDefault(); // Prevent browser navigation
if(this.isMobile) { // toggle the menu
document.getElementById('nav-main-btn').click();
const navMain = document.getElementById('nav-main');
// fix unwanted toggle from off to on for some links on mobile
if(navMain.classList.contains('show')) document.getElementById('nav-main-btn').click();
}
this.$router.push(route);
+23 -18
View File
@@ -28,7 +28,10 @@ export default {
todayDate,
date: this.date,
focusDate: this.focusDate,
size: Vue.computed({ get: () => this.size, set: v => this.size = v }),
size: Vue.computed({ get: () => this.size }),
calendarHeight: Vue.computed({ get: () => this.calendarHeight }),
calendarWidth: Vue.computed({ get: () => this.calendarWidth }),
events: Vue.computed(() => this.eventsPerDay),
filteredEvents: Vue.computed(() => this.filteredEvents),
minimized: Vue.computed({ get: () => this.minimized, set: v => this.$emit('update:minimized', v) }),
@@ -36,11 +39,9 @@ export default {
noMonthView: this.noMonthView,
noWeekView: this.noWeekView,
eventsAreNull: Vue.computed(() => this.events === null),
classHeader: this.classHeader,
mode: Vue.computed(()=>this.mode),
selectedEvent: Vue.computed(() => this.selectedEvent),
setSelectedEvent: (event)=>{this.selectedEvent = event;},
widget: this.widget
};
},
props: {
@@ -57,17 +58,9 @@ export default {
type: String,
default: 'month'
},
classHeader: {
type: [String, Object, Array],
default: ''
},
minimized: Boolean,
noWeekView: Boolean,
noMonthView: Boolean,
widget: {
type: Boolean,
default: false
}
noMonthView: Boolean
},
watch:{
selectedEvent:{
@@ -110,11 +103,14 @@ export default {
date: new CalendarDate(),
focusDate: new CalendarDate(),
size: 0,
containerWidth: 0,
containerHeight: 0,
selectedEvent:null,
}
},
computed: {
sizeClass() {
sizeClass() {
// mainly determines calendar font-size
return 'fhc-calendar-' + ['xs', 'sm', 'md', 'lg'][this.size];
},
mode: {
@@ -210,16 +206,25 @@ export default {
if (this.$refs.container) {
new ResizeObserver(entries => {
for (const entry of entries) {
let w = entry.contentBoxSize ? entry.contentBoxSize[0].inlineSize : entry.contentRect.width;
// TODO(chris): rework sizing
if (w > 600)
const w = entry.contentBoxSize ? entry.contentBoxSize[0].inlineSize : entry.contentRect.width;
const h = entry.contentBoxSize ? entry.contentBoxSize[0].blockSize : entry.contentRect.height;
// https://getbootstrap.com/docs/5.0/layout/breakpoints/
// bootstrap breakpoints watch window size and this function monitors container size of calendar itself.
// calendar is using bootstrap breakpoints which influence layout, which retriggers this function
// -> some width constellations will loop so we dont use values around bs5 breakpoints
// ['xs', 'sm', 'md', 'lg'][this.size]
if (w >= 600)
this.size = 3;
else if (w > 350)
else if (w >= 350)
this.size = 2;
else if (w > 250)
else if (w >= 250)
this.size = 1;
else
this.size = 0;
this.containerWidth = w
this.containerHeight = h
}
}).observe(this.$refs.container);
}
+1 -1
View File
@@ -103,7 +103,7 @@ export default {
}
},
dayText(){
if(!this.size || !this.day)return {};
if(!this.day)return {};
return {
heading: this.day.toLocaleString(this.$p.user_locale.value, { dateStyle: 'short' }),
tag: this.day.toLocaleString(this.$p.user_locale.value, { weekday: this.size < 2 ? 'narrow' : (this.size < 3 ? 'short' : 'long') }),
+1 -20
View File
@@ -13,11 +13,9 @@ export default {
inject: [
'eventsAreNull',
'size',
'classHeader',
'mode',
'noWeekView',
'noMonthView',
'widget'
],
props: {
title: String
@@ -28,25 +26,8 @@ export default {
'next',
'click'
],
computed: {
myClassHeader() {
// TODO(chris): + {'btn-sm': !this.size}
let c = this.classHeader;
if (Array.isArray(c)) {
if (!this.size)
c.push('btn-sm');
} else if (typeof c === 'string' || c instanceof String) {
if (!this.size)
c += ' btn-sm';
} else {
c['btn-sm'] = !this.size;
}
return c;
}
},
template: /*html*/`
<div class="calendar-header card-header w-100" :class="classHeader">
<div class="calendar-header card-header w-100">
<div class="row align-items-center ">
<div class=" col-12 col-md-3 d-flex justify-content-center justify-content-md-start align-items-center">
<slot name="calendarDownloads"></slot>
@@ -8,7 +8,6 @@ export default {
'size',
'minimized',
'date',
'classHeader'
],
data() {
return {
+18 -29
View File
@@ -18,7 +18,7 @@ export default {
'showWeeks',
'noWeekView',
'selectedEvent',
'setSelectedEvent',
'setSelectedEvent'
],
props: {
year: Number,
@@ -71,12 +71,9 @@ export default {
const isNotThisMonth = day.getMonth() != this.month
const isInThePast = day.getTime() < this.today // this.date is just the focusDate but not the initial Date
if(isHighlightedWeek) classstring += ' fhc-highlight-week'
if(isHighlightedDay) classstring += ' fhc-highlight-day'
if(isThisDate) classstring += ' active'
if(isNotThisMonth) classstring += ' opacity-25'
if(isInThePast) classstring += ' fhc-calendar-past'
return classstring
@@ -112,38 +109,29 @@ export default {
},
getNumberStyle(day) {
// TODO: move this to active css class in calendar.css
const styleObj = {}
// styleObj.display = 'inline-block';
// styleObj.widt = '32px'; /* Adjust based on the calendar cell size */
// styleObj.height = '32px';
// styleObj['line-height'] = '32px';
// styleObj['text-align'] = 'center';
// styleObj['font-weight'] = 'bold';
// styleObj['font-size'] = '14px';
//
// if(day.getDate() === this.todayDate.getDate()
// && day.getMonth() === this.todayDate.getMonth()
// && day.getFullYear() === this.todayDate.getFullYear()) {
// styleObj['background-color'] = '#00649c'; // fh blau
// styleObj.color = 'white';
// } else {
// // styleObj['background-color'] = '#ffffff'; // fh blau
// // styleObj.color = 'white'; /* White text for contrast */
// }
styleObj.display = 'inline-block';
styleObj.height = '32px';
styleObj['line-height'] = '32px';
styleObj['text-align'] = 'center';
styleObj['font-weight'] = 'bold';
styleObj['font-size'] = '14px';
if(day.getDate() === this.todayDate.getDate()
&& day.getMonth() === this.todayDate.getMonth()
&& day.getFullYear() === this.todayDate.getFullYear()) {
styleObj['background-color'] = '#00649c'; // fh blau
styleObj.color = 'white';
}
return styleObj
}
},
mounted() {
const container = document.getElementById("calendarContainer")
if(container) container.style['overflow-y'] = 'scroll'
if(container) container.style['overflow-y'] = 'auto'
},
// unmounted() {
// const container = document.getElementById("calendarContainer")
// if(container) container.style['overflow-y'] = ''
// },
template: /*html*/`
<div class="fhc-calendar-month-page" :class="{'show-weeks': showWeeks}">
<div v-if="showWeeks" class=" bg-light fw-bold border-top border-bottom text-center"></div>
@@ -173,5 +161,6 @@ export default {
</span>
</a>
</template>
</div>`
</div>
`
}
+11 -12
View File
@@ -36,6 +36,9 @@ export default {
'input',
],
computed: {
laneWidth() {
return this.width / this.days.length
},
curTime() {
const now = new Date();
return String(now.getHours()).padStart(2, '0') + ':' + String(now.getMinutes()).padStart(2, '0');
@@ -137,10 +140,6 @@ export default {
)
},
curIndicatorStyle() {
const todayIndex = this.days.findIndex(d => d.getFullYear() === this.todayDate.getFullYear() &&
d.getMonth() === this.todayDate.getMonth() &&
d.getDate() === this.todayDate.getDate()
)
return {
'pointer-events': 'none',
@@ -149,8 +148,7 @@ export default {
'z-index': 2,
'border-color': '#00649C!important',
top: this.getDayTimePercent + '%',
left: 'calc(('+this.width+'px + 3em) /'+this.days.length+'*'+todayIndex+')',
right: 'calc(('+this.width+'px - 3em)/'+this.days.length+'*'+(this.days.length - 1 - todayIndex)+')',
width: this.laneWidth + 'px' // todo: manage the real value of 1fr somehow
}
},
getDayTimePercent() {
@@ -314,16 +312,17 @@ export default {
<span class="border border-top-0 px-2 bg-white">{{hourPositionTime}}</span>
</div>
</Transition>
<Transition>
<div v-if="lookingAtToday" class="position-absolute border-top small" :style="curIndicatorStyle">
<span class="border border-top-0 px-2 bg-white">{{curTime}}</span>
</div>
</Transition>
<div class="events" :ref="'eventsRef'+week">
<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 in eventsPerDayAndHour" :key="day" class=" day border-start" :style="dayGridStyle(day)">
<Transition>
<div v-if="day.isToday" class="position-absolute border-top small" :style="curIndicatorStyle">
<span class="border border-top-0 px-2 bg-white">{{curTime}}</span>
</div>
</Transition>
<div v-for="event in day.events" :key="event" @click.prevent="weekPageClick(event.orig, day)"
:selected="event.orig == selectedEvent"
:style="eventGridStyle(day,event)"
@@ -331,7 +330,7 @@ export default {
v-contrast >
<slot name="weekPage" :event="event" :day="day">
<p>this is a placeholder which means that no template was passed to the Calendar Page slot</p>
</slot>
</slot>
</div>
</div>
@@ -178,7 +178,7 @@ export default {
<div v-if="events === null" class="d-flex h-100 justify-content-center align-items-center">
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
<template ref="allWeek" v-else-if="allEventsGrouped.size" v-for="([key, value], index) in allEventsGrouped" :key="index" style="margin-top: 8px;">
<template v-else-if="allEventsGrouped.size" v-for="([key, value], index) in allEventsGrouped" :key="index" style="margin-top: 8px;">
<div class="card-header d-grid p-0">
<button class="btn btn-link link-secondary text-decoration-none" @click="setCalendarMaximized">{{ key.format({dateStyle: "full"})}}</button>
</div>