Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add day view event popups #137

Merged
merged 1 commit into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions frontend/src/components/CalendarDay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@
<div class="flex-center text-gray-400 bg-white dark:bg-gray-700">
{{ t('label.allDay') }}
</div>
<div class="grid auto-rows-max gap-1 p-1 bg-white dark:bg-gray-700">
<div
class="grid auto-rows-max gap-1 p-1 bg-white dark:bg-gray-700"
@mouseleave="popup = {...initialEventPopupData}"
>
<div
v-for="event in eventsByDate?.allDay"
:key="event"
class="flex overflow-hidden"
@mouseenter="element => popup=showEventPopup(element, event, popupPosition)"
>
<div class="w-full text-sm truncate rounded mx-8 px-2 py-0.5 bg-amber-400/80">
{{ event.title }}
Expand All @@ -33,13 +37,15 @@
<div
class="grid bg-white dark:bg-gray-700"
:style="{ gridAutoRows: unitRem + 'rem' }"
@mouseleave="popup = {...initialEventPopupData}"
>
<div
v-for="event in eventsByDate?.duringDay"
:key="event"
class="flex overflow-hidden"
:class="{ 'hidden': event.offset < 0 }"
:style="{ gridRow: event.offset + ' / span ' + event.span }"
@mouseenter="element => !booking ? popup=showEventPopup(element, event, popupPosition) : null"
>
<div
v-if="!booking"
Expand Down Expand Up @@ -123,13 +129,25 @@
</div>
</div>
</div>
<event-popup
v-if="(events && !booking)"
:style="{
display: popup.display,
top: popup.top,
left: popup.left,
right: popup.right,
}"
:event="popup.event"
:position="popupPosition"
/>
</div>
</template>

<script setup>
import { computed, inject } from 'vue';
import { eventColor, timeFormat } from '@/utils';
import { computed, inject, ref } from 'vue';
import { eventColor, timeFormat, initialEventPopupData, showEventPopup } from '@/utils';
import { useI18n } from 'vue-i18n';
import EventPopup from '@/elements/EventPopup';

// icons
import {
Expand All @@ -149,6 +167,7 @@ const props = defineProps({
booking: Boolean, // flag indicating if calendar is used to book time slots
appointments: Array, // data of appointments to show
events: Array, // data of calendar events to show
popupPosition: String, // currently supported: right, left, top
});

// component emits
Expand Down Expand Up @@ -254,4 +273,7 @@ const bookSlot = (d) => {
emit('eventSelected', d);
};

// event details
const popup = ref({...initialEventPopupData});

</script>
2 changes: 1 addition & 1 deletion frontend/src/components/CalendarMonth.vue
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const props = defineProps({
appointments: Array, // data of appointments to show
events: Array, // data of calendar events to show
schedules: Array, // data of scheduled event previews to show
popupPosition: String, // currently supported: right, left
popupPosition: String, // currently supported: right, left, top
});

// component emits
Expand Down
41 changes: 13 additions & 28 deletions frontend/src/components/CalendarWeek.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@
v-for="d in days"
:key="d.day"
class="grid auto-rows-max gap-1 p-1 bg-white dark:bg-gray-700"
@mouseleave="hideEventPopup"
@mouseleave="popup = {...initialEventPopupData}"
>
<div
v-for="event in eventsByDate(d.date)?.allDay"
:key="event"
class="flex overflow-hidden"
@mouseenter="element => showEventPopup(element, event)"
@mouseenter="element => popup=showEventPopup(element, event, popupPosition)"
>
<div class="w-full text-sm truncate rounded px-2 py-0.5 bg-amber-400/80">
{{ event.title }}
Expand All @@ -55,15 +55,15 @@
:key="d.day"
class="grid bg-white dark:bg-gray-700"
:style="{ gridAutoRows: unitRem + 'rem' }"
@mouseleave="hideEventPopup"
@mouseleave="popup = {...initialEventPopupData}"
>
<div
v-for="event in eventsByDate(d.date)?.duringDay"
:key="event"
class="flex overflow-hidden"
:class="{ 'hidden': event.offset < 0 }"
:style="{ gridRow: event.offset + ' / span ' + event.span }"
@mouseenter="element => !booking ? showEventPopup(element, event) : null"
@mouseenter="element => !booking ? popup=showEventPopup(element, event, popupPosition) : null"
>
<div
v-if="!booking"
Expand Down Expand Up @@ -114,18 +114,20 @@
<event-popup
v-if="(events && !booking)"
:style="{
'display': popup.display,
'top': popup.top,
'left': popup.left,
display: popup.display,
top: popup.top,
left: popup.left,
right: popup.right,
}"
:event="popup.event"
:position="popupPosition"
/>
</div>
</template>

<script setup>
import { computed, inject, reactive } from 'vue';
import { eventColor, timeFormat } from '@/utils';
import { computed, inject, ref } from 'vue';
import { eventColor, timeFormat, initialEventPopupData, showEventPopup } from '@/utils';
import { useI18n } from 'vue-i18n';
import EventPopup from '@/elements/EventPopup';

Expand All @@ -138,6 +140,7 @@ const props = defineProps({
booking: Boolean, // flag indicating if calendar is used to book time slots
appointments: Array, // data of appointments to show
events: Array, // data of calendar events to show
popupPosition: String, // currently supported: right, left, top
});

// component emits
Expand Down Expand Up @@ -260,24 +263,6 @@ const bookSlot = (d) => {
};

// event details
const popup = reactive({
event: null,
display: 'none',
top: 0,
left: 0,
});

// calculate properties of event popup for given element and show popup
const showEventPopup = (el, event) => {
popup.event = event;
popup.display = 'block';
popup.top = `${el.target.offsetTop + el.target.clientHeight / 2 - el.target.parentElement.scrollTop}px`;
popup.left = `${el.target.offsetLeft + el.target.clientWidth}px`;
};
const popup = ref({...initialEventPopupData});

// reset event popup and hide it
const hideEventPopup = () => {
popup.event = null;
popup.display = 'none';
};
</script>
36 changes: 6 additions & 30 deletions frontend/src/elements/CalendarMonthDay.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
'bg-gray-50 dark:bg-gray-600 text-gray-400': !isActive || disabled,
'cursor-not-allowed': disabled
}"
@mouseleave="hideEventPopup"
@mouseleave="popup = {...initialEventPopupData}"
>
<div
class="w-6 rounded-full text-center relative"
Expand Down Expand Up @@ -45,7 +45,7 @@
backgroundColor: eventColor(event, placeholder).background,
}"
@click="emit('eventSelected', day)"
@mouseenter="element => showDetails ? showEventPopup(element, event) : null"
@mouseenter="element => showDetails ? popup=showEventPopup(element, event, popupPosition) : null"
>
<div
v-if="event.remote && !event.all_day"
Expand Down Expand Up @@ -85,8 +85,8 @@
</template>

<script setup>
import { eventColor, timeFormat } from '@/utils';
import { inject, reactive, computed } from 'vue';
import { eventColor, timeFormat, initialEventPopupData, showEventPopup } from '@/utils';
import { inject, computed, ref } from 'vue';
import EventPopup from '@/elements/EventPopup';

const dj = inject('dayjs');
Expand All @@ -101,7 +101,7 @@ const props = defineProps({
placeholder: Boolean, // flag formating events as placeholder
events: Array, // list of events to show on this day or null
showDetails: Boolean, // flag enabling event popups with details
popupPosition: String, // currently supported: right, left
popupPosition: String, // currently supported: right, left, top
disabled: Boolean, // flag making this day non-selectable and inactive
});

Expand All @@ -123,31 +123,7 @@ const sortedEvents = computed(() => [...props.events].sort((a, b) => {
}));

// event details
const popup = reactive({
event: null,
display: 'none',
top: 0,
left: 'initial',
});

// calculate properties of event popup for given element and show popup
const showEventPopup = (el, event) => {
popup.event = event;
popup.display = 'block';
popup.top = `${el.target.offsetTop + el.target.clientHeight / 2 - el.target.parentElement.scrollTop}px`;
if (!props.popupPosition || props.popupPosition === 'right') {
popup.left = `${el.target.offsetLeft + el.target.clientWidth + 4}px`;
}
if (props.popupPosition === 'left') {
popup.left = 'initial'
}
};

// reset event popup and hide it
const hideEventPopup = () => {
popup.event = null;
popup.display = 'none';
};
const popup = ref({...initialEventPopupData});

// formatted time range
const formattedTimeRange = (event) => {
Expand Down
12 changes: 7 additions & 5 deletions frontend/src/elements/EventPopup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
<div
class="absolute p-3 -translate-y-1/2 transition-all shadow-lg rounded-md z-30 bg-white dark:bg-gray-800"
:class="{
'-translate-x-full': position === 'left'
'-translate-x-full': position === 'left',
'-translate-x-1/2': position === 'top'
}"
>
<div
class="absolute top-1/2 -translate-y-1/2 rotate-45 w-3 h-3 bg-white dark:bg-gray-800"
class="absolute rotate-45 w-3 h-3 bg-white dark:bg-gray-800"
:class="{
'-left-1.5': !position || position === 'right',
'-right-1.5': position === 'left',
'top-1/2 -translate-y-1/2 -left-1.5': !position || position === 'right',
'top-1/2 -translate-y-1/2 -right-1.5': position === 'left',
'left-1/2 -translate-x-1/2 -bottom-1.5': position === 'top',
}"
></div>
<div class="flex flex-col gap-2 text-gray-700 dark:text-gray-200">
Expand Down Expand Up @@ -49,7 +51,7 @@ const dj = inject('dayjs');
// component properties
const props = defineProps({
event: Object, // event to show details in popup for
position: String, // currently supported: right, left
position: String, // currently supported: right, left, top
});

// format datetime of event
Expand Down
28 changes: 28 additions & 0 deletions frontend/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,38 @@ export const timeFormat = () => {
return 'LT';
};

// event popup handling
export const initialEventPopupData = {
event: null,
display: 'none',
top: 0,
left: 'initial',
};
// calculate properties of event popup for given element and show popup
export const showEventPopup = (el, event, position='right') => {
const obj = { ...initialEventPopupData };
obj.event = event;
obj.display = 'block';
obj.top = `${el.target.offsetTop + el.target.clientHeight / 2 - el.target.parentElement.scrollTop}px`;
if (!position || position === 'right') {
obj.left = `${el.target.offsetLeft + el.target.clientWidth + 4}px`;
}
if (position === 'left') {
obj.left = `${el.target.offsetLeft - 4}px`;;
}
if (position === 'top') {
obj.left = `${el.target.offsetLeft + el.target.clientWidth/2}px`;
obj.top = `${el.target.offsetTop - 50}px`;
}
return obj;
};

export default {
keyByValue,
eventColor,
initials,
download,
timeFormat,
initialEventPopupData,
showEventPopup,
};
1 change: 1 addition & 0 deletions frontend/src/views/CalendarView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
:selected="activeDate"
:appointments="pendingAppointments"
:events="calendarEvents"
popup-position="top"
/>
<!-- page side bar -->
<div class="w-full sm:w-1/2 md:w-1/5 mx-auto mb-10 md:mb-0 min-w-[310px]">
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/views/ScheduleView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,15 @@
:selected="activeDate"
:appointments="pendingAppointments"
:events="calendarEvents"
popup-position="left"
/>
<calendar-day
v-show="tabActive === calendarViews.day"
class="w-full md:w-4/5"
:selected="activeDate"
:appointments="pendingAppointments"
:events="calendarEvents"
popup-position="top"
/>
</div>
</template>
Expand Down