Major new version (#2)

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).
This commit is contained in:
LeoVasanko committed 2025-08-26 03:58:24 +00:00
1 parent 86a06acccb
commit dbe4c99b07
28 files changed
+4465 -2207

No files matched your search

+179
View File
@@ -0,0 +1,179 @@
// holidays.js — Holiday utilities using date-holidays package
import Holidays from 'date-holidays'
let holidaysInstance = null
let currentCountry = null
let currentState = null
let currentRegion = null
let holidayCache = new Map()
let yearCache = new Map()
/**
* Initialize holidays for a specific country/region
* @param {string} country - Country code (e.g., 'US', 'GB', 'DE')
* @param {string} [state] - State/province code (e.g., 'CA' for California)
* @param {string} [region] - Region code
*/
export function initializeHolidays(country, state = null, region = null) {
if (!country) {
console.warn('No country provided for holiday initialization')
holidaysInstance = null
return false
}
try {
holidaysInstance = new Holidays(country, state, region)
currentCountry = country
currentState = state
currentRegion = region
holidayCache.clear()
yearCache.clear()
return true
} catch (error) {
console.warn('Failed to initialize holidays for', country, state, region, error)
holidaysInstance = null
return false
}
}
/**
* Get holidays for a specific year
* @param {number} year - The year to get holidays for
* @returns {Array} Array of holiday objects
*/
export function getHolidaysForYear(year) {
if (!holidaysInstance) {
return []
}
if (yearCache.has(year)) {
return yearCache.get(year)
}
try {
const holidays = holidaysInstance.getHolidays(year)
yearCache.set(year, holidays)
return holidays
} catch (error) {
console.warn('Failed to get holidays for year', year, error)
return []
}
}
/**
* Get holiday for a specific date
* @param {string|Date} date - Date in YYYY-MM-DD format or Date object
* @returns {Object|null} Holiday object or null if no holiday
*/
export function getHolidayForDate(date) {
if (!holidaysInstance) {
return null
}
const cacheKey = typeof date === 'string' ? date : date.toISOString().split('T')[0]
if (holidayCache.has(cacheKey)) {
return holidayCache.get(cacheKey)
}
try {
let dateObj
if (typeof date === 'string') {
const [year, month, day] = date.split('-').map(Number)
dateObj = new Date(year, month - 1, day)
} else {
dateObj = date
}
const year = dateObj.getFullYear()
const holidays = getHolidaysForYear(year)
const holiday = holidays.find((h) => {
const holidayDate = new Date(h.date)
return (
holidayDate.getFullYear() === dateObj.getFullYear() &&
holidayDate.getMonth() === dateObj.getMonth() &&
holidayDate.getDate() === dateObj.getDate()
)
})
const result = holiday || null
holidayCache.set(cacheKey, result)
return result
} catch (error) {
console.warn('Failed to get holiday for date', date, error)
return null
}
}
/**
* Check if a date is a holiday
* @param {string|Date} date - Date in YYYY-MM-DD format or Date object
* @returns {boolean} True if the date is a holiday
*/
export function isHoliday(date) {
return getHolidayForDate(date) !== null
}
/**
* Get available countries for holidays
* @returns {Array} Array of country codes
*/
export function getAvailableCountries() {
try {
const holidays = new Holidays()
const countries = holidays.getCountries()
// The getCountries method might return an object, convert to array of keys
if (countries && typeof countries === 'object') {
return Array.isArray(countries) ? countries : Object.keys(countries)
}
return ['US', 'GB', 'DE', 'FR', 'CA', 'AU'] // Fallback
} catch (error) {
console.warn('Failed to get available countries', error)
return ['US', 'GB', 'DE', 'FR', 'CA', 'AU'] // Fallback to common countries
}
}
/**
* Get available states/regions for a country
* @param {string} country - Country code
* @returns {Array} Array of state/region codes
*/
export function getAvailableStates(country) {
try {
if (!country) return []
const holidays = new Holidays()
const states = holidays.getStates(country)
// The getStates method might return an object, convert to array of keys
if (states && typeof states === 'object') {
return Array.isArray(states) ? states : Object.keys(states)
}
return []
} catch (error) {
console.warn('Failed to get available states for', country, error)
return []
}
}
/**
* Get holiday configuration info
* @returns {Object} Current holiday configuration
*/
export function getHolidayConfig() {
return {
country: currentCountry,
state: currentState,
region: currentRegion,
initialized: !!holidaysInstance,
}
}
// Initialize with US holidays by default
initializeHolidays('US')