Major new version #2

Merged
LeoVasanko merged 86 commits from vol002 into main 2025-08-26 05:58:24 +01:00
2 changed files with 117 additions and 3 deletions
Showing only changes of commit d794758b54 - Show all commits

View File

@ -161,6 +161,25 @@ function startLocalDrag(init, evt) {
window.addEventListener('pointercancel', onDragPointerUp, { passive: false })
}
// Determine date under pointer: traverse DOM to find day cell carrying data-date attribute
function getDateUnderPointer(x, y, el) {
let cur = el
while (cur) {
if (cur.dataset && cur.dataset.date) {
return { date: cur.dataset.date }
}
cur = cur.parentElement
}
// Fallback: elementFromPoint scan
const probe = document.elementFromPoint(x, y)
let p = probe
while (p) {
if (p.dataset && p.dataset.date) return { date: p.dataset.date }
p = p.parentElement
}
return null
}
function onDragPointerMove(e) {
const st = dragState.value
if (!st) return
@ -234,9 +253,12 @@ function normalizeDateOrder(aStr, bStr) {
}
function applyRangeDuringDrag(st, startDate, endDate) {
// If dragging a virtual occurrence, map to base move without changing recurrence pattern mid-series.
// We disallow resizing individual virtual occurrences; treat as move of whole series anchor.
if (st.isVirtual && st.mode !== 'move') return
if (st.isVirtual) {
if (st.mode !== 'move') return // no resize for virtual occurrence
// Split-move: occurrence being dragged treated as first of new series
store.splitMoveVirtualOccurrence(st.id, st.startDate, startDate, endDate)
return
}
store.setEventRange(st.id, startDate, endDate, { mode: st.mode })
}

View File

@ -586,6 +586,98 @@ export const useCalendarStore = defineStore('calendar', {
// no expansion
},
// Split a repeating series at a specific occurrence date and move that occurrence (and future) to new range
splitMoveVirtualOccurrence(baseId, occurrenceDateStr, newStartStr, newEndStr) {
const base = this._findEventInAnyList(baseId)
if (!base || !base.isRepeating) return
const originalCountRaw = base.repeatCount
const spanDays = Math.max(
0,
Math.round(
(fromLocalString(base.endDate) - fromLocalString(base.startDate)) / (24 * 60 * 60 * 1000),
),
)
const occurrenceDate = fromLocalString(occurrenceDateStr)
const baseStart = fromLocalString(base.startDate)
if (occurrenceDate <= baseStart) {
// Moving the base itself: just move entire series
this.setEventRange(baseId, newStartStr, newEndStr, { mode: 'move' })
return
}
let keptOccurrences = 0 // number of occurrences BEFORE the moved one
if (base.repeat === 'weeks') {
const interval = base.repeatInterval || 1
const pattern = base.repeatWeekdays || []
if (!pattern.some(Boolean)) return
const WEEK_MS = 7 * 86400000
const blockStartBase = new Date(baseStart)
blockStartBase.setDate(blockStartBase.getDate() - blockStartBase.getDay())
function isAligned(d) {
const blk = new Date(d)
blk.setDate(d.getDate() - d.getDay())
const diff = Math.floor((blk - blockStartBase) / WEEK_MS)
return diff % interval === 0
}
const cursor = new Date(baseStart)
while (cursor < occurrenceDate) {
if (pattern[cursor.getDay()] && isAligned(cursor)) keptOccurrences++
cursor.setDate(cursor.getDate() + 1)
}
} else if (base.repeat === 'months') {
const diffMonths =
(occurrenceDate.getFullYear() - baseStart.getFullYear()) * 12 +
(occurrenceDate.getMonth() - baseStart.getMonth())
const interval = base.repeatInterval || 1
if (diffMonths <= 0 || diffMonths % interval !== 0) return // invalid occurrence
keptOccurrences = diffMonths // base is occurrence 0; we keep all before diffMonths
} else {
// Unsupported repeat type
return
}
// Truncate original series to keptOccurrences
this._terminateRepeatSeriesAtIndex(baseId, keptOccurrences)
// Compute remaining occurrences count
let remainingCount = 'unlimited'
if (originalCountRaw !== 'unlimited') {
const total = parseInt(originalCountRaw, 10)
if (!isNaN(total)) {
const rem = total - keptOccurrences
if (rem <= 0) return
remainingCount = String(rem)
}
}
// Determine repeat-specific adjustments
let repeatWeekdays = base.repeatWeekdays
if (base.repeat === 'weeks' && Array.isArray(base.repeatWeekdays)) {
// Rotate pattern so that the moved occurrence weekday stays active relative to new anchor
const origWeekday = occurrenceDate.getDay()
const newWeekday = fromLocalString(newStartStr).getDay()
const shift = newWeekday - origWeekday
if (shift !== 0) {
const rotated = [false, false, false, false, false, false, false]
for (let i = 0; i < 7; i++) {
if (base.repeatWeekdays[i]) {
let ni = (i + shift) % 7
if (ni < 0) ni += 7
rotated[ni] = true
}
}
repeatWeekdays = rotated
}
}
// Create continuation series starting at newStartStr
this.createEvent({
title: base.title,
startDate: newStartStr,
endDate: newEndStr,
colorId: base.colorId,
repeat: base.repeat,
repeatInterval: base.repeatInterval,
repeatCount: remainingCount,
repeatWeekdays,
})
},
// Split a repeating series at a given occurrence index; returns new series id
splitRepeatSeries(baseId, occurrenceIndex, newStartStr, newEndStr, _grabbedWeekday) {
const base = this._findEventInAnyList(baseId)