Release Notes Architecture - Component refactor: removed monoliths (`Calendar.vue`, `CalendarGrid.vue`); expanded granular view + header/control components. - Dialog system introduced (`BaseDialog`, `SettingsDialog`). State & Data - Store redesigned: Map-based events + recurrence map; mutation counters. - Local persistence + undo/redo history (custom plugins). Date & Holidays - Migrated all date logic to `date-fns` (+ tz). - Added national holiday support (toggle + loading utilities). Recurrence & Events - Consolidated recurrence handling; fixes for monthly edge days (29–31), annual, multi‑day, and complex weekly repeats. - Reliable splitting/moving/resizing/deletion of repeating and multi‑day events. Interaction & UX - Double‑tap to create events; improved drag (multi‑day + position retention). - Scroll & inertial/momentum navigation; year change via numeric scroller. - Movable event dialog; live settings application. Performance - Progressive / virtual week rendering, reduced off‑screen buffer. - Targeted repaint strategy; minimized full re-renders. Plugins Added - History, undo normalization, persistence, scroll manager, virtual weeks. Styling & Layout - Responsive + compact layout refinements; header restructured. - Simplified visual elements (removed dots/overflow text); holiday styling adjustments. Reliability / Fixes - Numerous recurrence, deletion, orientation/rotation, and event indexing corrections. - Cross-browser fallback (Firefox week info). Dependencies Added - date-fns, date-fns-tz, date-holidays, pinia-plugin-persistedstate. Net Change - 28 files modified; ~4.4K insertions / ~2.2K deletions (major refactor + feature set).
95 lines
2.1 KiB
Vue
95 lines
2.1 KiB
Vue
<script setup>
|
|
import CalendarDay from './CalendarDay.vue'
|
|
import EventOverlay from './EventOverlay.vue'
|
|
|
|
const props = defineProps({ week: Object, dragging: { type: Boolean, default: false } })
|
|
|
|
const emit = defineEmits([
|
|
'day-mousedown',
|
|
'day-mouseenter',
|
|
'day-mouseup',
|
|
'day-touchstart',
|
|
'event-click',
|
|
])
|
|
|
|
const handleDayMouseDown = (dateStr) => {
|
|
emit('day-mousedown', dateStr)
|
|
}
|
|
|
|
const handleDayMouseEnter = (dateStr) => {
|
|
emit('day-mouseenter', dateStr)
|
|
}
|
|
|
|
const handleDayMouseUp = (dateStr) => {
|
|
emit('day-mouseup', dateStr)
|
|
}
|
|
|
|
const handleDayTouchStart = (dateStr) => {
|
|
emit('day-touchstart', dateStr)
|
|
}
|
|
|
|
// touchmove & touchend handled globally in CalendarView
|
|
|
|
const handleEventClick = (payload) => {
|
|
emit('event-click', payload)
|
|
}
|
|
|
|
// Only apply upside-down rotation (bottomup) for Latin script month labels
|
|
function shouldRotateMonth(label) {
|
|
if (!label) return false
|
|
try {
|
|
return /\p{Script=Latin}/u.test(label)
|
|
} catch (e) {
|
|
return /[A-Za-z\u00C0-\u024F\u1E00-\u1EFF]/u.test(label)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="week-row" :style="{ top: `${props.week.top}px` }">
|
|
<div class="week-label">W{{ props.week.weekNumber }}</div>
|
|
<div class="days-grid">
|
|
<CalendarDay
|
|
v-for="day in props.week.days"
|
|
:key="day.date"
|
|
:day="day"
|
|
:dragging="props.dragging"
|
|
@mousedown="handleDayMouseDown(day.date)"
|
|
@mouseenter="handleDayMouseEnter(day.date)"
|
|
@mouseup="handleDayMouseUp(day.date)"
|
|
@touchstart="handleDayTouchStart(day.date)"
|
|
/>
|
|
<EventOverlay :week="props.week" @event-click="handleEventClick" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.week-row {
|
|
display: grid;
|
|
grid-template-columns: var(--week-w) repeat(7, 1fr);
|
|
position: absolute;
|
|
height: var(--row-h);
|
|
width: 100%;
|
|
}
|
|
|
|
.week-label {
|
|
display: grid;
|
|
place-items: center;
|
|
width: 100%;
|
|
color: var(--muted);
|
|
font-size: 1.2em;
|
|
font-weight: 500;
|
|
user-select: none;
|
|
height: var(--row-h);
|
|
}
|
|
|
|
.days-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
position: relative;
|
|
height: 100%;
|
|
width: 100%;
|
|
}
|
|
</style>
|