mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-10-22 11:49:36 +00:00
Minor mods
This commit is contained in:
@@ -19,8 +19,6 @@ class Tempus extends Auth_Controller
|
||||
|
||||
// Load Config
|
||||
$this->load->config('calendar');
|
||||
|
||||
$this->load->helper('hlp_header_helper');
|
||||
}
|
||||
|
||||
public function index()
|
||||
|
||||
@@ -58,7 +58,7 @@ class CollisionChecker
|
||||
foreach ($this->_checks as $check)
|
||||
{
|
||||
if (empty($remaining_ids)) break;
|
||||
log_message('error', 'CollisionChecker: Running check ' . $check->getName() . ' for ' . count($remaining_ids) . ' remaining kalender_ids');
|
||||
|
||||
$batchResult = $check->checkAll($remaining_ids);
|
||||
foreach ($batchResult as $kalender_id => $errors)
|
||||
{
|
||||
|
||||
@@ -110,20 +110,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.weekPageContainer .calendar-event-collisions,
|
||||
.rangePageContainer .calendar-event-collisions
|
||||
{
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
background: repeating-linear-gradient(
|
||||
135deg,
|
||||
transparent,
|
||||
transparent 16px,
|
||||
rgba(0, 0, 0, 0.3) 16px,
|
||||
rgba(0, 0, 0, 0.3) 32px
|
||||
);
|
||||
}
|
||||
|
||||
.tag-row-cell {
|
||||
width: 100%;
|
||||
max-width: 0;
|
||||
|
||||
@@ -101,7 +101,6 @@ export default {
|
||||
this.$emit('resize-start', {
|
||||
edge,
|
||||
evt,
|
||||
horizontal: false,
|
||||
el: this.$refs.eventEl,
|
||||
event: this.event,
|
||||
});
|
||||
@@ -176,7 +175,6 @@ export default {
|
||||
>
|
||||
<i class="fa-solid fa-grip-lines text-muted"></i>
|
||||
</div>
|
||||
|
||||
<teleport to="body">
|
||||
<div
|
||||
v-if="contextMenu.show"
|
||||
|
||||
@@ -39,7 +39,6 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
open: false,
|
||||
semesterOptions: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -132,8 +132,6 @@ export default {
|
||||
rangeConfig() {
|
||||
if (this.$props.mode === 'list') {
|
||||
return { autoRange: this.listLength - 1 };
|
||||
} else if (['range', 'tableList'].includes(this.$props.mode)) {
|
||||
return true;
|
||||
} else if (this.$props.mode === 'range') {
|
||||
return true;
|
||||
} else if (this.$props.mode === 'tableList') {
|
||||
|
||||
@@ -2,17 +2,15 @@ export default {
|
||||
name: 'CalendarSlider',
|
||||
inject: {
|
||||
time: {
|
||||
from: "sliderTime",
|
||||
default: ".3s"
|
||||
from: 'sliderTime',
|
||||
default: '.3s',
|
||||
},
|
||||
onVisibleDatesChanged: {
|
||||
from: "calendarVisibleDatesChanged",
|
||||
default: null
|
||||
}
|
||||
from: 'calendarVisibleDatesChanged',
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
'slid'
|
||||
],
|
||||
emits: ['slid'],
|
||||
data() {
|
||||
return {
|
||||
target: 0,
|
||||
@@ -24,15 +22,15 @@ export default {
|
||||
mutationObserver: null,
|
||||
visibleGrids: Vue.markRaw(new Set()),
|
||||
visibilityUpdateScheduled: false,
|
||||
lastVisibleDatesKey: null
|
||||
}
|
||||
lastVisibleDatesKey: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
itemsAfter() {
|
||||
return [...Array(this.extrasAfter)].map((i, k) => 1+k);
|
||||
return [...Array(this.extrasAfter)].map((i, k) => 1 + k);
|
||||
},
|
||||
itemsBefore() {
|
||||
return [...Array(this.extrasBefore)].map((i, k) => k-this.extrasBefore);
|
||||
return [...Array(this.extrasBefore)].map((i, k) => k - this.extrasBefore);
|
||||
},
|
||||
styleSlider() {
|
||||
const style = {
|
||||
@@ -40,10 +38,10 @@ export default {
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
height: '100%',
|
||||
};
|
||||
if (this.running) {
|
||||
style.left = (-this.target * 100) + '%';
|
||||
style.left = -this.target * 100 + '%';
|
||||
style.transition = 'left ' + this.time + ' ease-in-out';
|
||||
}
|
||||
return style;
|
||||
@@ -55,7 +53,7 @@ export default {
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
right: '100%',
|
||||
width: (this.extrasBefore * 100) + '%'
|
||||
width: this.extrasBefore * 100 + '%',
|
||||
};
|
||||
},
|
||||
styleAfter() {
|
||||
@@ -65,96 +63,104 @@ export default {
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
left: '100%',
|
||||
width: (this.extrasAfter * 100) + '%'
|
||||
width: this.extrasAfter * 100 + '%',
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
startVisibilityTracking() {
|
||||
if (!this.onVisibleDatesChanged || typeof IntersectionObserver === 'undefined')
|
||||
if (
|
||||
!this.onVisibleDatesChanged ||
|
||||
typeof IntersectionObserver === 'undefined'
|
||||
)
|
||||
return;
|
||||
|
||||
this.intersectionObserver = Vue.markRaw(new IntersectionObserver(entries => {
|
||||
let changed = false;
|
||||
this.intersectionObserver = Vue.markRaw(
|
||||
new IntersectionObserver(
|
||||
(entries) => {
|
||||
let changed = false;
|
||||
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting && entry.intersectionRatio > 0) {
|
||||
if (!this.visibleGrids.has(entry.target)) {
|
||||
this.visibleGrids.add(entry.target);
|
||||
changed = true;
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting && entry.intersectionRatio > 0) {
|
||||
if (!this.visibleGrids.has(entry.target)) {
|
||||
this.visibleGrids.add(entry.target);
|
||||
changed = true;
|
||||
}
|
||||
} else if (this.visibleGrids.delete(entry.target)) {
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) this.scheduleVisibleDatesUpdate();
|
||||
},
|
||||
{
|
||||
root: this.$el,
|
||||
threshold: 0,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.mutationObserver = Vue.markRaw(
|
||||
new MutationObserver((mutations) => {
|
||||
let changed = false;
|
||||
|
||||
mutations.forEach((mutation) => {
|
||||
if (mutation.type === 'attributes') {
|
||||
if (
|
||||
this.visibleGrids.has(this.getContainingGrid(mutation.target))
|
||||
)
|
||||
changed = true;
|
||||
return;
|
||||
}
|
||||
} else if (this.visibleGrids.delete(entry.target)) {
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (changed)
|
||||
this.scheduleVisibleDatesUpdate();
|
||||
}, {
|
||||
root: this.$el,
|
||||
threshold: 0
|
||||
}));
|
||||
mutation.addedNodes.forEach((node) => this.observeGrids(node));
|
||||
mutation.removedNodes.forEach((node) => {
|
||||
this.getGrids(node).forEach((grid) => {
|
||||
this.intersectionObserver.unobserve(grid);
|
||||
if (this.visibleGrids.delete(grid)) changed = true;
|
||||
});
|
||||
});
|
||||
|
||||
this.mutationObserver = Vue.markRaw(new MutationObserver(mutations => {
|
||||
let changed = false;
|
||||
|
||||
mutations.forEach(mutation => {
|
||||
if (mutation.type === 'attributes') {
|
||||
if (this.visibleGrids.has(this.getContainingGrid(mutation.target)))
|
||||
changed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
mutation.addedNodes.forEach(node => this.observeGrids(node));
|
||||
mutation.removedNodes.forEach(node => {
|
||||
this.getGrids(node).forEach(grid => {
|
||||
this.intersectionObserver.unobserve(grid);
|
||||
if (this.visibleGrids.delete(grid))
|
||||
changed = true;
|
||||
});
|
||||
});
|
||||
|
||||
if (this.visibleGrids.has(this.getContainingGrid(mutation.target)))
|
||||
changed = true;
|
||||
});
|
||||
|
||||
if (changed)
|
||||
{
|
||||
this.scheduleVisibleDatesUpdate();
|
||||
}
|
||||
}));
|
||||
if (changed) {
|
||||
this.scheduleVisibleDatesUpdate();
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
this.observeGrids(this.$el);
|
||||
this.mutationObserver.observe(this.$el, {
|
||||
subtree: true,
|
||||
childList: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['data-visible-start', 'data-visible-end']
|
||||
attributeFilter: ['data-visible-start', 'data-visible-end'],
|
||||
});
|
||||
},
|
||||
getGrids(node) {
|
||||
if (!(node instanceof Element))
|
||||
return [];
|
||||
if (!(node instanceof Element)) return [];
|
||||
|
||||
const grids = node.matches('.fhc-calendar-base-grid') ? [node] : [];
|
||||
return grids.concat([...node.querySelectorAll('.fhc-calendar-base-grid')]);
|
||||
return grids.concat([
|
||||
...node.querySelectorAll('.fhc-calendar-base-grid'),
|
||||
]);
|
||||
},
|
||||
observeGrids(node) {
|
||||
this.getGrids(node).forEach(grid => {
|
||||
this.getGrids(node).forEach((grid) => {
|
||||
this.intersectionObserver.observe(grid);
|
||||
});
|
||||
},
|
||||
getContainingGrid(node) {
|
||||
if (!(node instanceof Element))
|
||||
return null;
|
||||
if (!(node instanceof Element)) return null;
|
||||
|
||||
return node.matches('.fhc-calendar-base-grid')
|
||||
? node
|
||||
: node.closest('.fhc-calendar-base-grid');
|
||||
},
|
||||
scheduleVisibleDatesUpdate() {
|
||||
if (this.visibilityUpdateScheduled)
|
||||
return;
|
||||
if (this.visibilityUpdateScheduled) return;
|
||||
|
||||
this.visibilityUpdateScheduled = true;
|
||||
this.$nextTick(() => {
|
||||
@@ -165,41 +171,35 @@ export default {
|
||||
emitVisibleDates() {
|
||||
const datesByKey = new Map();
|
||||
|
||||
this.visibleGrids.forEach(grid => {
|
||||
this.visibleGrids.forEach((grid) => {
|
||||
const dates = this.getGridVisibleDates(grid);
|
||||
if (dates)
|
||||
datesByKey.set(dates.start + '/' + dates.end, dates);
|
||||
if (dates) datesByKey.set(dates.start + '/' + dates.end, dates);
|
||||
});
|
||||
|
||||
const dates = [...datesByKey.values()]
|
||||
.sort((a, b) => a.start.localeCompare(b.start));
|
||||
if (!dates.length)
|
||||
return;
|
||||
const dates = [...datesByKey.values()].sort((a, b) =>
|
||||
a.start.localeCompare(b.start),
|
||||
);
|
||||
if (!dates.length) return;
|
||||
|
||||
const key = dates.map(date => date.start + '/' + date.end).join('|');
|
||||
const key = dates.map((date) => date.start + '/' + date.end).join('|');
|
||||
|
||||
if (key === this.lastVisibleDatesKey)
|
||||
return;
|
||||
if (key === this.lastVisibleDatesKey) return;
|
||||
|
||||
this.lastVisibleDatesKey = key;
|
||||
this.onVisibleDatesChanged(dates);
|
||||
},
|
||||
getGridVisibleDates(grid) {
|
||||
if (!grid.isConnected)
|
||||
return null;
|
||||
if (!grid.isConnected) return null;
|
||||
|
||||
let start = null;
|
||||
let end = null;
|
||||
grid.querySelectorAll('.fhc-calendar-base-grid-line').forEach(line => {
|
||||
grid.querySelectorAll('.fhc-calendar-base-grid-line').forEach((line) => {
|
||||
const lineStart = line.dataset.visibleStart;
|
||||
const lineEnd = line.dataset.visibleEnd;
|
||||
if (!lineStart || !lineEnd)
|
||||
return;
|
||||
if (!lineStart || !lineEnd) return;
|
||||
|
||||
if (!start || lineStart.localeCompare(start) < 0)
|
||||
start = lineStart;
|
||||
if (!end || lineEnd.localeCompare(end) > 0)
|
||||
end = lineEnd;
|
||||
if (!start || lineStart.localeCompare(start) < 0) start = lineStart;
|
||||
if (!end || lineEnd.localeCompare(end) > 0) end = lineEnd;
|
||||
});
|
||||
|
||||
return start && end ? { start, end } : null;
|
||||
@@ -225,16 +225,14 @@ export default {
|
||||
return this.slidePages(num);
|
||||
},
|
||||
slidePages(dir) {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve) => {
|
||||
this.promiseResolve = resolve;
|
||||
this.running = true;
|
||||
const newTarget = this.target + dir;
|
||||
if (newTarget > 0) {
|
||||
if (this.extrasAfter < newTarget)
|
||||
this.extrasAfter = newTarget;
|
||||
if (this.extrasAfter < newTarget) this.extrasAfter = newTarget;
|
||||
} else if (newTarget < 0) {
|
||||
if (-this.extrasBefore > newTarget)
|
||||
this.extrasBefore = -newTarget;
|
||||
if (-this.extrasBefore > newTarget) this.extrasBefore = -newTarget;
|
||||
}
|
||||
this.target = newTarget;
|
||||
});
|
||||
@@ -248,7 +246,7 @@ export default {
|
||||
this.running = false;
|
||||
this.target = 0;
|
||||
this.extrasAfter = this.extrasBefore = 0;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.startVisibilityTracking();
|
||||
@@ -256,7 +254,7 @@ export default {
|
||||
beforeUnmount() {
|
||||
this.stopVisibilityTracking();
|
||||
},
|
||||
template: /* html */`
|
||||
template: /* html */ `
|
||||
<div
|
||||
class="fhc-calendar-base-slider h-100"
|
||||
style="position:relative;overflow:hidden"
|
||||
@@ -288,5 +286,5 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
<div class="fhc-calendar-base-grid" :class="{'h-100': $props.shouldMatchParentHeight}"
|
||||
style="display:grid;width:100%;height:100%;overflow:auto" data-cy="calendar-base-grid"
|
||||
:style="'--fhc-grid-displayed-axis-main-count: ' + displayedAxisMain.length + ';height:' + (scrollable ? '100%' : 'auto') + ';min-height:' + (scrollable ? '0' : '100%') + ';overflow:' + (scrollable ? 'auto' : 'visible') + ';grid-template-' + axisRow + 's:auto' + (allDayEvents ? ' auto ' : ' ') + 'minmax(0, 1fr);grid-template-' + axisCol + 's:max-content ' + styleGridCols">
|
||||
<div class="grid-header" style="display:grid"
|
||||
:style="'grid-template-' + axisCol + 's:subgrid;grid-' + axisCol + ':1/-1;position:' + (stickyHeader ? 'sticky' : 'static') + ';top:0;z-index:' + (stickyHeader ? '100' : 'auto') + ';align-self:' + (stickyHeader ? 'start' : 'auto') + ';border-bottom:' + (stickyHeader ? '1px solid var(--bs-gray-500, #adb5bd)' : 'none')">
|
||||
<template v-for="(date, index) in displayedAxisMain" :key="index">
|
||||
<div v-if="date" class="main-header" :class="{
|
||||
'collapsed-header': isDayCollapsed(index),
|
||||
'main-header-sunday': date.weekday === 7,
|
||||
}" :style="'grid-' + axisCol + ':' + (2+index)">
|
||||
<slot name="main-header" v-bind="{ index: displayedAxisMainIndexes[index], date }" />
|
||||
</div>
|
||||
<div v-else class="main-header main-header-empty" :style="'grid-' + axisCol + ':' + (2+index)"></div>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="allDayEvents" class="grid-allday" style="display:grid"
|
||||
:style="'grid-template-' + axisCol + 's:subgrid;grid-' + axisCol + ':1/-1'">
|
||||
<template v-for="(events, index) in eventsAllDay" :key="index">
|
||||
<div v-if="displayedAxisMain[index]" class="all-day-events"
|
||||
:class="{'all-day-events-sunday': displayedAxisMain[index].weekday === 7}"
|
||||
:style="'grid-' + axisCol + ':' + (2+index)">
|
||||
<grid-line-event v-for="(event, i) in events"
|
||||
:key="event.orig?.eindeutige_kalender_gruppen_id ?? event.orig?.kalender_id ?? i" :event="event">
|
||||
<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 ref="scroller" @scrollend="userScroll ? disableAutoScroll() : userScroll = true" id="grid-main-scrollable"
|
||||
style="display:grid;overflow:auto"
|
||||
:style="'overflow:' + (scrollable ? 'auto' : 'visible') + ';grid-' + axisCol + ':1/-1;grid-template-' + axisCol + 's:subgrid'">
|
||||
<div ref="main" class="grid-main" style="position:relative;grid-column:1/-1;grid-row:1/-1;display:grid"
|
||||
:style="'grid-template-' + axisCol + 's:subgrid;grid-template-' + axisRow + 's:' + styleGridRows">
|
||||
<div v-for="(part, index) in axisPartsSave" :key="index" class="part-header"
|
||||
:class="{'part-header-followed-by-break': isFollowedByBreak(index)}"
|
||||
:style="'grid-' + axisCol + ':1;grid-' + axisRow + ': ps_' + index + '/pe_' + index + ';min-width:50px;'">
|
||||
<slot name="part-header" v-bind="{ index, part }" />
|
||||
</div>
|
||||
|
||||
<div ref="body" class="grid-body"
|
||||
style="display:grid;grid-template-rows:subgrid;grid-template-columns:subgrid"
|
||||
@dragenter="isDragging = true" @dragleave.self="isDragging = false" @dragend="isDragging = false"
|
||||
@drop="isDragging = false" :style="'grid-' + axisCol + ':2/-1;grid-' + axisRow + ':1/-1'">
|
||||
<template v-for="(date, index) in displayedAxisMain" :key="index">
|
||||
<div v-for="(part, i) in axisPartsSave" :key="i" class="part-body"
|
||||
:class="{'part-body-followed-by-break': isFollowedByBreak(i)}" style="position:relative"
|
||||
:style="'grid-' + axisCol + ':' + (1+index) + ';grid-' + axisRow + ':ps_' + i + '/pe_' + i"
|
||||
:data-drop-index="index * axisPartsSave.length + i + 1" data-cy="calendar-grid-part">
|
||||
<slot name="part-body" v-bind="{ index, part }" />
|
||||
|
||||
<div v-if="isFreeSlot(date, part, eventsNormal[index]) && reservierbar"
|
||||
class="fhc-calendar-empty-slot" style="position:absolute; inset:0; z-index:1"
|
||||
v-cal-click:slot="{ date, part }"
|
||||
:title="reservierbarMap?.[date.toISODate()] ? $p.t('LvPlan/add_reservation') : $p.t('LvPlan/reservation_not_allowed')">
|
||||
<div class="fhc-calendar-empty-slot-plus">
|
||||
<i v-if="reservierbarMap?.[date.toISODate()]" class="fa-solid fa-plus"></i>
|
||||
<i v-else class="fa-solid fa-ban" style="color: red;"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div 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"
|
||||
:data-drop-index="displayedAxisMainIndexes[index] * axisPartsSave.length + i + 1"
|
||||
data-cy="calendar-grid-part">
|
||||
<slot name="part-body" v-bind="{ index: displayedAxisMainIndexes[index], part }" />
|
||||
<div v-if="snapToGrid" class="fhc-calendar-base-grid-dropzone"
|
||||
style="position:absolute;inset:0" :style="{ zIndex: isDragging ? 10 : 1 }"
|
||||
:data-day="date.toFormat('yyyy-MM-dd')"
|
||||
v-drop:move.lehreinheit.kalender.reservierung="(evt, item) => onDropSnap(evt, item, date, part)">
|
||||
</div>
|
||||
</div>
|
||||
<grid-line :start="date.plus(start)" :end="date.plus(end)" :date="date"
|
||||
:events="eventsNormal[index]" :backgrounds="backgrounds[index]"
|
||||
:collapsed="isDayUnchecked(index)" style="position:relative"
|
||||
@resize-start="handleResizeStart"
|
||||
: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" class="fhc-calendar-base-grid-dropzone"
|
||||
style="position:absolute;inset:0" :style="{ zIndex: isDragging ? 10 : 1 }"
|
||||
v-drop:move.lehreinheit.kalender.reservierung="(evt, item) => onDropFree(evt, item, date)">
|
||||
</div>
|
||||
</template>
|
||||
</grid-line>
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,7 +102,6 @@ export default {
|
||||
start: luxon.Duration.fromISOTime(tu.start),
|
||||
end: luxon.Duration.fromISOTime(tu.end),
|
||||
};
|
||||
scroll;
|
||||
});
|
||||
} else if (this.hoursPlan) {
|
||||
const start = this.hoursPlan?.start ?? 7;
|
||||
|
||||
@@ -27,11 +27,10 @@ export default {
|
||||
},
|
||||
provide() {
|
||||
return {
|
||||
rangeLength: Vue.computed(() => this.currentRangeLength),
|
||||
rangeViewPresets: Vue.computed(() => this.semesterRangePresets),
|
||||
rangeViewPreviewLink: Vue.computed(() => this.rangeViewPreviewLink),
|
||||
showRaster: Vue.computed(() => this.showRaster),
|
||||
teachingunits: Vue.computed(() => this.teachingunits),
|
||||
rangeLength: Vue.computed(() => this.currentRangeLength),
|
||||
rangeViewPreviewLink: Vue.computed(() => this.rangeViewPreviewLink),
|
||||
rangeViewPresetsConfig: Vue.computed(() => {
|
||||
return {
|
||||
label: this.$p.t('LvPlan/view_specific_semester'),
|
||||
@@ -42,10 +41,6 @@ export default {
|
||||
},
|
||||
inject: {
|
||||
renderers: { from: 'renderers' },
|
||||
currentSemester: {
|
||||
from: 'currentSemester',
|
||||
default: null,
|
||||
},
|
||||
canToggleGrid: {
|
||||
from: 'canToggleGrid',
|
||||
default: false,
|
||||
@@ -178,7 +173,6 @@ export default {
|
||||
hoursplan: null,
|
||||
showRaster: true,
|
||||
currentRangeLength: getRangeLength(this.range) ?? this.rangeLength,
|
||||
selectedRangePreset: null, //this.currentSemester, TODO: This is a hack to make the range mode work, but it should be fixed in the future
|
||||
semesterRangePresets: {
|
||||
label: null,
|
||||
presets: [],
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
export default {
|
||||
inject: {
|
||||
mode: {
|
||||
from: 'mode',
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
event: {
|
||||
type: Object,
|
||||
@@ -168,31 +162,13 @@ export default {
|
||||
</div>
|
||||
<div
|
||||
v-if="!event.allDayEvent && event?.beginn && event?.ende"
|
||||
class="event-time d-grid h-100"
|
||||
class="event-time d-none d-xl-grid h-100"
|
||||
>
|
||||
<span>{{ start }}</span>
|
||||
<span>{{ end }}</span>
|
||||
</div>
|
||||
<div class="event-text" v-tooltip="tooltipString">
|
||||
<div
|
||||
>
|
||||
<span class="event-topic">{{ topicString }}</span>
|
||||
<div
|
||||
class="d-flex gap-1 position-absolute top-0 start-0 m-1"
|
||||
>
|
||||
<i :class="statusIcon"></i>
|
||||
<i
|
||||
v-if="event.has_assigned_resources"
|
||||
v-tooltip="resourcesTooltip"
|
||||
class="fa-solid fa-table-list text-muted"
|
||||
></i>
|
||||
<i
|
||||
v-if="tags?.length"
|
||||
v-tooltip="tagsTooltip"
|
||||
class="fa-solid fa-tags"
|
||||
></i>
|
||||
</div>
|
||||
</div>
|
||||
<span class="event-topic">{{ topicString }}</span>
|
||||
<span
|
||||
class="event-place"
|
||||
data-cy="calendar-event-room"
|
||||
|
||||
@@ -430,9 +430,6 @@ export default {
|
||||
if (!eventEl) return;
|
||||
|
||||
const calendar = document.querySelector('.fhc-calendar-base-grid');
|
||||
const eventRect = eventEl.getBoundingClientRect();
|
||||
|
||||
const offset = 300;
|
||||
|
||||
const isInsideScrolledView =
|
||||
eventEl.offsetLeft < calendar.scrollLeft + calendar.clientWidth &&
|
||||
@@ -440,7 +437,6 @@ export default {
|
||||
eventEl.offsetTop < calendar.scrollTop + calendar.clientHeight &&
|
||||
eventEl.offsetTop + eventEl.offsetHeight > calendar.scrollTop;
|
||||
|
||||
const rect = eventEl.getBoundingClientRect();
|
||||
if (!isInsideScrolledView) {
|
||||
eventEl.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
|
||||
@@ -59,9 +59,7 @@ export function useResizeGhost() {
|
||||
|
||||
return {
|
||||
startTop: topInGrid,
|
||||
startHeight: eventRect.height,
|
||||
startLeft: leftInGrid,
|
||||
startWidth: eventRect.width
|
||||
startHeight: eventRect.height
|
||||
};
|
||||
}
|
||||
|
||||
@@ -71,7 +69,7 @@ export function useResizeGhost() {
|
||||
labelEl.textContent = text;
|
||||
}
|
||||
|
||||
function updatePosition(top, height, width, left)
|
||||
function updatePosition(top, height)
|
||||
{
|
||||
if (!ghostEl)
|
||||
return;
|
||||
@@ -79,21 +77,15 @@ export function useResizeGhost() {
|
||||
ghostEl.style.top = `${top}px`;
|
||||
if (height !== null)
|
||||
ghostEl.style.height = `${height}px`;
|
||||
if (width !== null)
|
||||
ghostEl.style.width = `${width}px`;
|
||||
if (left !== null)
|
||||
ghostEl.style.left = `${left}px`;
|
||||
}
|
||||
|
||||
function getPosition()
|
||||
{
|
||||
if (!ghostEl)
|
||||
return { top: 0, height: 0, left: 0, width: 0 };
|
||||
return { top: 0, height: 0 };
|
||||
return {
|
||||
top: parseFloat(ghostEl.style.top),
|
||||
height: parseFloat(ghostEl.style.height),
|
||||
left: parseFloat(ghostEl.style.left),
|
||||
width: parseFloat(ghostEl.style.width)
|
||||
height: parseFloat(ghostEl.style.height)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -107,4 +99,4 @@ export function useResizeGhost() {
|
||||
}
|
||||
|
||||
return { create, updateLabel, updatePosition, getPosition, remove };
|
||||
}
|
||||
}
|
||||
@@ -41,30 +41,20 @@ function getSnapTimes(timeGrid, dayISO, zoneName)
|
||||
|
||||
function calculateNewTimes(activeResize, ghostPosition)
|
||||
{
|
||||
const { edge, event, timeGrid, horizontal, startTop, startHeight, startLeft, startWidth } = activeResize;
|
||||
const { edge, event, timeGrid, startTop, startHeight } = activeResize;
|
||||
const { start, end } = event;
|
||||
|
||||
const durationMinutes = end.diff(start, 'minutes').minutes;
|
||||
if (!durationMinutes || durationMinutes <= 0)
|
||||
return null;
|
||||
|
||||
const startSize = horizontal ? startWidth : startHeight;
|
||||
if (!Number.isFinite(startSize) || startSize <= 0)
|
||||
return null;
|
||||
|
||||
const pxPerMinute = startSize / durationMinutes;
|
||||
const pxPerMinute = startHeight / durationMinutes;
|
||||
|
||||
let draggedPx = 0;
|
||||
if (edge === 'end')
|
||||
draggedPx = horizontal
|
||||
? ghostPosition.width - startWidth
|
||||
: ghostPosition.height - startHeight;
|
||||
draggedPx = ghostPosition.height - startHeight;
|
||||
if (edge === 'start')
|
||||
draggedPx = horizontal
|
||||
? ghostPosition.left - startLeft
|
||||
: ghostPosition.top - startTop;
|
||||
if (!Number.isFinite(draggedPx))
|
||||
return null;
|
||||
draggedPx = ghostPosition.top - startTop;
|
||||
|
||||
const draggedMinutes = snapToGrid(draggedPx / pxPerMinute, edge);
|
||||
|
||||
@@ -107,12 +97,6 @@ export function useResizeHandler() {
|
||||
return (evt.clientY - gridRect.top) + activeResize.gridEl.scrollTop;
|
||||
}
|
||||
|
||||
function getPointerXInGrid(evt)
|
||||
{
|
||||
const gridRect = activeResize.gridEl.getBoundingClientRect();
|
||||
return (evt.clientX - gridRect.left) + activeResize.gridEl.scrollLeft;
|
||||
}
|
||||
|
||||
function updateGhostLabel()
|
||||
{
|
||||
const result = calculateNewTimes(activeResize, ghost.getPosition());
|
||||
@@ -127,59 +111,36 @@ export function useResizeHandler() {
|
||||
return;
|
||||
evt.preventDefault();
|
||||
|
||||
const maxEnd = activeResize.horizontal
|
||||
? activeResize.gridEl.scrollWidth
|
||||
: activeResize.gridEl.scrollHeight;
|
||||
const pointer = activeResize.horizontal
|
||||
? getPointerXInGrid(evt)
|
||||
: getPointerYInGrid(evt);
|
||||
const draggedPx = pointer - (activeResize.horizontal
|
||||
? activeResize.dragStartX
|
||||
: activeResize.dragStartY);
|
||||
const maxBottom = activeResize.gridEl.scrollHeight;
|
||||
const pointerY = getPointerYInGrid(evt);
|
||||
const draggedPx = pointerY - activeResize.dragStartY;
|
||||
|
||||
if (activeResize.edge === 'end')
|
||||
{
|
||||
const minSize = MIN_HEIGHT_PX;
|
||||
const newSize = Math.max(minSize, (activeResize.horizontal
|
||||
? activeResize.startWidth
|
||||
: activeResize.startHeight) + draggedPx);
|
||||
const startPosition = activeResize.horizontal
|
||||
? activeResize.startLeft
|
||||
: activeResize.startTop;
|
||||
const constrainedSize = Math.min(newSize, maxEnd - startPosition);
|
||||
let newHeight = Math.max(MIN_HEIGHT_PX, activeResize.startHeight + draggedPx);
|
||||
if (activeResize.startTop + newHeight > maxBottom)
|
||||
newHeight = maxBottom - activeResize.startTop;
|
||||
|
||||
if (activeResize.horizontal)
|
||||
ghost.updatePosition(null, null, constrainedSize);
|
||||
else
|
||||
ghost.updatePosition(null, constrainedSize);
|
||||
ghost.updatePosition(null, newHeight);
|
||||
}
|
||||
else if (activeResize.edge === 'start')
|
||||
{
|
||||
const startPosition = activeResize.horizontal
|
||||
? activeResize.startLeft
|
||||
: activeResize.startTop;
|
||||
const startSize = activeResize.horizontal
|
||||
? activeResize.startWidth
|
||||
: activeResize.startHeight;
|
||||
let newPosition = startPosition + draggedPx;
|
||||
let newSize = startSize - draggedPx;
|
||||
let newTop = activeResize.startTop + draggedPx;
|
||||
let newHeight = activeResize.startHeight - draggedPx;
|
||||
|
||||
if (newPosition < 0)
|
||||
if (newTop < 0)
|
||||
{
|
||||
newSize -= (0 - newPosition);
|
||||
newPosition = 0;
|
||||
newHeight -= (0 - newTop);
|
||||
newTop = 0;
|
||||
}
|
||||
|
||||
if (newSize < MIN_HEIGHT_PX)
|
||||
if (newHeight < MIN_HEIGHT_PX)
|
||||
{
|
||||
newPosition = (startPosition + startSize) - MIN_HEIGHT_PX;
|
||||
newSize = MIN_HEIGHT_PX;
|
||||
newTop = (activeResize.startTop + activeResize.startHeight) - MIN_HEIGHT_PX;
|
||||
newHeight = MIN_HEIGHT_PX;
|
||||
}
|
||||
|
||||
if (activeResize.horizontal)
|
||||
ghost.updatePosition(null, null, newSize, newPosition);
|
||||
else
|
||||
ghost.updatePosition(newPosition, newSize);
|
||||
ghost.updatePosition(newTop, newHeight);
|
||||
}
|
||||
|
||||
updateGhostLabel();
|
||||
@@ -197,6 +158,7 @@ export function useResizeHandler() {
|
||||
activeResize.eventEl.style.opacity = activeResize.originalOpacity ?? '';
|
||||
|
||||
const result = calculateNewTimes(activeResize, ghost.getPosition());
|
||||
|
||||
ghost.remove();
|
||||
|
||||
if (result)
|
||||
@@ -211,13 +173,12 @@ export function useResizeHandler() {
|
||||
}
|
||||
}
|
||||
|
||||
activeResize.onFinish?.();
|
||||
activeResize = null;
|
||||
}
|
||||
|
||||
function startResize(edge, evt, { el, gridEl, event, horizontal = false, timeGrid, onEnd, onFinish })
|
||||
function startResize(edge, evt, { el, gridEl, event, timeGrid, onEnd })
|
||||
{
|
||||
const { startTop, startHeight, startLeft, startWidth } = ghost.create(gridEl, el, edge);
|
||||
const { startTop, startHeight } = ghost.create(gridEl, el, edge);
|
||||
|
||||
activeResize = {
|
||||
edge,
|
||||
@@ -225,16 +186,11 @@ export function useResizeHandler() {
|
||||
eventEl: el,
|
||||
gridEl,
|
||||
event,
|
||||
horizontal,
|
||||
timeGrid,
|
||||
onEnd,
|
||||
onFinish,
|
||||
dragStartY: (evt.clientY - gridEl.getBoundingClientRect().top) + gridEl.scrollTop,
|
||||
dragStartX: (evt.clientX - gridEl.getBoundingClientRect().left) + gridEl.scrollLeft,
|
||||
startTop,
|
||||
startHeight,
|
||||
startLeft,
|
||||
startWidth,
|
||||
originalOpacity: el.style.opacity,
|
||||
};
|
||||
|
||||
@@ -254,9 +210,8 @@ export function useResizeHandler() {
|
||||
window.removeEventListener('pointermove', onPointerMove);
|
||||
window.removeEventListener('pointerup', onPointerUp);
|
||||
ghost.remove();
|
||||
activeResize.onFinish?.();
|
||||
activeResize = null;
|
||||
}
|
||||
|
||||
return { startResize, cleanup };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user