Major new version #2
@ -161,6 +161,25 @@ function startLocalDrag(init, evt) {
|
|||||||
window.addEventListener('pointercancel', onDragPointerUp, { passive: false })
|
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) {
|
function onDragPointerMove(e) {
|
||||||
const st = dragState.value
|
const st = dragState.value
|
||||||
if (!st) return
|
if (!st) return
|
||||||
@ -234,9 +253,12 @@ function normalizeDateOrder(aStr, bStr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyRangeDuringDrag(st, startDate, endDate) {
|
function applyRangeDuringDrag(st, startDate, endDate) {
|
||||||
// If dragging a virtual occurrence, map to base move without changing recurrence pattern mid-series.
|
if (st.isVirtual) {
|
||||||
// We disallow resizing individual virtual occurrences; treat as move of whole series anchor.
|
if (st.mode !== 'move') return // no resize for virtual occurrence
|
||||||
if (st.isVirtual && st.mode !== 'move') return
|
// 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 })
|
store.setEventRange(st.id, startDate, endDate, { mode: st.mode })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,6 +586,98 @@ export const useCalendarStore = defineStore('calendar', {
|
|||||||
// no expansion
|
// 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
|
// Split a repeating series at a given occurrence index; returns new series id
|
||||||
splitRepeatSeries(baseId, occurrenceIndex, newStartStr, newEndStr, _grabbedWeekday) {
|
splitRepeatSeries(baseId, occurrenceIndex, newStartStr, newEndStr, _grabbedWeekday) {
|
||||||
const base = this._findEventInAnyList(baseId)
|
const base = this._findEventInAnyList(baseId)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user