Event moving fixed again, splitting correctly for recurrent events.
This commit is contained in:
		| @@ -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) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Leo Vasanko
					Leo Vasanko