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" | ||||
|   > | ||||
|     <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> | ||||
|  | ||||
|     <div v-if="props.day.holiday" class="holiday-info"> | ||||
|       <span class="holiday-name" :title="props.day.holiday.name"> | ||||
|     <div v-if="props.day.holiday" class="holiday-info" dir="auto" :title="props.day.holiday.name"> | ||||
|       {{ props.day.holiday.name }} | ||||
|       </span> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| @@ -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; | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -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, | ||||
|   () => { | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Leo Vasanko
					Leo Vasanko