Fix RTL handling, holiday names direction and UI details for full page RTL.
This commit is contained in:
		| @@ -21,13 +21,10 @@ const props = defineProps({ | |||||||
|     ]" |     ]" | ||||||
|     :data-date="props.day.date" |     :data-date="props.day.date" | ||||||
|   > |   > | ||||||
|     <h1>{{ props.day.displayText }}</h1> |     <h1 class="day-number">{{ props.day.displayText }}</h1> | ||||||
|     <span v-if="props.day.lunarPhase" class="lunar-phase">{{ props.day.lunarPhase }}</span> |     <span v-if="props.day.lunarPhase" class="lunar-phase">{{ props.day.lunarPhase }}</span> | ||||||
|  |     <div v-if="props.day.holiday" class="holiday-info" dir="auto" :title="props.day.holiday.name"> | ||||||
|     <div v-if="props.day.holiday" class="holiday-info"> |       {{ props.day.holiday.name }} | ||||||
|       <span class="holiday-name" :title="props.day.holiday.name"> |  | ||||||
|         {{ props.day.holiday.name }} |  | ||||||
|       </span> |  | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| @@ -38,19 +35,27 @@ const props = defineProps({ | |||||||
|   border-right: 1px solid var(--border-color); |   border-right: 1px solid var(--border-color); | ||||||
|   border-bottom: 1px solid var(--border-color); |   border-bottom: 1px solid var(--border-color); | ||||||
|   user-select: none; |   user-select: none; | ||||||
|   display: flex; |   display: grid; | ||||||
|   flex-direction: row; |   /* 3 columns: day number, flexible space, lunar phase */ | ||||||
|   align-items: flex-start; |   grid-template-columns: min-content 1fr min-content; | ||||||
|   justify-content: flex-start; |   /* 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; |   padding: 0.25em; | ||||||
|   overflow: hidden; |   overflow: hidden; | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   height: var(--row-h); |   height: var(--row-h); | ||||||
|   font-weight: 700; |   font-weight: 700; | ||||||
|   transition: background-color 0.15s ease; |   transition: background-color 0.15s ease; | ||||||
|  |   align-items: start; | ||||||
| } | } | ||||||
|  | .cell h1.day-number { | ||||||
| .cell h1 { |  | ||||||
|   margin: 0; |   margin: 0; | ||||||
|   padding: 0; |   padding: 0; | ||||||
|   min-width: 1.5em; |   min-width: 1.5em; | ||||||
| @@ -58,15 +63,16 @@ const props = defineProps({ | |||||||
|   font-weight: 700; |   font-weight: 700; | ||||||
|   color: var(--ink); |   color: var(--ink); | ||||||
|   transition: background-color 0.15s ease; |   transition: background-color 0.15s ease; | ||||||
|  |   grid-area: day-number; | ||||||
| } | } | ||||||
| .cell.weekend h1 { | .cell.weekend h1.day-number { | ||||||
|   color: var(--weekend); |   color: var(--weekend); | ||||||
| } | } | ||||||
| .cell.firstday h1 { | .cell.firstday h1.day-number { | ||||||
|   color: var(--firstday); |   color: var(--firstday); | ||||||
|   text-shadow: 0 0 0.1em var(--strong); |   text-shadow: 0 0 0.1em var(--strong); | ||||||
| } | } | ||||||
| .cell.today h1 { | .cell.today h1.day-number { | ||||||
|   border-radius: 2em; |   border-radius: 2em; | ||||||
|   background: var(--today); |   background: var(--today); | ||||||
|   border: 0.2em solid var(--today); |   border: 0.2em solid var(--today); | ||||||
| @@ -77,16 +83,9 @@ const props = defineProps({ | |||||||
| .cell.selected { | .cell.selected { | ||||||
|   filter: hue-rotate(180deg); |   filter: hue-rotate(180deg); | ||||||
| } | } | ||||||
| .cell.selected h1 { | .cell.selected h1.day-number { | ||||||
|   color: var(--strong); |   color: var(--strong); | ||||||
| } | } | ||||||
| .lunar-phase { |  | ||||||
|   position: absolute; |  | ||||||
|   top: 0.5em; |  | ||||||
|   right: 0.2em; |  | ||||||
|   font-size: 0.8em; |  | ||||||
|   opacity: 0.7; |  | ||||||
| } |  | ||||||
| .cell.holiday { | .cell.holiday { | ||||||
|   background-image: linear-gradient( |   background-image: linear-gradient( | ||||||
|     135deg, |     135deg, | ||||||
| @@ -103,27 +102,32 @@ const props = defineProps({ | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| .cell.holiday h1 { | .cell.holiday h1.day-number { | ||||||
|   /* Slight emphasis without forcing a specific hue */ |   /* Slight emphasis without forcing a specific hue */ | ||||||
|   color: var(--holiday); |   color: var(--holiday); | ||||||
|   text-shadow: 0 0 0.3em rgba(255, 255, 255, 0.4); |   text-shadow: 0 0 0.3em rgba(255, 255, 255, 0.4); | ||||||
| } | } | ||||||
| .holiday-info { | .lunar-phase { | ||||||
|   position: absolute; |   grid-area: lunar-phase; | ||||||
|   bottom: 0.1em; |   align-self: start; | ||||||
|   left: 0.1em; |   justify-self: end; | ||||||
|   right: 0.1em; |   margin-top: 0.5em; | ||||||
|   line-height: 1; |   margin-inline-end: 0.2em; | ||||||
|   overflow: hidden; |   font-size: 0.8em; | ||||||
|   font-size: clamp(1.2vw, 0.6em, 1em); |   opacity: 0.7; | ||||||
| } | } | ||||||
|  |  | ||||||
| .holiday-name { | .holiday-info { | ||||||
|   display: block; |   grid-area: holiday-info; | ||||||
|   color: var(--holiday-label); |   align-self: end; | ||||||
|   padding: 0.15em 0.35em 0.15em 0.25em; |   overflow: hidden; | ||||||
|   text-overflow: ellipsis; |   text-overflow: ellipsis; | ||||||
|   white-space: nowrap; |   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; | ||||||
| } | } | ||||||
| </style> | </style> | ||||||
|   | |||||||
| @@ -87,7 +87,7 @@ const vwm = createVirtualWeekManager({ | |||||||
|   contentHeight, |   contentHeight, | ||||||
| }) | }) | ||||||
| const visibleWeeks = vwm.visibleWeeks | const visibleWeeks = vwm.visibleWeeks | ||||||
| const { scheduleWindowUpdate, resetWeeks, refreshEvents } = vwm | const { scheduleWindowUpdate, resetWeeks, refreshEvents, refreshHolidays } = vwm | ||||||
|  |  | ||||||
| // Scroll managers (after scheduleWindowUpdate available) | // Scroll managers (after scheduleWindowUpdate available) | ||||||
| const scrollManager = createScrollManager({ viewport, scheduleRebuild: scheduleWindowUpdate }) | const scrollManager = createScrollManager({ viewport, scheduleRebuild: scheduleWindowUpdate }) | ||||||
| @@ -98,8 +98,7 @@ const weekColumnScrollManager = createWeekColumnScrollManager({ | |||||||
|   contentHeight, |   contentHeight, | ||||||
|   setScrollTop, |   setScrollTop, | ||||||
| }) | }) | ||||||
| const { isWeekColDragging, handleWeekColMouseDown, handlePointerLockChange } = | const { handleWeekColMouseDown, handlePointerLockChange } = weekColumnScrollManager | ||||||
|   weekColumnScrollManager |  | ||||||
| const monthScrollManager = createMonthScrollManager({ | const monthScrollManager = createMonthScrollManager({ | ||||||
|   viewport, |   viewport, | ||||||
|   viewportHeight, |   viewportHeight, | ||||||
| @@ -160,6 +159,25 @@ function clearSelection() { | |||||||
|   selection.value = { startDate: null, dayCount: 0 } |   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) { | function startDrag(dateStr) { | ||||||
|   dateStr = normalizeDate(dateStr) |   dateStr = normalizeDate(dateStr) | ||||||
|   if (calendarStore.config.select_days === 0) return |   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(() => { | onMounted(() => { | ||||||
|   computeRowHeight() |   computeRowHeight() | ||||||
|   calendarStore.updateCurrentDate() |   calendarStore.updateCurrentDate() | ||||||
| @@ -376,8 +385,6 @@ const handleEventClick = (payload) => { | |||||||
|   emit('edit-event', payload) |   emit('edit-event', payload) | ||||||
| } | } | ||||||
|  |  | ||||||
| // header year change delegated to manager |  | ||||||
|  |  | ||||||
| // Heuristic: rotate month label (180deg) only for predominantly Latin text. | // Heuristic: rotate month label (180deg) only for predominantly Latin text. | ||||||
| // We explicitly avoid locale detection; rely solely on characters present. | // We explicitly avoid locale detection; rely solely on characters present. | ||||||
| // Disable rotation if any CJK Unified Ideograph or Compatibility Ideograph appears. | // 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( | watch( | ||||||
|   () => calendarStore.events, |   () => calendarStore.events, | ||||||
|   () => { |   () => { | ||||||
|   | |||||||
| @@ -101,12 +101,12 @@ onBeforeUnmount(() => { | |||||||
|   display: flex; |   display: flex; | ||||||
|   justify-content: end; |   justify-content: end; | ||||||
|   align-items: center; |   align-items: center; | ||||||
|   margin-right: 1.5rem; |   margin-inline-end: 2rem; | ||||||
| } | } | ||||||
| .toggle-btn { | .toggle-btn { | ||||||
|   position: fixed; |   position: fixed; | ||||||
|   top: 0; |   top: 0; | ||||||
|   right: 0; |   inset-inline-end: 0; | ||||||
|   background: transparent; |   background: transparent; | ||||||
|   border: none; |   border: none; | ||||||
|   color: var(--muted); |   color: var(--muted); | ||||||
| @@ -157,7 +157,6 @@ onBeforeUnmount(() => { | |||||||
|   color: var(--muted); |   color: var(--muted); | ||||||
|   padding: 0; |   padding: 0; | ||||||
|   margin: 0; |   margin: 0; | ||||||
|   margin-right: 0.6rem; |  | ||||||
|   cursor: pointer; |   cursor: pointer; | ||||||
|   font-size: 1.5rem; |   font-size: 1.5rem; | ||||||
|   line-height: 1; |   line-height: 1; | ||||||
|   | |||||||
| @@ -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() { |   function goToToday() { | ||||||
|     const top = addDays(new Date(calendarStore.now), -21) |     const top = addDays(new Date(calendarStore.now), -21) | ||||||
|     const targetWeekIndex = getWeekIndex(top) |     const targetWeekIndex = getWeekIndex(top) | ||||||
| @@ -391,6 +413,7 @@ export function createVirtualWeekManager({ | |||||||
|     resetWeeks, |     resetWeeks, | ||||||
|     updateVisibleWeeks, |     updateVisibleWeeks, | ||||||
|     refreshEvents, |     refreshEvents, | ||||||
|  |     refreshHolidays, | ||||||
|     getWeekIndex, |     getWeekIndex, | ||||||
|     getFirstDayForVirtualWeek, |     getFirstDayForVirtualWeek, | ||||||
|     goToToday, |     goToToday, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Leo Vasanko
					Leo Vasanko