From 5752855f5217005482b12cf2ab3d0f751ab3e316 Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Tue, 26 Aug 2025 09:09:32 -0600 Subject: [PATCH] Fix RTL handling, holiday names direction and UI details for full page RTL. --- src/components/CalendarDay.vue | 78 ++++++++++++++++--------------- src/components/CalendarView.vue | 37 +++++++++------ src/components/HeaderControls.vue | 5 +- src/plugins/virtualWeeks.js | 23 +++++++++ 4 files changed, 88 insertions(+), 55 deletions(-) diff --git a/src/components/CalendarDay.vue b/src/components/CalendarDay.vue index fde7669..0b93324 100644 --- a/src/components/CalendarDay.vue +++ b/src/components/CalendarDay.vue @@ -21,13 +21,10 @@ const props = defineProps({ ]" :data-date="props.day.date" > -

{{ props.day.displayText }}

+

{{ props.day.displayText }}

{{ props.day.lunarPhase }} - -
- - {{ props.day.holiday.name }} - +
+ {{ props.day.holiday.name }}
@@ -38,19 +35,27 @@ const props = defineProps({ border-right: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color); user-select: none; - display: flex; - flex-direction: row; - align-items: flex-start; - justify-content: flex-start; + display: grid; + /* 3 columns: day number, flexible space, lunar phase */ + grid-template-columns: min-content 1fr min-content; + /* 3 rows: header, flexible filler, holiday label */ + grid-template-rows: auto 1fr auto; + /* Named grid areas (only ones actually used) */ + grid-template-areas: + 'day-number . lunar-phase' + 'day-number . lunar-phase' + 'holiday-info holiday-info holiday-info'; + /* Explicit areas mainly for clarity */ + grid-auto-flow: row; padding: 0.25em; overflow: hidden; width: 100%; height: var(--row-h); font-weight: 700; transition: background-color 0.15s ease; + align-items: start; } - -.cell h1 { +.cell h1.day-number { margin: 0; padding: 0; min-width: 1.5em; @@ -58,15 +63,16 @@ const props = defineProps({ font-weight: 700; color: var(--ink); transition: background-color 0.15s ease; + grid-area: day-number; } -.cell.weekend h1 { +.cell.weekend h1.day-number { color: var(--weekend); } -.cell.firstday h1 { +.cell.firstday h1.day-number { color: var(--firstday); text-shadow: 0 0 0.1em var(--strong); } -.cell.today h1 { +.cell.today h1.day-number { border-radius: 2em; background: var(--today); border: 0.2em solid var(--today); @@ -77,16 +83,9 @@ const props = defineProps({ .cell.selected { filter: hue-rotate(180deg); } -.cell.selected h1 { +.cell.selected h1.day-number { color: var(--strong); } -.lunar-phase { - position: absolute; - top: 0.5em; - right: 0.2em; - font-size: 0.8em; - opacity: 0.7; -} .cell.holiday { background-image: linear-gradient( 135deg, @@ -103,27 +102,32 @@ const props = defineProps({ ); } } -.cell.holiday h1 { +.cell.holiday h1.day-number { /* Slight emphasis without forcing a specific hue */ color: var(--holiday); text-shadow: 0 0 0.3em rgba(255, 255, 255, 0.4); } -.holiday-info { - position: absolute; - bottom: 0.1em; - left: 0.1em; - right: 0.1em; - line-height: 1; - overflow: hidden; - font-size: clamp(1.2vw, 0.6em, 1em); +.lunar-phase { + grid-area: lunar-phase; + align-self: start; + justify-self: end; + margin-top: 0.5em; + margin-inline-end: 0.2em; + font-size: 0.8em; + opacity: 0.7; } -.holiday-name { - display: block; - color: var(--holiday-label); - padding: 0.15em 0.35em 0.15em 0.25em; +.holiday-info { + grid-area: holiday-info; + align-self: end; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - overflow: hidden; + color: var(--holiday-label); + font-size: clamp(1.2vw, 0.6em, 1em); + line-height: 1; + padding-inline: 0.15em; + padding-block-end: 0.05em; + pointer-events: auto; } diff --git a/src/components/CalendarView.vue b/src/components/CalendarView.vue index b2912e6..387f5d7 100644 --- a/src/components/CalendarView.vue +++ b/src/components/CalendarView.vue @@ -87,7 +87,7 @@ const vwm = createVirtualWeekManager({ contentHeight, }) const visibleWeeks = vwm.visibleWeeks -const { scheduleWindowUpdate, resetWeeks, refreshEvents } = vwm +const { scheduleWindowUpdate, resetWeeks, refreshEvents, refreshHolidays } = vwm // Scroll managers (after scheduleWindowUpdate available) const scrollManager = createScrollManager({ viewport, scheduleRebuild: scheduleWindowUpdate }) @@ -98,8 +98,7 @@ const weekColumnScrollManager = createWeekColumnScrollManager({ contentHeight, setScrollTop, }) -const { isWeekColDragging, handleWeekColMouseDown, handlePointerLockChange } = - weekColumnScrollManager +const { handleWeekColMouseDown, handlePointerLockChange } = weekColumnScrollManager const monthScrollManager = createMonthScrollManager({ viewport, viewportHeight, @@ -160,6 +159,25 @@ function clearSelection() { selection.value = { startDate: null, dayCount: 0 } } +// React to holiday config changes: rebuild or refresh holidays +watch( + () => [ + calendarStore.config.holidays.enabled, + calendarStore.config.holidays.country, + calendarStore.config.holidays.state, + calendarStore.config.holidays.region, + ], + (_newVals, _oldVals) => { + // If weeks already built, just refresh holiday info + if (visibleWeeks.value.length) { + refreshHolidays('config-change') + } else { + resetWeeks('holiday-config-change') + } + }, + { deep: false }, +) + function startDrag(dateStr) { dateStr = normalizeDate(dateStr) if (calendarStore.config.select_days === 0) return @@ -294,15 +312,6 @@ function calculateSelection(anchorStr, otherStr) { } } -// ---------------- Week label column drag scrolling ---------------- -function getWeekLabelRect() { - // Prefer header year label width as stable reference - const headerYear = document.querySelector('.calendar-header .year-label') - if (headerYear) return headerYear.getBoundingClientRect() - const weekLabel = viewport.value?.querySelector('.week-row .week-label') - return weekLabel ? weekLabel.getBoundingClientRect() : null -} - onMounted(() => { computeRowHeight() calendarStore.updateCurrentDate() @@ -376,8 +385,6 @@ const handleEventClick = (payload) => { emit('edit-event', payload) } -// header year change delegated to manager - // Heuristic: rotate month label (180deg) only for predominantly Latin text. // We explicitly avoid locale detection; rely solely on characters present. // Disable rotation if any CJK Unified Ideograph or Compatibility Ideograph appears. @@ -402,7 +409,7 @@ watch( }, ) -// Watch lightweight mutation counter only (not deep events map) and rebuild lazily +// Event changes watch( () => calendarStore.events, () => { diff --git a/src/components/HeaderControls.vue b/src/components/HeaderControls.vue index e61a373..e1ca08a 100644 --- a/src/components/HeaderControls.vue +++ b/src/components/HeaderControls.vue @@ -101,12 +101,12 @@ onBeforeUnmount(() => { display: flex; justify-content: end; align-items: center; - margin-right: 1.5rem; + margin-inline-end: 2rem; } .toggle-btn { position: fixed; top: 0; - right: 0; + inset-inline-end: 0; background: transparent; border: none; color: var(--muted); @@ -157,7 +157,6 @@ onBeforeUnmount(() => { color: var(--muted); padding: 0; margin: 0; - margin-right: 0.6rem; cursor: pointer; font-size: 1.5rem; line-height: 1; diff --git a/src/plugins/virtualWeeks.js b/src/plugins/virtualWeeks.js index 6d73466..817228f 100644 --- a/src/plugins/virtualWeeks.js +++ b/src/plugins/virtualWeeks.js @@ -371,6 +371,28 @@ export function createVirtualWeekManager({ } } + // Refresh holiday data for currently visible weeks without rebuilding structure + function refreshHolidays(reason = 'holidays-refresh') { + if (!visibleWeeks.value.length) return + const enabled = calendarStore.config.holidays.enabled + if (enabled) calendarStore._ensureHolidaysInitialized?.() + for (const week of visibleWeeks.value) { + for (const day of week.days) { + if (enabled) { + const holiday = getHolidayForDate(day.date) + ;((day.holiday = holiday), (day.isHoliday = holiday !== null)) + } else { + day.holiday = null + day.isHoliday = false + } + } + } + if (process.env.NODE_ENV !== 'production') { + // eslint-disable-next-line no-console + console.debug('[VirtualWeeks] refreshHolidays', reason, { weeks: visibleWeeks.value.length }) + } + } + function goToToday() { const top = addDays(new Date(calendarStore.now), -21) const targetWeekIndex = getWeekIndex(top) @@ -391,6 +413,7 @@ export function createVirtualWeekManager({ resetWeeks, updateVisibleWeeks, refreshEvents, + refreshHolidays, getWeekIndex, getFirstDayForVirtualWeek, goToToday,