mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-10-12 15:04:04 +00:00
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ecf1845d4 | ||
|
|
07d04d5701 | ||
|
|
3c373aac25 | ||
|
|
4e2254dfc7 | ||
|
|
e976c825c9 | ||
|
|
88b5a0af46 | ||
|
|
a53551b462 | ||
|
|
989ce6d707 | ||
|
|
018169b06a | ||
|
|
0416a2ce7d | ||
|
|
dde95c8e01 | ||
|
|
7260a0a00e | ||
|
|
5cfa398ea6 |
@@ -82,6 +82,21 @@ function generateCSSsInclude($CSSs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates tags for the style sheets you want to include, the parameter could by a string or an array of strings
|
||||||
|
*/
|
||||||
|
function generatePrintCSS($path)
|
||||||
|
{
|
||||||
|
$cssLink = '<link rel="stylesheet" media="print" href="%s" />';
|
||||||
|
|
||||||
|
$ci =& get_instance();
|
||||||
|
$cachetoken = '?'.$ci->config->item('fhcomplete_build_version');
|
||||||
|
|
||||||
|
if (isset($path)) {
|
||||||
|
echo sprintf($cssLink, base_url($path).$cachetoken) . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates global JS-Object to pass parms to other javascripts
|
* Generates global JS-Object to pass parms to other javascripts
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ $includesArray = array(
|
|||||||
'fontawesome6' => true,
|
'fontawesome6' => true,
|
||||||
'axios027' => true,
|
'axios027' => true,
|
||||||
'primevue3' => true,
|
'primevue3' => true,
|
||||||
|
'printCssPath' => 'public/css/Cis4/print.css',
|
||||||
'customJSModules' => array_merge([
|
'customJSModules' => array_merge([
|
||||||
'public/js/apps/Cis/Menu.js'
|
'public/js/apps/Cis/Menu.js'
|
||||||
], $customJSModules ?? []),
|
], $customJSModules ?? []),
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
$customJSs = isset($customJSs) ? $customJSs : null;
|
$customJSs = isset($customJSs) ? $customJSs : null;
|
||||||
$customJSModules = isset($customJSModules) ? $customJSModules : null;
|
$customJSModules = isset($customJSModules) ? $customJSModules : null;
|
||||||
$skipID = isset($skipID) ? $skipID : null;
|
$skipID = isset($skipID) ? $skipID : null;
|
||||||
|
$printCssPath = isset($printCssPath) ? $printCssPath : null;
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<!-- Header start -->
|
<!-- Header start -->
|
||||||
|
|
||||||
@@ -138,6 +140,9 @@
|
|||||||
generateCSSsIncludeIfExtensionCssExists('tags.css');
|
generateCSSsIncludeIfExtensionCssExists('tags.css');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print CSS
|
||||||
|
if ($printCssPath) generatePrintCSS($printCssPath);
|
||||||
|
|
||||||
$extapphelper = ExtendableAppsHelper::getInstance();
|
$extapphelper = ExtendableAppsHelper::getInstance();
|
||||||
$extapphelper->init($customCSSs, $customJSs, $customJSModules);
|
$extapphelper->init($customCSSs, $customJSs, $customJSModules);
|
||||||
|
|
||||||
|
|||||||
@@ -59,12 +59,21 @@
|
|||||||
padding: .25em .5em .25em .25em;
|
padding: .25em .5em .25em .25em;
|
||||||
margin-inline-end: .25rem;
|
margin-inline-end: .25rem;
|
||||||
}
|
}
|
||||||
|
.rangePageContainer .calendar-event-default .event-time {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
.weekPageContainer .calendar-event-default .event-text {
|
.weekPageContainer .calendar-event-default .event-text {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
.rangePageContainer .calendar-event-default .event-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
.dayPageContainer .calendar-event-default,
|
.dayPageContainer .calendar-event-default,
|
||||||
.listPageContainer .calendar-event-default {
|
.listPageContainer .calendar-event-default {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
@page {
|
||||||
|
size: landscape;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
background-color: white !important;
|
||||||
|
color: black !important;
|
||||||
|
font-size: x-small !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fhc-calendar-base-slider > div {
|
||||||
|
position: relative !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
header,
|
||||||
|
#skiplink,
|
||||||
|
#cis-lvplan-personal-heading,
|
||||||
|
.cis-lvplan-personal hr,
|
||||||
|
.fhc-calendar-base-header,
|
||||||
|
.fhc-calendar-mode-range .grid-allday,
|
||||||
|
.fhc-calendar-mode-range #rangePresetSelector,
|
||||||
|
.fhc-calendar-base-grid-line-background {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fhc-calendar-mode-range .main-header-sunday,
|
||||||
|
.fhc-calendar-mode-range .fhc-calendar-base-grid-line-sunday {
|
||||||
|
border-bottom-width: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fhc-calendar-base-grid-line-sunday,
|
||||||
|
.fhc-calendar-mode-range .main-header-sunday {
|
||||||
|
break-after: page !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fhc-calendar-mode-range .fhc-calendar-base-grid {
|
||||||
|
grid-template-rows: auto repeat(var(--fhc-grid-displayed-axis-main-count), auto) !important;
|
||||||
|
gap: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fhc-calendar-mode-range .fhc-calendar-base-grid * {
|
||||||
|
gap: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fhc-calendar-mode-range .main-header:not(.main-header-empty),
|
||||||
|
.fhc-calendar-mode-range .fhc-calendar-base-grid-line {
|
||||||
|
border-top: solid !important;
|
||||||
|
border-top-color: black !important;
|
||||||
|
border-top-width: 1px !important;
|
||||||
|
}
|
||||||
@@ -104,11 +104,17 @@
|
|||||||
.fhc-calendar-base-grid-line {
|
.fhc-calendar-base-grid-line {
|
||||||
grid-gap: var(--fhc-calendar-gap-events, var(--fhc-calendar-gap, 1px));
|
grid-gap: var(--fhc-calendar-gap-events, var(--fhc-calendar-gap, 1px));
|
||||||
}
|
}
|
||||||
|
.fhc-calendar-mode-range .main-header-sunday,
|
||||||
|
.fhc-calendar-mode-range .all-day-events-sunday,
|
||||||
|
.fhc-calendar-mode-range .fhc-calendar-base-grid-line-sunday {
|
||||||
|
border: solid;
|
||||||
|
border-width: 0 0 8px 0;
|
||||||
|
border-color: var(--fhc-calendar-gap);
|
||||||
|
}
|
||||||
.fhc-calendar-base-label-time {
|
.fhc-calendar-base-label-time {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: end;
|
|
||||||
}
|
}
|
||||||
/* Header */
|
/* Header */
|
||||||
.fhc-calendar-base-header .header-picker {
|
.fhc-calendar-base-header .header-picker {
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ const router = VueRouter.createRouter({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `/Cis/MyLvPlan/:mode?/:focus_date?`,
|
path: `/Cis/MyLvPlan/:mode?/:focus_date?/:range_length?`,
|
||||||
name: 'MyLvPlan',
|
name: 'MyLvPlan',
|
||||||
component: MyLvPlan,
|
component: MyLvPlan,
|
||||||
props(route) {
|
props(route) {
|
||||||
|
|||||||
@@ -93,6 +93,10 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: undefined
|
default: undefined
|
||||||
},
|
},
|
||||||
|
btnRange: {
|
||||||
|
type: Boolean,
|
||||||
|
default: undefined
|
||||||
|
},
|
||||||
timeGrid: Array,
|
timeGrid: Array,
|
||||||
draggableEvents: [Boolean, Array, Function],
|
draggableEvents: [Boolean, Array, Function],
|
||||||
dropableEvents: [Boolean, Array, Function],
|
dropableEvents: [Boolean, Array, Function],
|
||||||
@@ -163,8 +167,16 @@ export default {
|
|||||||
return date.setLocale(this.locale);
|
return date.setLocale(this.locale);
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
this.internalDate = value;
|
let date;
|
||||||
this.$emit('update:date', value, this.cMode);
|
let rangeLength;
|
||||||
|
if (value instanceof luxon.DateTime) {
|
||||||
|
date = value;
|
||||||
|
} else {
|
||||||
|
date = value.date;
|
||||||
|
rangeLength = value.rangeLength;
|
||||||
|
}
|
||||||
|
this.internalDate = date;
|
||||||
|
this.$emit('update:date', date, this.cMode, rangeLength);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
sMode() {
|
sMode() {
|
||||||
@@ -282,6 +294,7 @@ export default {
|
|||||||
:btn-week="!!modes['week'] && (btnWeek || (showBtns && btnWeek !== false))"
|
:btn-week="!!modes['week'] && (btnWeek || (showBtns && btnWeek !== false))"
|
||||||
:btn-month="!!modes['month'] && (btnMonth || (showBtns && btnMonth !== false))"
|
:btn-month="!!modes['month'] && (btnMonth || (showBtns && btnMonth !== false))"
|
||||||
:btn-list="!!modes['list'] && (btnList || (showBtns && btnList !== false))"
|
:btn-list="!!modes['list'] && (btnList || (showBtns && btnList !== false))"
|
||||||
|
:btn-range="!!modes['range'] && (btnRange || (showBtns && btnRange !== false))"
|
||||||
:mode-options="modeOptions ? modeOptions[cMode] : undefined"
|
:mode-options="modeOptions ? modeOptions[cMode] : undefined"
|
||||||
>
|
>
|
||||||
<slot name="actions" />
|
<slot name="actions" />
|
||||||
@@ -293,6 +306,7 @@ export default {
|
|||||||
@update:range="$emit('update:range', $event)"
|
@update:range="$emit('update:range', $event)"
|
||||||
@request-modal-open="showEventModal"
|
@request-modal-open="showEventModal"
|
||||||
@request-modal-close="hideEventModal"
|
@request-modal-close="hideEventModal"
|
||||||
|
@update:date="cDate = $event"
|
||||||
v-bind="modeOptions ? modeOptions[cMode] : null || {}"
|
v-bind="modeOptions ? modeOptions[cMode] : null || {}"
|
||||||
>
|
>
|
||||||
<template v-slot="slot"><slot v-bind="slot" /></template>
|
<template v-slot="slot"><slot v-bind="slot" /></template>
|
||||||
|
|||||||
@@ -53,7 +53,11 @@ export default {
|
|||||||
flipAxis: Boolean,
|
flipAxis: Boolean,
|
||||||
allDayEvents: Boolean,
|
allDayEvents: Boolean,
|
||||||
axisMainCollapsible: Boolean,
|
axisMainCollapsible: Boolean,
|
||||||
snapToGrid: Boolean
|
snapToGrid: Boolean,
|
||||||
|
arePartHeadersRepeated: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -110,6 +114,19 @@ export default {
|
|||||||
return this.axisParts.slice(0, -1);
|
return this.axisParts.slice(0, -1);
|
||||||
return this.axisParts;
|
return this.axisParts;
|
||||||
},
|
},
|
||||||
|
displayedAxisMain() {
|
||||||
|
if (!this.$props.arePartHeadersRepeated)
|
||||||
|
return this.$props.axisMain;
|
||||||
|
|
||||||
|
let displayedAxisMain = [];
|
||||||
|
this.$props.axisMain.forEach((part, index) => {
|
||||||
|
if (part.weekday === 1 && index > 0) {
|
||||||
|
displayedAxisMain.push(null);
|
||||||
|
}
|
||||||
|
displayedAxisMain.push(part);
|
||||||
|
});
|
||||||
|
return displayedAxisMain;
|
||||||
|
},
|
||||||
start() {
|
start() {
|
||||||
return this.axisPartsWithBreaks[0].start;
|
return this.axisPartsWithBreaks[0].start;
|
||||||
},
|
},
|
||||||
@@ -128,15 +145,17 @@ export default {
|
|||||||
return ends;
|
return ends;
|
||||||
},
|
},
|
||||||
axisMainBorders() {
|
axisMainBorders() {
|
||||||
return this.axisMain.reduce(
|
return this.displayedAxisMain.reduce(
|
||||||
(res, curr) => res.concat([curr.plus(this.start), curr.plus(this.end)]),
|
(res, curr) => res.concat(curr ? [curr.plus(this.start), curr.plus(this.end)] : [null, null]),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
eventsAllDay() {
|
eventsAllDay() {
|
||||||
if (!this.allDayEvents)
|
if (!this.allDayEvents)
|
||||||
return [];
|
return [];
|
||||||
return this.mapIntoMainAxis(this.originalEvents.filter(event => event.orig.allDayEvent));
|
|
||||||
|
let eventsAllDay = this.mapIntoMainAxis(this.originalEvents.filter(event => event.orig.allDayEvent));
|
||||||
|
return eventsAllDay;
|
||||||
},
|
},
|
||||||
eventsNormal() {
|
eventsNormal() {
|
||||||
if (!this.allDayEvents)
|
if (!this.allDayEvents)
|
||||||
@@ -153,7 +172,7 @@ export default {
|
|||||||
return this.events.find(e => e.length);
|
return this.events.find(e => e.length);
|
||||||
},
|
},
|
||||||
styleGridCols() {
|
styleGridCols() {
|
||||||
let cols = 'repeat(' + this.axisMain.length + ', 1fr)';
|
let cols = 'repeat(' + this.displayedAxisMain.length + ', 1fr)';
|
||||||
if (this.axisMainCollapsible) {
|
if (this.axisMainCollapsible) {
|
||||||
if (this.hasValidEvents)
|
if (this.hasValidEvents)
|
||||||
cols = this.events
|
cols = this.events
|
||||||
@@ -181,7 +200,7 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.eventsNormal.forEach((events, mainIndex) => {
|
this.eventsNormal.forEach((events, mainIndex) => {
|
||||||
let day = this.axisMain[mainIndex];
|
let day = this.displayedAxisMain[mainIndex];
|
||||||
events.forEach(event => {
|
events.forEach(event => {
|
||||||
if (!event.startsHere && !event.endsHere)
|
if (!event.startsHere && !event.endsHere)
|
||||||
return;
|
return;
|
||||||
@@ -211,15 +230,17 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
mapIntoMainAxis(target) {
|
mapIntoMainAxis(target) {
|
||||||
const result = Array.from({length: this.axisMain.length}, () => Array());
|
const result = Array.from({length: this.displayedAxisMain.length}, () => Array());
|
||||||
|
|
||||||
target.forEach(event => {
|
target.forEach(event => {
|
||||||
const start = event.start || this.axisMainBorders[0].plus(-1);
|
const start = event.start || this.axisMainBorders[0].plus(-1);
|
||||||
const end = event.end || this.axisMainBorders[this.axisMainBorders.length - 1].plus(1);
|
const end = event.end || this.axisMainBorders[this.axisMainBorders.length - 1].plus(1);
|
||||||
|
|
||||||
for (var i = 0; i < this.axisMain.length; i++) {
|
this.displayedAxisMain.forEach((part, index) => {
|
||||||
let laneStart = this.axisMainBorders[i * 2];
|
if (!part) return;
|
||||||
let laneEnd = this.axisMainBorders[i * 2 + 1];
|
|
||||||
|
let laneStart = this.axisMainBorders[index * 2];
|
||||||
|
let laneEnd = this.axisMainBorders[index * 2 + 1];
|
||||||
if (event.orig?.allDayEvent) {
|
if (event.orig?.allDayEvent) {
|
||||||
laneStart = laneStart.startOf('day');
|
laneStart = laneStart.startOf('day');
|
||||||
laneEnd = laneEnd.endOf('day');
|
laneEnd = laneEnd.endOf('day');
|
||||||
@@ -227,13 +248,13 @@ export default {
|
|||||||
if (start < laneEnd && end > laneStart) {
|
if (start < laneEnd && end > laneStart) {
|
||||||
const startsHere = start >= laneStart;
|
const startsHere = start >= laneStart;
|
||||||
const endsHere = end <= laneEnd;
|
const endsHere = end <= laneEnd;
|
||||||
result[i].push({
|
result[index].push({
|
||||||
...event,
|
...event,
|
||||||
startsHere,
|
startsHere,
|
||||||
endsHere
|
endsHere
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -317,22 +338,31 @@ export default {
|
|||||||
<div
|
<div
|
||||||
class="fhc-calendar-base-grid"
|
class="fhc-calendar-base-grid"
|
||||||
style="display:grid;width:100%;height:100%;overflow:auto"
|
style="display:grid;width:100%;height:100%;overflow:auto"
|
||||||
:style="'grid-template-' + axisRow + 's:auto' + (allDayEvents ? ' auto ' : ' ') + '1fr;grid-template-' + axisCol + 's:auto ' + styleGridCols"
|
:style="'--fhc-grid-displayed-axis-main-count: ' + displayedAxisMain.length + ';grid-template-' + axisRow + 's:auto' + (allDayEvents ? ' auto ' : ' ') + '1fr;grid-template-' + axisCol + 's:auto ' + styleGridCols"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="grid-header"
|
class="grid-header"
|
||||||
style="display:grid"
|
style="display:grid"
|
||||||
:style="'grid-template-' + axisCol + 's:subgrid;grid-' + axisCol + ':1/-1'"
|
:style="'grid-template-' + axisCol + 's:subgrid;grid-' + axisCol + ':1/-1'"
|
||||||
>
|
>
|
||||||
<div
|
<template v-for="(date, index) in displayedAxisMain" :key="index">
|
||||||
v-for="(date, index) in axisMain"
|
<div
|
||||||
:key="index"
|
v-if="date"
|
||||||
class="main-header"
|
class="main-header"
|
||||||
:class="{'collapsed-header': axisMainCollapsible && hasValidEvents && !events[index].length}"
|
:class="{
|
||||||
:style="'grid-' + axisCol + ':' + (2+index)"
|
'collapsed-header': axisMainCollapsible && hasValidEvents && !events[index].length,
|
||||||
>
|
'main-header-sunday': date.weekday === 7,
|
||||||
<slot name="main-header" v-bind="{ index, date }" />
|
}"
|
||||||
</div>
|
:style="'grid-' + axisCol + ':' + (2+index)"
|
||||||
|
>
|
||||||
|
<slot name="main-header" v-bind="{ index, date }" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="main-header main-header-empty"
|
||||||
|
:style="'grid-' + axisCol + ':' + (2+index)"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="allDayEvents"
|
v-if="allDayEvents"
|
||||||
@@ -340,26 +370,34 @@ export default {
|
|||||||
style="display:grid"
|
style="display:grid"
|
||||||
:style="'grid-template-' + axisCol + 's:subgrid;grid-' + axisCol + ':1/-1'"
|
:style="'grid-template-' + axisCol + 's:subgrid;grid-' + axisCol + ':1/-1'"
|
||||||
>
|
>
|
||||||
<div
|
<template v-for="(events, index) in eventsAllDay" :key="index">
|
||||||
v-for="(events, index) in eventsAllDay"
|
<div
|
||||||
:key="index"
|
v-if="displayedAxisMain[index]"
|
||||||
class="all-day-events"
|
class="all-day-events"
|
||||||
:style="'grid-' + axisCol + ':' + (2+index)"
|
:class="{'all-day-events-sunday': displayedAxisMain[index].weekday === 7}"
|
||||||
>
|
:style="'grid-' + axisCol + ':' + (2+index)"
|
||||||
<grid-line-event
|
|
||||||
v-for="(event, i) in events"
|
|
||||||
:key="i"
|
|
||||||
:event="event"
|
|
||||||
>
|
>
|
||||||
<template v-slot="slot">
|
<grid-line-event
|
||||||
<slot name="event" v-bind="slot" />
|
v-for="(event, i) in events"
|
||||||
</template>
|
:key="i"
|
||||||
</grid-line-event>
|
:event="event"
|
||||||
</div>
|
>
|
||||||
|
<template v-slot="slot">
|
||||||
|
<slot name="event" v-bind="slot" />
|
||||||
|
</template>
|
||||||
|
</grid-line-event>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
class="all-day-events"
|
||||||
|
:style="'grid-' + axisCol + ':' + (2+index)"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
ref="scroller"
|
ref="scroller"
|
||||||
@scrollend="userScroll ? disableAutoScroll() : userScroll = true"
|
@scrollend="userScroll ? disableAutoScroll() : userScroll = true"
|
||||||
|
id="grid-main-scrollable"
|
||||||
style="display:grid;overflow:auto"
|
style="display:grid;overflow:auto"
|
||||||
:style="'grid-' + axisCol + ':1/-1;grid-template-' + axisCol + 's:subgrid'"
|
:style="'grid-' + axisCol + ':1/-1;grid-template-' + axisCol + 's:subgrid'"
|
||||||
>
|
>
|
||||||
@@ -371,9 +409,9 @@ export default {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-for="(part, index) in axisPartsSave"
|
v-for="(part, index) in axisPartsSave"
|
||||||
:key="index"
|
:key="index"2
|
||||||
class="part-header"
|
class="part-header"
|
||||||
:style="'grid-' + axisCol + ':1;grid-' + axisRow + ': ps_' + index + '/pe_' + index"
|
:style="'grid-' + axisCol + ':1;grid-' + axisRow + ': ps_' + index + '/pe_' + index + ';min-width:50px;'"
|
||||||
>
|
>
|
||||||
<slot name="part-header" v-bind="{ index, part }" />
|
<slot name="part-header" v-bind="{ index, part }" />
|
||||||
</div>
|
</div>
|
||||||
@@ -389,43 +427,56 @@ export default {
|
|||||||
@dragover="dropAllowed ? $event.preventDefault() : null"
|
@dragover="dropAllowed ? $event.preventDefault() : null"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
v-for="(date, index) in axisMain"
|
v-for="(date, index) in displayedAxisMain"
|
||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
<div
|
<template v-if="date">
|
||||||
v-for="(part, i) in axisPartsSave"
|
|
||||||
:key="i"
|
|
||||||
class="part-body"
|
|
||||||
style="position:relative"
|
|
||||||
:style="'grid-' + axisCol + ':' + (1+index) + ';grid-' + axisRow + ':ps_' + i + '/pe_' + i"
|
|
||||||
>
|
|
||||||
<slot name="part-body" v-bind="{ index, part }" />
|
|
||||||
<div
|
<div
|
||||||
v-if="snapToGrid && dragging"
|
v-for="(part, i) in axisPartsSave"
|
||||||
style="position:absolute;inset:0;z-index:1"
|
:key="i"
|
||||||
v-cal-dnd:dropzone.once="{date: date.plus(part.start || part), ends: ends.slice(ends.findIndex(end => end > date))}"
|
class="part-body"
|
||||||
></div>
|
style="position:relative"
|
||||||
</div>
|
:style="'grid-' + axisCol + ':' + (1+index) + ';grid-' + axisRow + ':ps_' + i + '/pe_' + i"
|
||||||
<grid-line
|
>
|
||||||
:start="date.plus(start)"
|
<slot name="part-body" v-bind="{ index, part }" />
|
||||||
:end="date.plus(end)"
|
|
||||||
:date="date"
|
|
||||||
:events="eventsNormal[index]"
|
|
||||||
:backgrounds="backgrounds[index]"
|
|
||||||
style="position:relative"
|
|
||||||
:style="'grid-' + axisRow + ':1/-1;grid-' + axisCol + ':' + (1+index)"
|
|
||||||
>
|
|
||||||
<template #event="slot">
|
|
||||||
<slot name="event" v-bind="slot" />
|
|
||||||
</template>
|
|
||||||
<template #dropzone>
|
|
||||||
<div
|
<div
|
||||||
v-if="!snapToGrid && dragging"
|
v-if="snapToGrid && dragging"
|
||||||
style="position:absolute;inset:0;z-index:1"
|
style="position:absolute;inset:0;z-index:1"
|
||||||
v-cal-dnd:dropzone="evt => getTimestampFromMouse(evt, date)"
|
v-cal-dnd:dropzone.once="{date: date.plus(part.start || part), ends: ends.slice(ends.findIndex(end => end > date))}"
|
||||||
></div>
|
></div>
|
||||||
</template>
|
</div>
|
||||||
</grid-line>
|
<grid-line
|
||||||
|
:start="date.plus(start)"
|
||||||
|
:end="date.plus(end)"
|
||||||
|
:date="date"
|
||||||
|
:events="eventsNormal[index]"
|
||||||
|
:backgrounds="backgrounds[index]"
|
||||||
|
style="position:relative"
|
||||||
|
:style="'grid-' + axisRow + ':1/-1;grid-' + axisCol + ':' + (1+index)"
|
||||||
|
>
|
||||||
|
<template #event="slot">
|
||||||
|
<slot name="event" v-bind="slot" />
|
||||||
|
</template>
|
||||||
|
<template #dropzone>
|
||||||
|
<div
|
||||||
|
v-if="!snapToGrid && dragging"
|
||||||
|
style="position:absolute;inset:0;z-index:1"
|
||||||
|
v-cal-dnd:dropzone="evt => getTimestampFromMouse(evt, date)"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
|
</grid-line>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div
|
||||||
|
v-for="(part, i) in axisPartsSave"
|
||||||
|
:key="i"
|
||||||
|
class="part-header-repeat part-body"
|
||||||
|
style="position:relative"
|
||||||
|
:style="'grid-' + axisCol + ':' + (1+index) + ';grid-' + axisRow + ':ps_' + i + '/pe_' + i"
|
||||||
|
>
|
||||||
|
<slot name="part-header-repeat" v-bind="{ index, part }" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -123,6 +123,9 @@ export default {
|
|||||||
template: /* html */ `
|
template: /* html */ `
|
||||||
<div
|
<div
|
||||||
class="fhc-calendar-base-grid-line"
|
class="fhc-calendar-base-grid-line"
|
||||||
|
:class="{
|
||||||
|
'fhc-calendar-base-grid-line-sunday': $props.date.weekday === 7
|
||||||
|
}"
|
||||||
style="position:relative;display:grid;grid-auto-flow:dense"
|
style="position:relative;display:grid;grid-auto-flow:dense"
|
||||||
:style="'grid-template-' + axisRow + 's:subgrid'"
|
:style="'grid-template-' + axisRow + 's:subgrid'"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ export default {
|
|||||||
btnMonth: Boolean,
|
btnMonth: Boolean,
|
||||||
btnWeek: Boolean,
|
btnWeek: Boolean,
|
||||||
btnDay: Boolean,
|
btnDay: Boolean,
|
||||||
btnList: Boolean
|
btnList: Boolean,
|
||||||
|
btnRange: Boolean,
|
||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
"next",
|
"next",
|
||||||
@@ -35,7 +36,8 @@ export default {
|
|||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
open: false
|
open: false,
|
||||||
|
semesterOptions: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -53,6 +55,15 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
<div class="header-modes">
|
<div class="header-modes">
|
||||||
<div class="d-flex gap-1 justify-content-end" role="group">
|
<div class="d-flex gap-1 justify-content-end" role="group">
|
||||||
|
<button
|
||||||
|
v-if="btnRange"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
:class="{active: mode === 'range'}"
|
||||||
|
@click="clickMode($event, 'range')"
|
||||||
|
>
|
||||||
|
<i class="fa fa-calendar"></i>
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="btnMonth"
|
v-if="btnMonth"
|
||||||
type="button"
|
type="button"
|
||||||
@@ -95,9 +106,10 @@ export default {
|
|||||||
<div class="header-picker">
|
<div class="header-picker">
|
||||||
<div class="btn-group" role="group">
|
<div class="btn-group" role="group">
|
||||||
<button
|
<button
|
||||||
class="btn btn-outline-secondary border-0"
|
v-if="$props.mode !== 'range'"
|
||||||
@click="$emit('prev')"
|
@click="$emit('prev')"
|
||||||
:disabled="open"
|
:disabled="open"
|
||||||
|
class="btn btn-outline-secondary border-0"
|
||||||
>
|
>
|
||||||
<i class="fa fa-chevron-left"></i>
|
<i class="fa fa-chevron-left"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -110,9 +122,10 @@ export default {
|
|||||||
:list-length="modeOptions.length"
|
:list-length="modeOptions.length"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="btn btn-outline-secondary border-0"
|
v-if="$props.mode !== 'range'"
|
||||||
@click="$emit('next')"
|
@click="$emit('next')"
|
||||||
:disabled="open"
|
:disabled="open"
|
||||||
|
class="btn btn-outline-secondary border-0"
|
||||||
>
|
>
|
||||||
<i class="fa fa-chevron-right"></i>
|
<i class="fa fa-chevron-right"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ export default {
|
|||||||
components: {
|
components: {
|
||||||
VueDatePicker
|
VueDatePicker
|
||||||
},
|
},
|
||||||
inject: [
|
inject: {
|
||||||
"locale",
|
locale: "locale",
|
||||||
"timezone"
|
timezone: "timezone",
|
||||||
],
|
rangeLength: {default: 30}
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
date: {
|
date: {
|
||||||
type: luxon.DateTime,
|
type: luxon.DateTime,
|
||||||
@@ -21,7 +22,7 @@ export default {
|
|||||||
listLength: {
|
listLength: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 7
|
default: 7
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
"update:date"
|
"update:date"
|
||||||
@@ -38,6 +39,8 @@ export default {
|
|||||||
return {month: this.convertedDate.month-1, year: this.convertedDate.year};
|
return {month: this.convertedDate.month-1, year: this.convertedDate.year};
|
||||||
case "list":
|
case "list":
|
||||||
return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.listLength }).ts - 1];
|
return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.listLength }).ts - 1];
|
||||||
|
case "range":
|
||||||
|
return [this.convertedDate.startOf('day').ts, this.convertedDate.startOf('day').plus({ days: this.rangeLength }).ts - 1];
|
||||||
case "week":
|
case "week":
|
||||||
return [this.convertedDate.startOf('week', { useLocaleWeeks: true }).ts, this.convertedDate.endOf('week', { useLocaleWeeks: true }).ts];
|
return [this.convertedDate.startOf('week', { useLocaleWeeks: true }).ts, this.convertedDate.endOf('week', { useLocaleWeeks: true }).ts];
|
||||||
case "day":
|
case "day":
|
||||||
@@ -56,6 +59,8 @@ export default {
|
|||||||
return this.$p.t('calendar/year_kw', { year, week });
|
return this.$p.t('calendar/year_kw', { year, week });
|
||||||
case "list":
|
case "list":
|
||||||
return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + this.date.plus({ days: this.listLength - 1 }).toLocaleString(luxon.DateTime.DATE_FULL);
|
return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + this.date.plus({ days: this.listLength - 1 }).toLocaleString(luxon.DateTime.DATE_FULL);
|
||||||
|
case "range":
|
||||||
|
return this.date.toLocaleString(luxon.DateTime.DATE_FULL) + '-' + this.date.plus({ days: this.rangeLength - 1 }).toLocaleString(luxon.DateTime.DATE_FULL);
|
||||||
case "day":
|
case "day":
|
||||||
return this.date.toLocaleString(luxon.DateTime.DATE_FULL);
|
return this.date.toLocaleString(luxon.DateTime.DATE_FULL);
|
||||||
default:
|
default:
|
||||||
@@ -64,11 +69,21 @@ export default {
|
|||||||
},
|
},
|
||||||
weekStart() {
|
weekStart() {
|
||||||
return luxon.Info.getStartOfWeek(this.date)%7;
|
return luxon.Info.getStartOfWeek(this.date)%7;
|
||||||
}
|
},
|
||||||
|
rangeConfig() {
|
||||||
|
if (this.$props.mode === "list") {
|
||||||
|
return { autoRange: listLength - 1 };
|
||||||
|
} else if (this.$props.mode === "range") {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
update(value) {
|
update(value) {
|
||||||
let date;
|
let date;
|
||||||
|
let rangeLength;
|
||||||
switch (this.mode) {
|
switch (this.mode) {
|
||||||
case "month":
|
case "month":
|
||||||
value.month++;
|
value.month++;
|
||||||
@@ -78,13 +93,18 @@ export default {
|
|||||||
case "week":
|
case "week":
|
||||||
date = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
|
date = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
|
||||||
break;
|
break;
|
||||||
|
case "range":
|
||||||
|
date = luxon.DateTime.fromJSDate(value[0]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
|
||||||
|
let endDate = luxon.DateTime.fromJSDate(value[1]).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
|
||||||
|
rangeLength = Math.floor(endDate.diff(date, "days").toObject().days) + 1;
|
||||||
|
break;
|
||||||
case "day":
|
case "day":
|
||||||
date = luxon.DateTime.fromJSDate(value).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
|
date = luxon.DateTime.fromJSDate(value).setZone(this.timezone, { keepLocalTime: true }).setLocale(this.locale);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return; // Don't update if the value is invalid!
|
return; // Don't update if the value is invalid!
|
||||||
}
|
}
|
||||||
this.$emit("update:date", date);
|
this.$emit("update:date", { date, rangeLength });
|
||||||
},
|
},
|
||||||
weekNumbers(date) {
|
weekNumbers(date) {
|
||||||
return luxon.DateTime.fromJSDate(date, { locale: this.locale }).localWeekNumber;
|
return luxon.DateTime.fromJSDate(date, { locale: this.locale }).localWeekNumber;
|
||||||
@@ -97,7 +117,7 @@ export default {
|
|||||||
:format="() => title"
|
:format="() => title"
|
||||||
:month-picker="mode == 'month'"
|
:month-picker="mode == 'month'"
|
||||||
:week-picker="mode == 'week'"
|
:week-picker="mode == 'week'"
|
||||||
:range="mode == 'list' ? { autoRange: listLength - 1 } : false"
|
:range="rangeConfig"
|
||||||
:text-input="mode == 'day'"
|
:text-input="mode == 'day'"
|
||||||
:week-start="weekStart"
|
:week-start="weekStart"
|
||||||
:week-numbers="{ type: weekNumbers }"
|
:week-numbers="{ type: weekNumbers }"
|
||||||
|
|||||||
@@ -26,7 +26,11 @@ export default {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
alignItemsClassSuffix: {
|
||||||
|
type: String,
|
||||||
|
default: "end",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
sanitizedTimestamps() {
|
sanitizedTimestamps() {
|
||||||
@@ -49,7 +53,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
<div class="fhc-calendar-base-label-time">
|
<div class="fhc-calendar-base-label-time" :class="'align-items-' + $props.alignItemsClassSuffix">
|
||||||
<span v-if="start">{{ start }}</span>
|
<span v-if="start">{{ start }}</span>
|
||||||
<span v-if="end">-</span>
|
<span v-if="end">-</span>
|
||||||
<span v-if="end">{{ end }}</span>
|
<span v-if="end">{{ end }}</span>
|
||||||
|
|||||||
@@ -9,13 +9,21 @@ import ModeDay from './Mode/Day.js';
|
|||||||
import ModeWeek from './Mode/Week.js';
|
import ModeWeek from './Mode/Week.js';
|
||||||
import ModeMonth from './Mode/Month.js';
|
import ModeMonth from './Mode/Month.js';
|
||||||
import ModeList from './Mode/List.js';
|
import ModeList from './Mode/List.js';
|
||||||
|
import ModeRange from './Mode/Range.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "CalendarLvPlan",
|
name: "CalendarLvPlan",
|
||||||
components: {
|
components: {
|
||||||
FhcCalendar
|
FhcCalendar
|
||||||
},
|
},
|
||||||
inject: ["isMobile"],
|
inject: {
|
||||||
|
isMobile: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
rangeLength: {
|
||||||
|
default: 30,
|
||||||
|
}
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
date: {
|
date: {
|
||||||
type: [Date, String, Number, luxon.DateTime],
|
type: [Date, String, Number, luxon.DateTime],
|
||||||
@@ -28,7 +36,11 @@ export default {
|
|||||||
getPromiseFunc: {
|
getPromiseFunc: {
|
||||||
type: Function,
|
type: Function,
|
||||||
required: true
|
required: true
|
||||||
}
|
},
|
||||||
|
shouldIncludeRangeMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
@@ -68,31 +80,37 @@ export default {
|
|||||||
backgrounds() {
|
backgrounds() {
|
||||||
let now = luxon.DateTime.now().setZone(this.timezone);
|
let now = luxon.DateTime.now().setZone(this.timezone);
|
||||||
|
|
||||||
if (this.mode == 'Month')
|
if (this.mode == 'Month') {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
class: 'background-past',
|
class: 'background-past',
|
||||||
end: now.startOf('day')
|
end: now.startOf('day')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
} else if (this.mode == 'Range') {
|
||||||
return [
|
return [];
|
||||||
{
|
} else {
|
||||||
class: 'background-past',
|
return [
|
||||||
end: now,
|
{
|
||||||
label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false })
|
class: 'background-past',
|
||||||
}
|
end: now,
|
||||||
];
|
label: now.startOf('minute').toISOTime({ suppressSeconds: true, includeOffset: false })
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modes() {
|
modes() {
|
||||||
let modes = {
|
let modes = {
|
||||||
day: Vue.markRaw(ModeDay),
|
day: Vue.markRaw(ModeDay),
|
||||||
month: Vue.markRaw(ModeMonth),
|
month: Vue.markRaw(ModeMonth),
|
||||||
};
|
};
|
||||||
if (this.isMobile) {
|
if (!this.isMobile) {
|
||||||
modes.list = Vue.markRaw(ModeList);
|
|
||||||
} else {
|
|
||||||
modes.week = Vue.markRaw(ModeWeek);
|
modes.week = Vue.markRaw(ModeWeek);
|
||||||
|
if (this.$props.shouldIncludeRangeMode) {
|
||||||
|
modes.range = Vue.markRaw(ModeRange);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
modes.list = Vue.markRaw(ModeList);
|
||||||
}
|
}
|
||||||
|
|
||||||
return modes;
|
return modes;
|
||||||
@@ -163,7 +181,7 @@ export default {
|
|||||||
:backgrounds="backgrounds"
|
:backgrounds="backgrounds"
|
||||||
:time-grid="teachingunits"
|
:time-grid="teachingunits"
|
||||||
show-btns
|
show-btns
|
||||||
@update:date="(newDate, newMode) => $emit('update:date', newDate, newMode)"
|
@update:date="(newDate, newMode, newRangeLength) => $emit('update:date', newDate, newMode, newRangeLength)"
|
||||||
@update:mode="(newMode, newDate) => $emit('update:mode', newMode, newDate)"
|
@update:mode="(newMode, newDate) => $emit('update:mode', newMode, newDate)"
|
||||||
@update:range="updateRange"
|
@update:range="updateRange"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
import BaseSlider from "../Base/Slider.js";
|
||||||
|
import RangeView from "./Range/View.js";
|
||||||
|
import FormInput from "../../Form/Input.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ModeRange",
|
||||||
|
components: {
|
||||||
|
BaseSlider,
|
||||||
|
RangeView,
|
||||||
|
FormInput,
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
rangeLength: {
|
||||||
|
default: 30,
|
||||||
|
},
|
||||||
|
rangeViewPresets: {
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
currentDate: {
|
||||||
|
type: luxon.DateTime,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ["update:currentDate", "update:range", "click", "requestModalOpen"],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedRangePreset: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
range() {
|
||||||
|
let first = this.$props.currentDate;
|
||||||
|
let last = first.plus({ days: this.rangeLength });
|
||||||
|
|
||||||
|
return luxon.Interval.fromDateTimes(first, last);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
currentDate() {
|
||||||
|
this.$emit("update:range", this.range);
|
||||||
|
},
|
||||||
|
rangeLength() {
|
||||||
|
this.$emit("update:range", this.range);
|
||||||
|
},
|
||||||
|
selectedRangePreset() {
|
||||||
|
if (!this.selectedRangePreset) return;
|
||||||
|
|
||||||
|
const preset = this.rangeViewPresets.presets.find(
|
||||||
|
(preset) => preset.name === this.selectedRangePreset,
|
||||||
|
);
|
||||||
|
if (!preset) return;
|
||||||
|
|
||||||
|
this.$emit("update:date", {
|
||||||
|
date: preset.startDate,
|
||||||
|
rangeLength:
|
||||||
|
preset.endDate.diff(preset.startDate, "days").days + 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.selectedRangePreset = null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
viewAttrs() {
|
||||||
|
const day = this.$props.currentDate.startOf("day");
|
||||||
|
const rangeLength = parseInt(this.rangeLength);
|
||||||
|
return { ...this.$attrs, day, rangeLength };
|
||||||
|
},
|
||||||
|
handleClickDefaults(evt) {
|
||||||
|
switch (evt.detail.source) {
|
||||||
|
case "day":
|
||||||
|
// default: Set current-date
|
||||||
|
this.$emit("update:currentDate", {
|
||||||
|
date: evt.detail.value,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "event":
|
||||||
|
// default: Request Modal
|
||||||
|
this.$emit("requestModalOpen", { event: evt.detail.value });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$emit("update:range", this.range);
|
||||||
|
},
|
||||||
|
template: /*html*/ `
|
||||||
|
<div
|
||||||
|
class="fhc-calendar-mode-range flex-grow-1 position-relative d-flex flex-column"
|
||||||
|
@cal-click-default.capture="handleClickDefaults"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="rangeViewPresets?.presets?.length"
|
||||||
|
id="rangePresetSelector"
|
||||||
|
class="w-100 d-flex flex-row gap-2 justify-content-center align-items-center py-2"
|
||||||
|
>
|
||||||
|
<span>{{ rangeViewPresets.label }}</span>
|
||||||
|
<form-input
|
||||||
|
name="rangePresetSelector"
|
||||||
|
type="select"
|
||||||
|
v-model="selectedRangePreset"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="preset in rangeViewPresets.presets"
|
||||||
|
:key="preset.name"
|
||||||
|
:value="preset.name"
|
||||||
|
>
|
||||||
|
{{ preset.name }}
|
||||||
|
</option>
|
||||||
|
</form-input>
|
||||||
|
</div>
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<base-slider ref="slider" v-slot="slot">
|
||||||
|
<range-view ref="view" v-bind="viewAttrs()">
|
||||||
|
<template v-slot="slot"><slot v-bind="slot" mode="range" /></template>
|
||||||
|
</range-view>
|
||||||
|
</base-slider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
};
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import CalendarGrid from '../../Base/Grid.js';
|
||||||
|
import LabelDay from '../../Base/Label/Day.js';
|
||||||
|
import LabelDow from '../../Base/Label/Dow.js';
|
||||||
|
import LabelTime from '../../Base/Label/Time.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "RangeView",
|
||||||
|
components: {
|
||||||
|
CalendarGrid,
|
||||||
|
LabelDay,
|
||||||
|
LabelDow,
|
||||||
|
LabelTime
|
||||||
|
},
|
||||||
|
inject: {
|
||||||
|
timeGrid: "timeGrid",
|
||||||
|
timezone: "timezone"
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
day: {
|
||||||
|
type: luxon.DateTime,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
collapseEmptyDays: Boolean,
|
||||||
|
rangeLength: Number,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
axisMain() {
|
||||||
|
return Array.from({ length: this.$props.rangeLength }, (e, i) => this.$props.day.plus({ days: i }));
|
||||||
|
},
|
||||||
|
axisParts() {
|
||||||
|
if (this.timeGrid) {
|
||||||
|
// create {start, end} array
|
||||||
|
return this.timeGrid.map(tu => {
|
||||||
|
return {
|
||||||
|
start: luxon.Duration.fromISOTime(tu.start),
|
||||||
|
end: luxon.Duration.fromISOTime(tu.end)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// create 07:00-23:00
|
||||||
|
return Array.from({ length: 17 }, (e, i) => luxon.Duration.fromObject({ hours: i + 7 }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isToday(date) {
|
||||||
|
return date.hasSame(luxon.DateTime.now().setZone(this.timezone), 'day');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: /* html */`
|
||||||
|
<div class="fhc-calendar-mode-range-view h-100">
|
||||||
|
<calendar-grid
|
||||||
|
ref="grid"
|
||||||
|
:axis-main="axisMain"
|
||||||
|
:axis-parts="axisParts"
|
||||||
|
:axis-main-collapsible="collapseEmptyDays"
|
||||||
|
:snap-to-grid="!!timeGrid"
|
||||||
|
:flip-axis="true"
|
||||||
|
:arePartHeadersRepeated="true"
|
||||||
|
all-day-events
|
||||||
|
>
|
||||||
|
<template #main-header="{ date }">
|
||||||
|
<div :class="{ today: isToday(date) }">
|
||||||
|
<label-dow
|
||||||
|
v-bind="{ date }"
|
||||||
|
@cal-click="evt => evt.detail.source = 'day'"
|
||||||
|
/>
|
||||||
|
<label-day
|
||||||
|
v-bind="{ date }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #part-header="{ part }">
|
||||||
|
<label-time v-bind="{ part }" :alignItemsClassSuffix="'center'" />
|
||||||
|
</template>
|
||||||
|
<template #part-header-repeat="{ part }">
|
||||||
|
<label-time v-bind="{ part }" :alignItemsClassSuffix="'center'" />
|
||||||
|
</template>
|
||||||
|
<template #event="slot">
|
||||||
|
<div v-if="slot.event.type == 'loading'" class="placeholder-glow h-100 opacity-50">
|
||||||
|
<span class="placeholder w-100 h-100" />
|
||||||
|
</div>
|
||||||
|
<slot v-else v-bind="slot" />
|
||||||
|
</template>
|
||||||
|
</calendar-grid>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import FhcCalendar from "../../Calendar/LvPlan.js";
|
|||||||
|
|
||||||
import ApiLvPlan from '../../../api/factory/lvPlan.js';
|
import ApiLvPlan from '../../../api/factory/lvPlan.js';
|
||||||
import ApiAuthinfo from '../../../api/factory/authinfo.js';
|
import ApiAuthinfo from '../../../api/factory/authinfo.js';
|
||||||
|
import ApiStudiensemester from '../../../api/factory/studiensemester.js';
|
||||||
|
|
||||||
export const DEFAULT_MODE_LVPLAN_DESKTOP = "Week";
|
export const DEFAULT_MODE_LVPLAN_DESKTOP = "Week";
|
||||||
export const DEFAULT_MODE_LVPLAN_MOBILE = "List";
|
export const DEFAULT_MODE_LVPLAN_MOBILE = "List";
|
||||||
@@ -23,9 +24,23 @@ export default {
|
|||||||
isMitarbeiter: false,
|
isMitarbeiter: false,
|
||||||
isStudent: false,
|
isStudent: false,
|
||||||
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
|
||||||
|
semesterRangePresets: {
|
||||||
|
label: null,
|
||||||
|
presets: [],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
inject: ["isMobile"],
|
inject: ["isMobile"],
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
rangeLength: Vue.computed(() => {
|
||||||
|
if (!this.$route.params.range_length) return 30;
|
||||||
|
else if (this.$route.params.range_length > 365) return 365;
|
||||||
|
else return this.$route.params.range_length;
|
||||||
|
}),
|
||||||
|
rangeViewPresets: Vue.computed(() => this.semesterRangePresets)
|
||||||
|
};
|
||||||
|
},
|
||||||
computed:{
|
computed:{
|
||||||
currentDay() {
|
currentDay() {
|
||||||
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
|
if (!this.propsViewData?.focus_date || isNaN(new Date(this.propsViewData?.focus_date)))
|
||||||
@@ -34,7 +49,11 @@ export default {
|
|||||||
},
|
},
|
||||||
currentMode() {
|
currentMode() {
|
||||||
let validModes = ["day", "month"];
|
let validModes = ["day", "month"];
|
||||||
validModes.push(this.isMobile ? "list" : "week");
|
if (!this.isMobile) {
|
||||||
|
validModes.push("week", "range");
|
||||||
|
} else {
|
||||||
|
validModes.push("list");
|
||||||
|
}
|
||||||
|
|
||||||
const defaultMode = this.isMobile
|
const defaultMode = this.isMobile
|
||||||
? DEFAULT_MODE_LVPLAN_MOBILE
|
? DEFAULT_MODE_LVPLAN_MOBILE
|
||||||
@@ -94,10 +113,10 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleChangeDate(day, newMode) {
|
handleChangeDate(day, newMode, rangeLength) {
|
||||||
return this.handleChangeMode(newMode, day);
|
return this.handleChangeMode(newMode, day, rangeLength);
|
||||||
},
|
},
|
||||||
handleChangeMode(newMode, day) {
|
handleChangeMode(newMode, day, range_length = null) {
|
||||||
const mode = newMode[0].toUpperCase() + newMode.slice(1)
|
const mode = newMode[0].toUpperCase() + newMode.slice(1)
|
||||||
const focus_date = day.toISODate();
|
const focus_date = day.toISODate();
|
||||||
|
|
||||||
@@ -105,7 +124,8 @@ export default {
|
|||||||
name: "MyLvPlan",
|
name: "MyLvPlan",
|
||||||
params: {
|
params: {
|
||||||
mode,
|
mode,
|
||||||
focus_date
|
focus_date,
|
||||||
|
range_length,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -134,13 +154,32 @@ export default {
|
|||||||
this.isMitarbeiter = authInfo.isMitarbeiter;
|
this.isMitarbeiter = authInfo.isMitarbeiter;
|
||||||
this.isStudent = authInfo.isStudent;
|
this.isStudent = authInfo.isStudent;
|
||||||
},
|
},
|
||||||
|
async fetchSemesters() {
|
||||||
|
const semestersResponse = await this.$api.call(ApiStudiensemester.getAll());
|
||||||
|
if (semestersResponse.meta.status === "success") {
|
||||||
|
this.semesterRangePresets = {
|
||||||
|
label: "View specific semester",
|
||||||
|
presets: semestersResponse.data.map((semester) => {
|
||||||
|
let startDate = luxon.DateTime.fromISO(semester.start);
|
||||||
|
let endDate = luxon.DateTime.fromISO(semester.ende);
|
||||||
|
return {
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
name: semester.studiensemester_kurzbz,
|
||||||
|
description: semester.bezeichnung,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
await this.fetchAuthInfo();
|
await this.fetchAuthInfo();
|
||||||
|
await this.fetchSemesters();
|
||||||
},
|
},
|
||||||
template: /*html*/`
|
template: /*html*/`
|
||||||
<div class="cis-lvplan-personal d-flex flex-column h-100">
|
<div class="cis-lvplan-personal d-flex flex-column h-100">
|
||||||
<h2>
|
<h2 id="cis-lvplan-personal-heading">
|
||||||
{{ $p.t('lehre/stundenplan') }}
|
{{ $p.t('lehre/stundenplan') }}
|
||||||
<span v-if="studiensemester_kurzbz" class="ps-3">
|
<span v-if="studiensemester_kurzbz" class="ps-3">
|
||||||
{{ studiensemester_kurzbz }}
|
{{ studiensemester_kurzbz }}
|
||||||
@@ -154,6 +193,7 @@ export default {
|
|||||||
:get-promise-func="getPromiseFunc"
|
:get-promise-func="getPromiseFunc"
|
||||||
:date="currentDay"
|
:date="currentDay"
|
||||||
:mode="currentMode"
|
:mode="currentMode"
|
||||||
|
:shouldIncludeRangeMode="true"
|
||||||
@update:date="handleChangeDate"
|
@update:date="handleChangeDate"
|
||||||
@update:mode="handleChangeMode"
|
@update:mode="handleChangeMode"
|
||||||
@update:range="updateRange"
|
@update:range="updateRange"
|
||||||
|
|||||||
Reference in New Issue
Block a user