picocrank 1.6.4 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/vue/components/Calendar.vue +124 -53
- package/vue/views/CalendarExample.vue +95 -32
package/package.json
CHANGED
|
@@ -57,6 +57,21 @@ const currentDate = computed(() => {
|
|
|
57
57
|
const currentMonth = computed(() => currentDate.value.getMonth())
|
|
58
58
|
const currentYear = computed(() => currentDate.value.getFullYear())
|
|
59
59
|
|
|
60
|
+
// Compute the first visible day (Monday) for the current month view
|
|
61
|
+
function getStartOfGrid(date: Date): Date {
|
|
62
|
+
const firstOfMonth = new Date(date.getFullYear(), date.getMonth(), 1)
|
|
63
|
+
const dayOfWeek = (firstOfMonth.getDay() + 6) % 7 // Monday=0
|
|
64
|
+
const start = new Date(firstOfMonth)
|
|
65
|
+
start.setDate(firstOfMonth.getDate() - dayOfWeek)
|
|
66
|
+
start.setHours(0, 0, 0, 0)
|
|
67
|
+
return start
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Anchor date for the 6x7 grid; we will shift this by weeks without re-rendering the grid structure
|
|
71
|
+
const gridStartDate = ref<Date>(getStartOfGrid(currentDate.value))
|
|
72
|
+
// Track if we're actively scrolling to prevent watcher from resetting grid anchor
|
|
73
|
+
const isScrolling = ref(false)
|
|
74
|
+
|
|
60
75
|
// Helper function to check if a time is midnight (00:00)
|
|
61
76
|
function isMidnight(dateValue: any): boolean {
|
|
62
77
|
if (!dateValue) return false
|
|
@@ -72,6 +87,15 @@ function formatTimeOrNoTime(dateValue: any): string {
|
|
|
72
87
|
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
|
73
88
|
}
|
|
74
89
|
|
|
90
|
+
// Determine if a date is in the past (before today)
|
|
91
|
+
function isPastDay(date: Date): boolean {
|
|
92
|
+
const day = new Date(date)
|
|
93
|
+
day.setHours(0, 0, 0, 0)
|
|
94
|
+
const today = new Date()
|
|
95
|
+
today.setHours(0, 0, 0, 0)
|
|
96
|
+
return day.getTime() < today.getTime()
|
|
97
|
+
}
|
|
98
|
+
|
|
75
99
|
// Get event date range
|
|
76
100
|
function getEventDateRange(event: CalendarEvent): { start: Date | null; end: Date | null } {
|
|
77
101
|
if (props.getEventDateRange) {
|
|
@@ -179,55 +203,18 @@ function formatEventTimeDefault(event: CalendarEvent, date: Date): string {
|
|
|
179
203
|
return 'All day'
|
|
180
204
|
}
|
|
181
205
|
|
|
182
|
-
// Calendar generation
|
|
206
|
+
// Calendar generation from persistent grid start
|
|
183
207
|
const calendarDays = computed(() => {
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const daysInMonth = lastDay.getDate()
|
|
190
|
-
|
|
191
|
-
// Get starting day of week (Monday as first day)
|
|
192
|
-
const startDay = (firstDay.getDay() + 6) % 7
|
|
193
|
-
|
|
194
|
-
const days = []
|
|
195
|
-
|
|
196
|
-
// Add days from previous month to fill the first row
|
|
197
|
-
const prevMonth = month === 0 ? 11 : month - 1
|
|
198
|
-
const prevYear = month === 0 ? year - 1 : year
|
|
199
|
-
const prevMonthLastDay = new Date(prevYear, prevMonth + 1, 0).getDate()
|
|
200
|
-
|
|
201
|
-
for (let i = 0; i < startDay; i++) {
|
|
202
|
-
const day = prevMonthLastDay - startDay + i + 1
|
|
203
|
-
const date = new Date(prevYear, prevMonth, day)
|
|
204
|
-
days.push({
|
|
205
|
-
date,
|
|
206
|
-
events: getEventsForDate(date)
|
|
207
|
-
})
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Add days of the month
|
|
211
|
-
for (let day = 1; day <= daysInMonth; day++) {
|
|
212
|
-
const date = new Date(year, month, day)
|
|
213
|
-
days.push({
|
|
214
|
-
date,
|
|
215
|
-
events: getEventsForDate(date)
|
|
216
|
-
})
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Add days from next month to complete the last row
|
|
220
|
-
const totalCells = 42
|
|
221
|
-
const remainingCells = totalCells - days.length
|
|
222
|
-
|
|
223
|
-
for (let day = 1; day <= remainingCells; day++) {
|
|
224
|
-
const date = new Date(year, month + 1, day)
|
|
208
|
+
const days = [] as { date: Date; events: CalendarEvent[] }[]
|
|
209
|
+
const start = gridStartDate.value
|
|
210
|
+
for (let i = 0; i < 42; i++) {
|
|
211
|
+
const date = new Date(start)
|
|
212
|
+
date.setDate(start.getDate() + i)
|
|
225
213
|
days.push({
|
|
226
214
|
date,
|
|
227
215
|
events: getEventsForDate(date)
|
|
228
216
|
})
|
|
229
217
|
}
|
|
230
|
-
|
|
231
218
|
return days
|
|
232
219
|
})
|
|
233
220
|
|
|
@@ -237,8 +224,11 @@ function previousMonth() {
|
|
|
237
224
|
const newMonth = currentMonth.value === 0 ? 11 : currentMonth.value - 1
|
|
238
225
|
const newYear = currentMonth.value === 0 ? currentYear.value - 1 : currentYear.value
|
|
239
226
|
emit('month-change', newMonth, newYear)
|
|
227
|
+
// Optimistically update grid anchor for smooth transition
|
|
228
|
+
gridStartDate.value = getStartOfGrid(new Date(newYear, newMonth, 1))
|
|
240
229
|
} else {
|
|
241
230
|
internalCurrentDate.value = new Date(currentYear.value, currentMonth.value - 1, 1)
|
|
231
|
+
gridStartDate.value = getStartOfGrid(internalCurrentDate.value)
|
|
242
232
|
}
|
|
243
233
|
}
|
|
244
234
|
|
|
@@ -247,8 +237,10 @@ function nextMonth() {
|
|
|
247
237
|
const newMonth = currentMonth.value === 11 ? 0 : currentMonth.value + 1
|
|
248
238
|
const newYear = currentMonth.value === 11 ? currentYear.value + 1 : currentYear.value
|
|
249
239
|
emit('month-change', newMonth, newYear)
|
|
240
|
+
gridStartDate.value = getStartOfGrid(new Date(newYear, newMonth, 1))
|
|
250
241
|
} else {
|
|
251
242
|
internalCurrentDate.value = new Date(currentYear.value, currentMonth.value + 1, 1)
|
|
243
|
+
gridStartDate.value = getStartOfGrid(internalCurrentDate.value)
|
|
252
244
|
}
|
|
253
245
|
}
|
|
254
246
|
|
|
@@ -256,11 +248,53 @@ function goToToday() {
|
|
|
256
248
|
if (props.currentMonth !== undefined && props.currentYear !== undefined) {
|
|
257
249
|
const today = new Date()
|
|
258
250
|
emit('month-change', today.getMonth(), today.getFullYear())
|
|
251
|
+
gridStartDate.value = getStartOfGrid(today)
|
|
259
252
|
} else {
|
|
260
253
|
internalCurrentDate.value = new Date()
|
|
254
|
+
gridStartDate.value = getStartOfGrid(internalCurrentDate.value)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Week navigation (mouse wheel)
|
|
259
|
+
const lastWheelAt = ref(0)
|
|
260
|
+
const wheelThrottleMs = 180
|
|
261
|
+
function getVisibleMonthYear(start: Date): { month: number; year: number } {
|
|
262
|
+
const mid = new Date(start)
|
|
263
|
+
mid.setDate(start.getDate() + 21) // middle of 6x7 grid
|
|
264
|
+
return { month: mid.getMonth(), year: mid.getFullYear() }
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function adjustByWeeks(weeks: number): void {
|
|
268
|
+
const newStart = new Date(gridStartDate.value)
|
|
269
|
+
newStart.setDate(newStart.getDate() + weeks * 7)
|
|
270
|
+
gridStartDate.value = newStart
|
|
271
|
+
|
|
272
|
+
if (props.currentMonth !== undefined && props.currentYear !== undefined) {
|
|
273
|
+
const { month, year } = getVisibleMonthYear(newStart)
|
|
274
|
+
if (month !== currentMonth.value || year !== currentYear.value) {
|
|
275
|
+
emit('month-change', month, year)
|
|
276
|
+
// Set flag to prevent watcher from resetting our scroll position
|
|
277
|
+
isScrolling.value = true
|
|
278
|
+
// Clear the flag after a short delay to allow future prop-driven updates
|
|
279
|
+
setTimeout(() => { isScrolling.value = false }, 100)
|
|
280
|
+
}
|
|
281
|
+
} else {
|
|
282
|
+
// Keep internal anchor and current date roughly aligned (set to first of visible month)
|
|
283
|
+
const { month, year } = getVisibleMonthYear(newStart)
|
|
284
|
+
internalCurrentDate.value = new Date(year, month, 1)
|
|
261
285
|
}
|
|
262
286
|
}
|
|
263
287
|
|
|
288
|
+
function handleWheel(event: WheelEvent): void {
|
|
289
|
+
// Prevent page scrolling while using calendar scroll
|
|
290
|
+
event.preventDefault()
|
|
291
|
+
const now = (typeof performance !== 'undefined' && performance.now) ? performance.now() : Date.now()
|
|
292
|
+
if (now - lastWheelAt.value < wheelThrottleMs) return
|
|
293
|
+
lastWheelAt.value = now
|
|
294
|
+
const direction = event.deltaY > 0 ? 1 : -1
|
|
295
|
+
adjustByWeeks(direction)
|
|
296
|
+
}
|
|
297
|
+
|
|
264
298
|
// Event handlers
|
|
265
299
|
function handleEventClick(event: CalendarEvent) {
|
|
266
300
|
emit('event-click', event)
|
|
@@ -275,6 +309,15 @@ function handleContextMenu(event: CalendarEvent, mouseEvent: MouseEvent) {
|
|
|
275
309
|
mouseEvent.stopPropagation()
|
|
276
310
|
emit('event-context-menu', event, mouseEvent)
|
|
277
311
|
}
|
|
312
|
+
import { watch } from 'vue'
|
|
313
|
+
|
|
314
|
+
// Sync grid start when controlled month/year props change (but not during scrolling)
|
|
315
|
+
watch([currentMonth, currentYear], () => {
|
|
316
|
+
if (isScrolling.value) return
|
|
317
|
+
const anchor = new Date(currentYear.value, currentMonth.value, 1)
|
|
318
|
+
gridStartDate.value = getStartOfGrid(anchor)
|
|
319
|
+
})
|
|
320
|
+
|
|
278
321
|
</script>
|
|
279
322
|
|
|
280
323
|
<template>
|
|
@@ -290,10 +333,10 @@ function handleContextMenu(event: CalendarEvent, mouseEvent: MouseEvent) {
|
|
|
290
333
|
</div>
|
|
291
334
|
</div>
|
|
292
335
|
|
|
293
|
-
<div
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
<div class="calendar-grid">
|
|
336
|
+
<div class="calendar-container">
|
|
337
|
+
<div v-if="error" class="calendar-error">{{ error }}</div>
|
|
338
|
+
<div v-if="loading" class="calendar-loading-overlay">Loading…</div>
|
|
339
|
+
<div class="calendar-grid" @wheel.prevent="handleWheel">
|
|
297
340
|
<!-- Day headers -->
|
|
298
341
|
<div v-for="day in dayNames" :key="day" class="day-header">{{ day }}</div>
|
|
299
342
|
|
|
@@ -306,7 +349,8 @@ function handleContextMenu(event: CalendarEvent, mouseEvent: MouseEvent) {
|
|
|
306
349
|
'today': day && day.date.toDateString() === new Date().toDateString(),
|
|
307
350
|
'weekend': day && (day.date.getDay() === 0 || day.date.getDay() === 6),
|
|
308
351
|
'prev-month': day && day.date.getMonth() !== currentMonth && day.date.getMonth() !== (currentMonth + 1) % 12,
|
|
309
|
-
'next-month': day && day.date.getMonth() !== currentMonth && day.date.getMonth() === (currentMonth + 1) % 12
|
|
352
|
+
'next-month': day && day.date.getMonth() !== currentMonth && day.date.getMonth() === (currentMonth + 1) % 12,
|
|
353
|
+
'past': day && isPastDay(day.date)
|
|
310
354
|
}"
|
|
311
355
|
>
|
|
312
356
|
<div v-if="day" class="day-content" @click="handleDateClick(day.date)">
|
|
@@ -397,11 +441,23 @@ function handleContextMenu(event: CalendarEvent, mouseEvent: MouseEvent) {
|
|
|
397
441
|
text-align: center;
|
|
398
442
|
}
|
|
399
443
|
|
|
444
|
+
.calendar-loading-overlay {
|
|
445
|
+
position: absolute;
|
|
446
|
+
inset: 0;
|
|
447
|
+
display: flex;
|
|
448
|
+
align-items: center;
|
|
449
|
+
justify-content: center;
|
|
450
|
+
background: rgba(255, 255, 255, 0.6);
|
|
451
|
+
z-index: 1;
|
|
452
|
+
font-weight: 600;
|
|
453
|
+
}
|
|
454
|
+
|
|
400
455
|
.calendar-container {
|
|
401
456
|
background: white;
|
|
402
457
|
border: 1px solid #e0e0e0;
|
|
403
458
|
overflow: hidden;
|
|
404
459
|
border-radius: 8px;
|
|
460
|
+
position: relative;
|
|
405
461
|
}
|
|
406
462
|
|
|
407
463
|
.calendar-grid {
|
|
@@ -427,10 +483,11 @@ function handleContextMenu(event: CalendarEvent, mouseEvent: MouseEvent) {
|
|
|
427
483
|
.calendar-day {
|
|
428
484
|
border-right: 1px solid #e0e0e0;
|
|
429
485
|
border-bottom: 1px solid #e0e0e0;
|
|
430
|
-
|
|
486
|
+
height: 160px;
|
|
431
487
|
position: relative;
|
|
432
488
|
transition: background-color 0.2s ease;
|
|
433
489
|
cursor: pointer;
|
|
490
|
+
overflow: hidden;
|
|
434
491
|
}
|
|
435
492
|
|
|
436
493
|
.calendar-day:hover {
|
|
@@ -452,12 +509,18 @@ function handleContextMenu(event: CalendarEvent, mouseEvent: MouseEvent) {
|
|
|
452
509
|
.calendar-day.next-month,
|
|
453
510
|
.calendar-day.prev-month {
|
|
454
511
|
background: #f8f9fa;
|
|
455
|
-
opacity: 0.6;
|
|
456
512
|
}
|
|
457
513
|
|
|
458
514
|
.calendar-day.next-month:hover,
|
|
459
515
|
.calendar-day.prev-month:hover {
|
|
460
516
|
background: #e9ecef;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.calendar-day.past {
|
|
520
|
+
opacity: 0.6;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.calendar-day.past:hover {
|
|
461
524
|
opacity: 0.8;
|
|
462
525
|
}
|
|
463
526
|
|
|
@@ -503,7 +566,7 @@ function handleContextMenu(event: CalendarEvent, mouseEvent: MouseEvent) {
|
|
|
503
566
|
|
|
504
567
|
.day-events {
|
|
505
568
|
flex: 1;
|
|
506
|
-
overflow:
|
|
569
|
+
overflow: auto;
|
|
507
570
|
}
|
|
508
571
|
|
|
509
572
|
.calendar-event {
|
|
@@ -587,7 +650,7 @@ function handleContextMenu(event: CalendarEvent, mouseEvent: MouseEvent) {
|
|
|
587
650
|
}
|
|
588
651
|
|
|
589
652
|
.calendar-day {
|
|
590
|
-
|
|
653
|
+
height: 80px;
|
|
591
654
|
}
|
|
592
655
|
|
|
593
656
|
.day-number {
|
|
@@ -609,7 +672,7 @@ function handleContextMenu(event: CalendarEvent, mouseEvent: MouseEvent) {
|
|
|
609
672
|
}
|
|
610
673
|
|
|
611
674
|
.calendar-day {
|
|
612
|
-
|
|
675
|
+
height: 60px;
|
|
613
676
|
}
|
|
614
677
|
|
|
615
678
|
.day-header {
|
|
@@ -692,5 +755,13 @@ function handleContextMenu(event: CalendarEvent, mouseEvent: MouseEvent) {
|
|
|
692
755
|
.calendar-day.weekend:hover {
|
|
693
756
|
background: #374151;
|
|
694
757
|
}
|
|
758
|
+
|
|
759
|
+
.calendar-day.past {
|
|
760
|
+
opacity: 0.6;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
.calendar-day.past:hover {
|
|
764
|
+
opacity: 0.8;
|
|
765
|
+
}
|
|
695
766
|
}
|
|
696
767
|
</style>
|
|
@@ -44,37 +44,48 @@ const currentMonth = ref('?')
|
|
|
44
44
|
const currentMonthIndex = ref(new Date().getMonth())
|
|
45
45
|
const currentYear = ref(new Date().getFullYear())
|
|
46
46
|
|
|
47
|
+
// Cache events by month/year key (e.g., "2024-10")
|
|
48
|
+
const eventsCache = new Map()
|
|
49
|
+
|
|
47
50
|
// Date picker state - using native HTML5 month input
|
|
48
51
|
const selectedDate = ref('')
|
|
49
52
|
|
|
50
|
-
//
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
// Simple seeded random number generator for consistent event generation
|
|
54
|
+
function seededRandom(seed) {
|
|
55
|
+
let value = seed
|
|
56
|
+
return function() {
|
|
57
|
+
value = (value * 9301 + 49297) % 233280
|
|
58
|
+
return value / 233280
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Generate dummy events for a specific month
|
|
63
|
+
function generateDummyEventsForMonth(monthIndex, year) {
|
|
64
|
+
// Get the number of days in the month
|
|
65
|
+
const daysInMonth = new Date(year, monthIndex + 1, 0).getDate()
|
|
58
66
|
|
|
59
67
|
const dummyEvents = []
|
|
60
68
|
|
|
69
|
+
// Create a seeded random generator for consistent events per month
|
|
70
|
+
const random = seededRandom(year * 12 + monthIndex)
|
|
71
|
+
|
|
61
72
|
// Add some recurring events
|
|
62
73
|
for (let day = 1; day <= daysInMonth; day++) {
|
|
63
|
-
const date = new Date(
|
|
74
|
+
const date = new Date(year, monthIndex, day)
|
|
64
75
|
const dayOfWeek = date.getDay()
|
|
65
76
|
|
|
66
77
|
// Add weekend events (Saturday and Sunday)
|
|
67
78
|
if (dayOfWeek === 0 || dayOfWeek === 6) {
|
|
68
79
|
dummyEvents.push({
|
|
69
|
-
id: `weekend-${day}`,
|
|
80
|
+
id: `weekend-${year}-${monthIndex}-${day}`,
|
|
70
81
|
title: 'Weekend Activity',
|
|
71
|
-
date: new Date(
|
|
82
|
+
date: new Date(year, monthIndex, day, 10, 0),
|
|
72
83
|
description: 'Relaxing weekend activity'
|
|
73
84
|
})
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
// Add some random events
|
|
77
|
-
if (
|
|
88
|
+
if (random() > 0.7) {
|
|
78
89
|
const eventTypes = [
|
|
79
90
|
{ title: 'Team Meeting', time: [9, 0], duration: 1 },
|
|
80
91
|
{ title: 'Client Call', time: [14, 30], duration: 0.5 },
|
|
@@ -83,13 +94,13 @@ function generateDummyEvents() {
|
|
|
83
94
|
{ title: 'Code Review', time: [10, 0], duration: 1.5 }
|
|
84
95
|
]
|
|
85
96
|
|
|
86
|
-
const eventType = eventTypes[Math.floor(
|
|
97
|
+
const eventType = eventTypes[Math.floor(random() * eventTypes.length)]
|
|
87
98
|
const [hours, minutes] = eventType.time
|
|
88
99
|
|
|
89
100
|
dummyEvents.push({
|
|
90
|
-
id: `event-${day}-${Math.random().toString(36).substr(2, 9)}`,
|
|
101
|
+
id: `event-${year}-${monthIndex}-${day}-${Math.random().toString(36).substr(2, 9)}`,
|
|
91
102
|
title: eventType.title,
|
|
92
|
-
date: new Date(
|
|
103
|
+
date: new Date(year, monthIndex, day, hours, minutes),
|
|
93
104
|
description: `Scheduled ${eventType.title.toLowerCase()}`
|
|
94
105
|
})
|
|
95
106
|
}
|
|
@@ -97,34 +108,34 @@ function generateDummyEvents() {
|
|
|
97
108
|
|
|
98
109
|
// Add some multi-day events
|
|
99
110
|
dummyEvents.push({
|
|
100
|
-
id:
|
|
111
|
+
id: `conference-${year}-${monthIndex}`,
|
|
101
112
|
title: 'Tech Conference',
|
|
102
|
-
startDate: new Date(
|
|
103
|
-
endDate: new Date(
|
|
113
|
+
startDate: new Date(year, monthIndex, 5, 9, 0),
|
|
114
|
+
endDate: new Date(year, monthIndex, 7, 17, 0),
|
|
104
115
|
description: 'Annual technology conference'
|
|
105
116
|
})
|
|
106
117
|
|
|
107
118
|
dummyEvents.push({
|
|
108
|
-
id:
|
|
119
|
+
id: `vacation-${year}-${monthIndex}`,
|
|
109
120
|
title: 'Vacation',
|
|
110
|
-
startDate: new Date(
|
|
111
|
-
endDate: new Date(
|
|
121
|
+
startDate: new Date(year, monthIndex, 15, 0, 0),
|
|
122
|
+
endDate: new Date(year, monthIndex, 18, 23, 59),
|
|
112
123
|
description: 'Family vacation time'
|
|
113
124
|
})
|
|
114
125
|
|
|
115
126
|
// Add some all-day events
|
|
116
127
|
dummyEvents.push({
|
|
117
|
-
id:
|
|
128
|
+
id: `holiday-${year}-${monthIndex}`,
|
|
118
129
|
title: 'Public Holiday',
|
|
119
|
-
date: new Date(
|
|
130
|
+
date: new Date(year, monthIndex, 1, 0, 0),
|
|
120
131
|
description: 'National holiday - office closed'
|
|
121
132
|
})
|
|
122
133
|
|
|
123
134
|
if (daysInMonth >= 25) {
|
|
124
135
|
dummyEvents.push({
|
|
125
|
-
id:
|
|
136
|
+
id: `deadline-${year}-${monthIndex}`,
|
|
126
137
|
title: 'Project Deadline',
|
|
127
|
-
date: new Date(
|
|
138
|
+
date: new Date(year, monthIndex, 25, 0, 0),
|
|
128
139
|
description: 'Important project deadline'
|
|
129
140
|
})
|
|
130
141
|
}
|
|
@@ -132,9 +143,47 @@ function generateDummyEvents() {
|
|
|
132
143
|
return dummyEvents
|
|
133
144
|
}
|
|
134
145
|
|
|
146
|
+
// Get or generate events for a specific month/year
|
|
147
|
+
function getEventsForMonth(monthIndex, year) {
|
|
148
|
+
const cacheKey = `${year}-${monthIndex}`
|
|
149
|
+
|
|
150
|
+
// Return cached events if available
|
|
151
|
+
if (eventsCache.has(cacheKey)) {
|
|
152
|
+
return eventsCache.get(cacheKey)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Generate new events for this month
|
|
156
|
+
const dummyEvents = generateDummyEventsForMonth(monthIndex, year)
|
|
157
|
+
|
|
158
|
+
// Cache the events
|
|
159
|
+
eventsCache.set(cacheKey, dummyEvents)
|
|
160
|
+
|
|
161
|
+
return dummyEvents
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Merge events for previous, current, and next months so overflow days show events
|
|
165
|
+
function getEventsForSurroundingMonths(monthIndex, year) {
|
|
166
|
+
const prevMonthIndex = monthIndex === 0 ? 11 : monthIndex - 1
|
|
167
|
+
const prevYear = monthIndex === 0 ? year - 1 : year
|
|
168
|
+
const nextMonthIndex = monthIndex === 11 ? 0 : monthIndex + 1
|
|
169
|
+
const nextYear = monthIndex === 11 ? year + 1 : year
|
|
170
|
+
|
|
171
|
+
return [
|
|
172
|
+
...getEventsForMonth(prevMonthIndex, prevYear),
|
|
173
|
+
...getEventsForMonth(monthIndex, year),
|
|
174
|
+
...getEventsForMonth(nextMonthIndex, nextYear)
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Generate dummy events for the current month (backward compatibility wrapper)
|
|
179
|
+
function generateDummyEvents() {
|
|
180
|
+
const now = new Date()
|
|
181
|
+
return getEventsForMonth(now.getMonth(), now.getFullYear())
|
|
182
|
+
}
|
|
183
|
+
|
|
135
184
|
function handleEventClick(event) {
|
|
136
|
-
|
|
137
|
-
|
|
185
|
+
console.log('Event clicked:', event)
|
|
186
|
+
alert(`Event: ${event.title}\nDescription: ${event.description || 'No description'}`)
|
|
138
187
|
}
|
|
139
188
|
|
|
140
189
|
function handleDateClick(date) {
|
|
@@ -166,6 +215,9 @@ function previousMonth() {
|
|
|
166
215
|
|
|
167
216
|
// Update date picker
|
|
168
217
|
updateDatePicker(newMonth, newYear)
|
|
218
|
+
|
|
219
|
+
// Update events for surrounding months
|
|
220
|
+
events.value = getEventsForSurroundingMonths(newMonth, newYear)
|
|
169
221
|
}
|
|
170
222
|
|
|
171
223
|
function nextMonth() {
|
|
@@ -185,6 +237,9 @@ function nextMonth() {
|
|
|
185
237
|
|
|
186
238
|
// Update date picker
|
|
187
239
|
updateDatePicker(newMonth, newYear)
|
|
240
|
+
|
|
241
|
+
// Update events for surrounding months
|
|
242
|
+
events.value = getEventsForSurroundingMonths(newMonth, newYear)
|
|
188
243
|
}
|
|
189
244
|
|
|
190
245
|
function goToToday() {
|
|
@@ -204,6 +259,9 @@ function goToToday() {
|
|
|
204
259
|
|
|
205
260
|
// Update date picker
|
|
206
261
|
updateDatePicker(newMonth, newYear)
|
|
262
|
+
|
|
263
|
+
// Update events for surrounding months
|
|
264
|
+
events.value = getEventsForSurroundingMonths(newMonth, newYear)
|
|
207
265
|
}
|
|
208
266
|
|
|
209
267
|
function goToSelectedDate() {
|
|
@@ -223,6 +281,9 @@ function goToSelectedDate() {
|
|
|
223
281
|
month: 'long',
|
|
224
282
|
year: 'numeric'
|
|
225
283
|
})
|
|
284
|
+
|
|
285
|
+
// Update events for surrounding months
|
|
286
|
+
events.value = getEventsForSurroundingMonths(monthIndex, year)
|
|
226
287
|
}
|
|
227
288
|
|
|
228
289
|
function handleMonthChange(month, year) {
|
|
@@ -245,18 +306,20 @@ function handleMonthChange(month, year) {
|
|
|
245
306
|
updateDatePicker(month, year)
|
|
246
307
|
|
|
247
308
|
setTimeout(() => {
|
|
248
|
-
//
|
|
249
|
-
events.value =
|
|
309
|
+
// Load events for surrounding months (will use cache if available)
|
|
310
|
+
events.value = getEventsForSurroundingMonths(month, year)
|
|
250
311
|
loading.value = false
|
|
251
312
|
}, 500)
|
|
252
313
|
}
|
|
253
314
|
|
|
254
315
|
onMounted(() => {
|
|
255
|
-
// Generate initial events for current
|
|
256
|
-
|
|
316
|
+
// Generate initial events for current, previous, and next months
|
|
317
|
+
const now = new Date()
|
|
318
|
+
const initMonth = now.getMonth()
|
|
319
|
+
const initYear = now.getFullYear()
|
|
320
|
+
events.value = getEventsForSurroundingMonths(initMonth, initYear)
|
|
257
321
|
|
|
258
322
|
// Set initial month title using native JavaScript
|
|
259
|
-
const now = new Date()
|
|
260
323
|
currentMonthIndex.value = now.getMonth()
|
|
261
324
|
currentYear.value = now.getFullYear()
|
|
262
325
|
currentMonth.value = now.toLocaleDateString('en-US', {
|