From ccb33cde60a9b19488ccafca9b41c0ed23ab1ef5 Mon Sep 17 00:00:00 2001 From: Ivymaster Date: Wed, 9 Sep 2026 16:53:23 +0200 Subject: [PATCH] WIP --- public/css/components/calendar/events.css | 21 +++++ public/js/components/Calendar/Base/Grid.js | 5 +- .../Calendar/Base/Grid/Line/Event.js | 24 ++++- public/js/components/Calendar/Tempus.js | 50 +++++++++-- .../Renderer/Lehreinheit/calendarEvent.js | 42 ++++++--- public/js/helpers/Tempus/ResizeGhost.js | 18 ++-- public/js/helpers/Tempus/ResizeHandler.js | 89 ++++++++++++++----- 7 files changed, 196 insertions(+), 53 deletions(-) diff --git a/public/css/components/calendar/events.css b/public/css/components/calendar/events.css index f224c6f7b..b10303a7f 100644 --- a/public/css/components/calendar/events.css +++ b/public/css/components/calendar/events.css @@ -109,3 +109,24 @@ z-index: 2; } +.fhc-calendar-mode-range .fhc-calendar-base-grid-line-event { + position: relative; +} + +.fhc-calendar-mode-range .fhc-resize-bar { + position: absolute; + top: 0; + bottom: 0; + height: auto; + width: 15px; + margin: 0; + cursor: ew-resize; +} + +.fhc-calendar-mode-range .fhc-resize-bar--left { + left: 0; +} + +.fhc-calendar-mode-range .fhc-resize-bar--right { + right: 0; +} diff --git a/public/js/components/Calendar/Base/Grid.js b/public/js/components/Calendar/Base/Grid.js index 081d6b2a9..148a46fd4 100644 --- a/public/js/components/Calendar/Base/Grid.js +++ b/public/js/components/Calendar/Base/Grid.js @@ -479,7 +479,7 @@ export default { ctrlKey: !!(evt?.ctrlKey || evt?.metaKey) }); }, - handleResizeStart({ edge, evt, el, event }) + handleResizeStart({ edge, evt, horizontal, el, event }) { const gridEl = this.$refs.body; @@ -490,6 +490,7 @@ export default { el, gridEl, event, + horizontal, timeGrid: this.timeGrid, onEnd: ({ event, newStart, newEnd }) => { const orig = event?.orig; @@ -650,4 +651,4 @@ export default { ` -} \ No newline at end of file +} diff --git a/public/js/components/Calendar/Base/Grid/Line/Event.js b/public/js/components/Calendar/Base/Grid/Line/Event.js index 04d733583..1bd97ef8c 100644 --- a/public/js/components/Calendar/Base/Grid/Line/Event.js +++ b/public/js/components/Calendar/Base/Grid/Line/Event.js @@ -94,6 +94,7 @@ export default { this.$emit('resize-start', { edge, evt, + horizontal: this.mode === 'range', el: this.$refs.eventEl, event: this.event }); @@ -153,7 +154,7 @@ export default { data-cy="calendar-event" >
+
+ +
+
+ +
-
` -} \ No newline at end of file +} diff --git a/public/js/components/Calendar/Tempus.js b/public/js/components/Calendar/Tempus.js index 64b5ef298..07b3d7441 100644 --- a/public/js/components/Calendar/Tempus.js +++ b/public/js/components/Calendar/Tempus.js @@ -5,15 +5,22 @@ import { useEventLoader } from '../../composables/EventLoader.js'; import ModeWeek from './Mode/Week.js'; import ModeMonth from './Mode/Month.js'; import ModeTable from './Mode/Table.js'; +import ModeRange from './Mode/Range.js'; import ApiKalender from '../../api/factory/tempus/kalender.js'; import draggable from '../../directives/draggable.js'; - +import ApiStudiensemester from '../../api/factory/studiensemester.js'; export default { name: "CalendarTempus", components: { FhcCalendar }, + provide() { + return { + rangeLength: Vue.computed(() => this.rangeLength), + rangeViewPresets: Vue.computed(() => this.semesterRangePresets) + }; + }, inject: { renderers: {from: 'renderers'}, canToggleGrid: { @@ -25,7 +32,14 @@ export default { default: { visible_status: 'all' } - } + }, + rangeLength: { + default: 1, + }, + shouldIncludeRangeMode: { + type: Boolean, + default: false, + }, }, directives: { draggable, @@ -79,13 +93,13 @@ export default { "event-unhover", "open-reservierung" ], - data() { return { modes: { week: Vue.markRaw(ModeWeek), month: Vue.markRaw(ModeMonth), tableList: Vue.markRaw(ModeTable), + range: Vue.markRaw(ModeRange), }, modeOptions: { day: { @@ -100,6 +114,10 @@ export default { teachingunits: null, hoursplan: null, showRaster: true, + semesterRangePresets: { + label: null, + presets: [], + }, }; }, computed: { @@ -114,7 +132,9 @@ export default { end: now.startOf('day') }]; } - else + else if (this.mode == 'Range') { + return []; + } else { past = [{ class: 'background-past', @@ -183,6 +203,24 @@ export default { navigateNext() { this.$refs.calendar.clickNext(); }, + 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, + }; + }) + }; + } + }, }, setup(props, context) { const rangeInterval = Vue.ref(null); @@ -220,6 +258,8 @@ export default { end: res.data.end }; }); + + this.fetchSemesters(); }, template: /* html */` -
- - - -
{{event.verplante_stunden}}
@@ -172,7 +165,28 @@ export default { {{ end }}
- {{ topicString }} +
+ {{ topicString }} +
+ + + +
+
maxBottom) - newHeight = maxBottom - activeResize.startTop; + 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); - ghost.updatePosition(null, newHeight); + if (activeResize.horizontal) + ghost.updatePosition(null, null, constrainedSize); + else + ghost.updatePosition(null, constrainedSize); } else if (activeResize.edge === 'start') { - let newTop = activeResize.startTop + draggedPx; - let newHeight = activeResize.startHeight - draggedPx; + const startPosition = activeResize.horizontal + ? activeResize.startLeft + : activeResize.startTop; + const startSize = activeResize.horizontal + ? activeResize.startWidth + : activeResize.startHeight; + let newPosition = startPosition + draggedPx; + let newSize = startSize - draggedPx; - if (newTop < 0) + if (newPosition < 0) { - newHeight -= (0 - newTop); - newTop = 0; + newSize -= (0 - newPosition); + newPosition = 0; } - if (newHeight < MIN_HEIGHT_PX) + if (newSize < MIN_HEIGHT_PX) { - newTop = (activeResize.startTop + activeResize.startHeight) - MIN_HEIGHT_PX; - newHeight = MIN_HEIGHT_PX; + newPosition = (startPosition + startSize) - MIN_HEIGHT_PX; + newSize = MIN_HEIGHT_PX; } - ghost.updatePosition(newTop, newHeight); + if (activeResize.horizontal) + ghost.updatePosition(null, null, newSize, newPosition); + else + ghost.updatePosition(newPosition, newSize); } updateGhostLabel(); @@ -176,9 +215,9 @@ export function useResizeHandler() { activeResize = null; } - function startResize(edge, evt, { el, gridEl, event, timeGrid, onEnd }) + function startResize(edge, evt, { el, gridEl, event, horizontal = false, timeGrid, onEnd }) { - const { startTop, startHeight } = ghost.create(gridEl, el, edge); + const { startTop, startHeight, startLeft, startWidth } = ghost.create(gridEl, el, edge); activeResize = { edge, @@ -186,11 +225,15 @@ export function useResizeHandler() { eventEl: el, gridEl, event, + horizontal, timeGrid, onEnd, dragStartY: (evt.clientY - gridEl.getBoundingClientRect().top) + gridEl.scrollTop, + dragStartX: (evt.clientX - gridEl.getBoundingClientRect().left) + gridEl.scrollLeft, startTop, startHeight, + startLeft, + startWidth, originalOpacity: el.style.opacity, }; @@ -214,4 +257,4 @@ export function useResizeHandler() { } return { startResize, cleanup }; -} \ No newline at end of file +}