mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-06-01 20:29:29 +00:00
ab5294de2f
coursepicker umgebaut, keine backend suche mehr hinzugefuegt: - resizeHanlder funktion - broadcastchannel postMessage "dropped" - tbl_kalender_status column bezeichnung_mehrsprachig - tbl_kalender_status column sort
334 lines
8.7 KiB
JavaScript
334 lines
8.7 KiB
JavaScript
import BaseHeader from './Base/Header.js';
|
|
import BaseSlider from './Base/Slider.js';
|
|
import BsModal from '../Bootstrap/Modal.js';
|
|
|
|
import CalClick from '../../directives/Calendar/Click.js';
|
|
import DragClick from '../../directives/dragClick.js';
|
|
import Draggable from '../../directives/draggable.js';
|
|
import Drop from '../../directives/drop.js';
|
|
|
|
export default {
|
|
name: "CalendarBase",
|
|
components: {
|
|
BaseHeader,
|
|
BaseSlider,
|
|
BsModal
|
|
},
|
|
directives: {
|
|
CalClick,
|
|
DragClick,
|
|
Draggable,
|
|
Drop
|
|
},
|
|
provide() {
|
|
return {
|
|
events: Vue.computed(() => this.convertedEvents),
|
|
backgrounds: Vue.computed(() => this.convertedBackgrounds),
|
|
dropAllowed: Vue.computed(() => !!this.onDrop),
|
|
onDrop: Vue.computed(() => this.onDrop || null),
|
|
locale: Vue.computed(() => this.locale),
|
|
timezone: Vue.computed(() => this.timezone),
|
|
timeGrid: Vue.computed(() => this.timeGrid),
|
|
draggableEvents: Vue.computed(() => {
|
|
if (!this.draggableEvents)
|
|
return () => false;
|
|
|
|
if (Array.isArray(this.draggableEvents))
|
|
return event => this.draggableEvents.includes(event.type);
|
|
if (this.draggableEvents instanceof Function)
|
|
return this.draggableEvents;
|
|
|
|
return () => true;
|
|
}),
|
|
dropableEvents: Vue.computed(() => {
|
|
if (!this.onDrop)
|
|
return () => false;
|
|
|
|
if (Array.isArray(this.dropableEvents))
|
|
return item => this.dropableEvents.includes(item.type);
|
|
if (this.dropableEvents instanceof Function)
|
|
return this.dropableEvents;
|
|
|
|
return () => true;
|
|
}),
|
|
resizableEvents: Vue.computed(() => {
|
|
if (!this.resizableEvents)
|
|
return () => false;
|
|
|
|
if (Array.isArray(this.resizableEvents))
|
|
return event => this.resizableEvents.includes(event.type);
|
|
if (this.resizableEvents instanceof Function)
|
|
return this.resizableEvents;
|
|
|
|
return () => true;
|
|
}),
|
|
hasDragoverFunc: Vue.computed(() => this.onDragover),
|
|
mode: Vue.computed(() => this.mode),
|
|
onResize: Vue.computed(() => this.onResize || null),
|
|
};
|
|
},
|
|
props: {
|
|
locale: {
|
|
type: String,
|
|
default: 'de'
|
|
},
|
|
timezone: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
date: {
|
|
type: [Date, String, Number, luxon.DateTime],
|
|
default: props => luxon.DateTime.now().setZone(props.timezone).startOf('day')
|
|
},
|
|
modes: {
|
|
type: Object,
|
|
required: true,
|
|
default: {}
|
|
// TODO(chris): verfication functions
|
|
},
|
|
mode: String,
|
|
modeOptions: Object,
|
|
events: {
|
|
type: Array,
|
|
default: []
|
|
},
|
|
backgrounds: {
|
|
type: Array,
|
|
default: []
|
|
},
|
|
showBtns: Boolean,
|
|
btnMonth: {
|
|
type: Boolean,
|
|
default: undefined
|
|
},
|
|
btnWeek: {
|
|
type: Boolean,
|
|
default: undefined
|
|
},
|
|
btnDay: {
|
|
type: Boolean,
|
|
default: undefined
|
|
},
|
|
btnList: {
|
|
type: Boolean,
|
|
default: undefined
|
|
},
|
|
btnTableList: {
|
|
type: Boolean,
|
|
default: undefined
|
|
},
|
|
timeGrid: Array,
|
|
draggableEvents: [Boolean, Array, Function],
|
|
dropableEvents: [Boolean, Array, Function],
|
|
resizableEvents: [Boolean, Array, Function],
|
|
onDragover: Function,
|
|
onDrop: Function,
|
|
onResize: Function
|
|
},
|
|
emits: [
|
|
"click:next",
|
|
"click:prev",
|
|
"click:mode",
|
|
"click:event",
|
|
"click:day",
|
|
"click:week",
|
|
"update:date",
|
|
"update:mode",
|
|
"update:range",
|
|
"drop"
|
|
],
|
|
data() {
|
|
return {
|
|
internalView: null,
|
|
internalDate: null,
|
|
modalEvent: null
|
|
};
|
|
},
|
|
computed: {
|
|
convertedEvents() {
|
|
return this.events.map(orig => ({
|
|
id: orig.type + orig[orig.type + '_id'],
|
|
type: orig.type,
|
|
start: luxon.DateTime.fromISO(orig.isostart).setZone(this.timezone),
|
|
end: luxon.DateTime.fromISO(orig.isoend).setZone(this.timezone),
|
|
orig
|
|
}));
|
|
},
|
|
convertedBackgrounds() {
|
|
return this.backgrounds.map(bg => {
|
|
const res = { ...bg };
|
|
if (res.start) {
|
|
if (Number.isInteger(res.start))
|
|
res.start = luxon.DateTime.fromMillis(res.start, { zone: this.timezone, locale: this.locale });
|
|
else if (res.start instanceof Date)
|
|
res.start = luxon.DateTime.fromJSDate(res.start, { zone: this.timezone, locale: this.locale });
|
|
else if (typeof res.start ===
|
|
'string' || res.start instanceof String)
|
|
res.start = luxon.DateTime.fromISO(res.start, { zone: this.timezone, locale: this.locale });
|
|
}
|
|
if (res.end) {
|
|
if (Number.isInteger(res.end))
|
|
res.end = luxon.DateTime.fromMillis(res.end, { zone: this.timezone, locale: this.locale });
|
|
else if (res.end instanceof Date)
|
|
res.end = luxon.DateTime.fromJSDate(res.end, { zone: this.timezone, locale: this.locale });
|
|
else if (typeof res.end ===
|
|
'string' || res.end instanceof String)
|
|
res.end = luxon.DateTime.fromISO(res.end, { zone: this.timezone, locale: this.locale });
|
|
}
|
|
return res;
|
|
});
|
|
},
|
|
sDate() {
|
|
if (this.date instanceof luxon.DateTime)
|
|
return this.date;
|
|
return luxon.DateTime.fromJSDate(new Date(this.date)).setZone(this.timezone);
|
|
},
|
|
cDate: {
|
|
get() {
|
|
const date = this.internalDate ? this.internalDate : this.sDate;
|
|
return date.setLocale(this.locale);
|
|
},
|
|
set(value) {
|
|
this.internalDate = value;
|
|
this.$emit('update:date', value, this.cMode);
|
|
}
|
|
},
|
|
sMode() {
|
|
// choose default mode
|
|
let mode = this.mode;
|
|
if (mode)
|
|
mode = mode.toLowerCase();
|
|
if (!mode || !this.modes[mode])
|
|
mode = Object.keys(this.modes).find(Boolean); // start with first entry as active mode
|
|
return mode || '';
|
|
},
|
|
cMode: {
|
|
get() {
|
|
return this.internalView ? this.internalView : this.sMode;
|
|
},
|
|
set(value) {
|
|
this.internalView = value;
|
|
this.$emit('update:mode', value, this.cDate);
|
|
}
|
|
}
|
|
},
|
|
watch: {
|
|
sDate(n, o) {
|
|
if (this.sDate.isValid && (!this.internalDate || !this.sDate.hasSame(this.internalDate, 'day')))
|
|
this.internalDate = this.sDate;
|
|
},
|
|
sMode() {
|
|
if (this.sMode)
|
|
this.internalView = this.sMode;
|
|
}
|
|
},
|
|
methods: {
|
|
clickPrev() {
|
|
const evt = new Event('click:prev', {cancelable: true});
|
|
this.$emit('click:prev', evt);
|
|
if (evt.defaultPrevented)
|
|
return;
|
|
|
|
// default: switch page
|
|
this.$refs.mode.prevPage();
|
|
},
|
|
clickNext() {
|
|
const evt = new Event('click:next', {cancelable: true});
|
|
this.$emit('click:next', evt);
|
|
if (evt.defaultPrevented)
|
|
return;
|
|
|
|
// default: switch page
|
|
this.$refs.mode.nextPage();
|
|
},
|
|
handleClickDefaults(evt) {
|
|
// TODO(chris): implement
|
|
switch (evt.detail.source) {
|
|
case 'day':
|
|
if (this.cMode != 'day' && this.modes['day']) {
|
|
evt.stopPropagation();
|
|
this.cDate = evt.detail.value;
|
|
this.cMode = 'day';
|
|
}
|
|
break;
|
|
case 'week':
|
|
if (this.cMode != 'week' && this.modes['week']) {
|
|
evt.stopPropagation();
|
|
this.cDate = luxon.DateTime.fromObject({
|
|
localWeekNumber: evt.detail.value.number,
|
|
localWeekYear: evt.detail.value.year
|
|
}, {
|
|
zone: this.cDate.zoneName,
|
|
locale: this.cDate.locale
|
|
});
|
|
this.cMode = 'week';
|
|
}
|
|
break;
|
|
}
|
|
},
|
|
|
|
showEventModal(eventObj) {
|
|
this.modalEvent = eventObj;
|
|
this.$refs.modal.show();
|
|
},
|
|
hideEventModal() {
|
|
if (this.modalEvent)
|
|
this.modalEvent.closeFn = undefined;
|
|
this.$refs.modal.hide();
|
|
this.modalEvent = null;
|
|
},
|
|
onModalHidden() {
|
|
if (this.modalEvent.closeFn)
|
|
this.modalEvent.closeFn();
|
|
}
|
|
},
|
|
beforeUnmount() {
|
|
this.hideEventModal();
|
|
},
|
|
template: /* html */`
|
|
<div class="fhc-calendar-base h-100">
|
|
<div class="card h-100"
|
|
v-cal-click:container
|
|
@cal-click-default.capture="handleClickDefaults"
|
|
>
|
|
<base-header
|
|
class="card-header"
|
|
v-model:date="cDate"
|
|
v-model:mode="cMode"
|
|
@prev="clickPrev"
|
|
@next="clickNext"
|
|
@click:mode="$emit('click:mode', $event)"
|
|
:btn-day="!!modes['day'] && (btnDay || (showBtns && btnDay !== false))"
|
|
:btn-week="!!modes['week'] && (btnWeek || (showBtns && btnWeek !== false))"
|
|
:btn-month="!!modes['month'] && (btnMonth || (showBtns && btnMonth !== false))"
|
|
:btn-list="!!modes['list'] && (btnList || (showBtns && btnList !== false))"
|
|
:btn-table-list="!!modes['tableList'] && (btnTableList || (showBtns && btnTableList !== false))"
|
|
:mode-options="modeOptions ? modeOptions[cMode] : undefined"
|
|
>
|
|
<slot name="actions" />
|
|
</base-header>
|
|
<component
|
|
:is="modes ? modes[cMode] : null || 'div'"
|
|
ref="mode"
|
|
v-model:current-date="cDate"
|
|
@update:range="$emit('update:range', $event)"
|
|
@request-modal-open="showEventModal"
|
|
@request-modal-close="hideEventModal"
|
|
@drop="$emit('drop', $event)"
|
|
v-bind="modeOptions ? modeOptions[cMode] : null || {}"
|
|
>
|
|
<template v-slot="slot"><slot v-bind="slot" /></template>
|
|
</component>
|
|
</div>
|
|
<bs-modal ref="modal" dialog-class="modal-lg" body-class="" @hidden-bs-modal="onModalHidden">
|
|
<template #title>
|
|
<slot v-if="modalEvent" v-bind="{mode: 'eventheader', event: modalEvent.event}" />
|
|
</template>
|
|
<template #default>
|
|
<slot v-if="modalEvent" v-bind="{mode: 'event', event: modalEvent.event}" />
|
|
</template>
|
|
</bs-modal>
|
|
</div>
|
|
`
|
|
}
|