Minor mods

This commit is contained in:
Ivymaster
2026-10-21 09:51:19 +02:00
parent 5dd8f648ac
commit ed3da03c75
14 changed files with 128 additions and 342 deletions
-2
View File
@@ -19,8 +19,6 @@ class Tempus extends Auth_Controller
// Load Config
$this->load->config('calendar');
$this->load->helper('hlp_header_helper');
}
public function index()
+1 -1
View File
@@ -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)
{
-14
View File
@@ -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') {
+94 -96
View File
@@ -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;
+2 -8
View File
@@ -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',
+5 -13
View File
@@ -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 };
}
}
+24 -69
View File
@@ -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 };
}
}