diff --git a/src/components/EventOverlay.vue b/src/components/EventOverlay.vue index 6a60ecd..76cc849 100644 --- a/src/components/EventOverlay.vue +++ b/src/components/EventOverlay.vue @@ -167,6 +167,24 @@ function startLocalDrag(init, evt) { else anchorOffset = daysInclusive(init.startDate, init.anchorDate) - 1 } + // Capture original repeating pattern & weekday (for weekly repeats) so we can rotate relative to original + let originalWeekday = null + let originalPattern = null + if (init.mode === 'move') { + try { + originalWeekday = new Date(init.startDate + 'T00:00:00').getDay() + const baseEv = store.getEventById(init.id) + if ( + baseEv && + baseEv.isRepeating && + baseEv.repeat === 'weeks' && + Array.isArray(baseEv.repeatWeekdays) + ) { + originalPattern = [...baseEv.repeatWeekdays] + } + } catch {} + } + dragState.value = { ...init, anchorOffset, @@ -174,6 +192,9 @@ function startLocalDrag(init, evt) { eventMoved: false, tentativeStart: init.startDate, tentativeEnd: init.endDate, + originalWeekday, + originalPattern, + realizedId: null, // for virtual occurrence converted to real during drag } // Begin compound history session (single snapshot after drag completes) @@ -238,8 +259,41 @@ function onDragPointerMove(e) { if (ns === st.tentativeStart && ne === st.tentativeEnd) return st.tentativeStart = ns st.tentativeEnd = ne - // Real-time update only for non-virtual events (avoid repeated split operations) - if (!st.isVirtual) { + if (st.mode === 'move') { + if (st.isVirtual) { + // On first movement convert virtual occurrence into a real new event (split series) + if (!st.realizedId) { + const newId = store.splitMoveVirtualOccurrence(st.id, st.startDate, ns, ne) + if (newId) { + st.realizedId = newId + st.id = newId + st.isVirtual = false + } else { + return + } + } else { + // Subsequent moves: update range without rotating pattern automatically + store.setEventRange(st.id, ns, ne, { mode: 'move', rotatePattern: false }) + } + } else { + // Normal non-virtual move; rotate handled in setEventRange + store.setEventRange(st.id, ns, ne, { mode: 'move', rotatePattern: false }) + } + // Manual rotation relative to original pattern (keeps pattern anchored to initially grabbed weekday) + if (st.originalPattern && st.originalWeekday != null) { + try { + const currentWeekday = new Date(ns + 'T00:00:00').getDay() + const shift = currentWeekday - st.originalWeekday + const rotated = store._rotateWeekdayPattern([...st.originalPattern], shift) + const ev = store.getEventById(st.id) + if (ev && ev.repeat === 'weeks') { + ev.repeatWeekdays = rotated + store.touchEvents() + } + } catch {} + } + } else if (!st.isVirtual) { + // Resizes on real events update immediately applyRangeDuringDrag( { id: st.id, isVirtual: st.isVirtual, mode: st.mode, startDate: ns, endDate: ne }, ns, diff --git a/src/stores/CalendarStore.js b/src/stores/CalendarStore.js index 1884a46..5c59f0f 100644 --- a/src/stores/CalendarStore.js +++ b/src/stores/CalendarStore.js @@ -37,6 +37,10 @@ export const useCalendarStore = defineStore('calendar', { }, }), actions: { + _rotateWeekdayPattern(pattern, shift) { + const k = (7 - (shift % 7)) % 7 + return pattern.slice(k).concat(pattern.slice(0, k)) + }, _resolveCountry(code) { if (!code || code !== 'auto') return code const locale = navigator.language || navigator.languages?.[0] @@ -264,7 +268,7 @@ export const useCalendarStore = defineStore('calendar', { this.notifyEventsChanged() }, - setEventRange(eventId, newStartStr, newEndStr, { mode = 'auto' } = {}) { + setEventRange(eventId, newStartStr, newEndStr, { mode = 'auto', rotatePattern = true } = {}) { const snapshot = this.events.get(eventId) if (!snapshot) return const prevStart = fromLocalString(snapshot.startDate, DEFAULT_TZ) @@ -283,6 +287,7 @@ export const useCalendarStore = defineStore('calendar', { ) if ( mode === 'move' && + rotatePattern && snapshot.isRepeating && snapshot.repeat === 'weeks' && Array.isArray(snapshot.repeatWeekdays) @@ -291,15 +296,7 @@ export const useCalendarStore = defineStore('calendar', { const newDow = newStart.getDay() const shift = newDow - oldDow if (shift !== 0) { - const rotated = [false, false, false, false, false, false, false] - for (let i = 0; i < 7; i++) { - if (snapshot.repeatWeekdays[i]) { - let ni = (i + shift) % 7 - if (ni < 0) ni += 7 - rotated[ni] = true - } - } - snapshot.repeatWeekdays = rotated + snapshot.repeatWeekdays = this._rotateWeekdayPattern(snapshot.repeatWeekdays, shift) } } this.events.set(eventId, { ...snapshot, isSpanning: snapshot.startDate < snapshot.endDate }) @@ -314,7 +311,7 @@ export const useCalendarStore = defineStore('calendar', { const baseStart = fromLocalString(base.startDate, DEFAULT_TZ) if (occurrenceDate <= baseStart) { this.setEventRange(baseId, newStartStr, newEndStr, { mode: 'move' }) - return + return baseId } let keptOccurrences = 0 if (base.repeat === 'weeks') { @@ -359,18 +356,10 @@ export const useCalendarStore = defineStore('calendar', { const newWeekday = fromLocalString(newStartStr, DEFAULT_TZ).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 + repeatWeekdays = this._rotateWeekdayPattern(base.repeatWeekdays, shift) } } - this.createEvent({ + const newId = this.createEvent({ title: base.title, startDate: newStartStr, endDate: newEndStr, @@ -381,6 +370,7 @@ export const useCalendarStore = defineStore('calendar', { repeatWeekdays, }) this.notifyEventsChanged() + return newId }, splitRepeatSeries(baseId, occurrenceIndex, newStartStr, newEndStr) {