Responsive date strings in calendar days for small screen support and consistent wrapping.
This commit is contained in:
		| @@ -1,15 +1,61 @@ | |||||||
| <script setup> | <script setup> | ||||||
| import { computed } from 'vue' | import { computed, ref, onMounted, onBeforeUnmount } from 'vue' | ||||||
| import { formatDateCompact, fromLocalString } from '@/utils/date' | import { fromLocalString } from '@/utils/date' | ||||||
|  |  | ||||||
| const props = defineProps({ | const props = defineProps({ | ||||||
|   day: Object, |   day: Object, | ||||||
|   dragging: { type: Boolean, default: false }, |   dragging: { type: Boolean, default: false }, | ||||||
| }) | }) | ||||||
|  |  | ||||||
|  | // Reactive viewport width detection | ||||||
|  | const isNarrowView = ref(false) | ||||||
|  | const isVeryNarrowView = ref(false) | ||||||
|  | const isSmallView = ref(false) | ||||||
|  |  | ||||||
|  | function checkViewportWidth() { | ||||||
|  |   const width = window.innerWidth | ||||||
|  |   isSmallView.value = width < 800 | ||||||
|  |   isNarrowView.value = width < 600 | ||||||
|  |   isVeryNarrowView.value = width < 400 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | onMounted(() => { | ||||||
|  |   checkViewportWidth() | ||||||
|  |   window.addEventListener('resize', checkViewportWidth) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | onBeforeUnmount(() => { | ||||||
|  |   window.removeEventListener('resize', checkViewportWidth) | ||||||
|  | }) | ||||||
|  |  | ||||||
| const formattedDate = computed(() => { | const formattedDate = computed(() => { | ||||||
|   const date = fromLocalString(props.day.date) |   const date = fromLocalString(props.day.date) | ||||||
|   return formatDateCompact(date) |    | ||||||
|  |   let options = { day: 'numeric', month: 'short' } | ||||||
|  |    | ||||||
|  |   if (isVeryNarrowView.value) { | ||||||
|  |     // Very narrow: show only day number | ||||||
|  |     options = { day: 'numeric' } | ||||||
|  |   } else if (isNarrowView.value) { | ||||||
|  |     // Narrow: show day and month, no weekday | ||||||
|  |     options = { day: 'numeric', month: 'short' } | ||||||
|  |   } else { | ||||||
|  |     // Wide: show weekday, day, and month | ||||||
|  |     options = { weekday: 'short', day: 'numeric', month: 'short' } | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   let formatted = date.toLocaleDateString(undefined, options) | ||||||
|  |    | ||||||
|  |   // Below 700px, replace first space with newline to force weekday on separate line | ||||||
|  |   if (isSmallView.value && !isNarrowView.value && !isVeryNarrowView.value) { | ||||||
|  |     formatted = formatted.replace(/\s/, '\n') | ||||||
|  |   } | ||||||
|  |    | ||||||
|  |   // Replace the last space (between month and day) with nbsp to prevent breaking there | ||||||
|  |   // but keep the space after weekday (if present) as regular space to allow wrapping | ||||||
|  |   formatted = formatted.replace(/\s+(?=\S+$)/, '\u00A0') | ||||||
|  |    | ||||||
|  |   return formatted | ||||||
| }) | }) | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| @@ -136,6 +182,7 @@ const formattedDate = computed(() => { | |||||||
|   color: var(--ink); |   color: var(--ink); | ||||||
|   line-height: 1; |   line-height: 1; | ||||||
|   pointer-events: none; |   pointer-events: none; | ||||||
|  |   white-space: pre-wrap; | ||||||
| } | } | ||||||
|  |  | ||||||
| .cell.weekend .compact-date { | .cell.weekend .compact-date { | ||||||
|   | |||||||
| @@ -192,17 +192,6 @@ function formatTodayString(date, weekday = "long", month = "long") { | |||||||
|   return formatted.charAt(0).toUpperCase() + formatted.slice(1) |   return formatted.charAt(0).toUpperCase() + formatted.slice(1) | ||||||
| } | } | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Format date as compact string for day cell corner (e.g., "Mon 15 Jan") |  | ||||||
|  */ |  | ||||||
| function formatDateCompact(date) { |  | ||||||
|   return date.toLocaleDateString(undefined, {  |  | ||||||
|     weekday: 'short',  |  | ||||||
|     day: 'numeric',  |  | ||||||
|     month: 'short' |  | ||||||
|   }) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export { | export { | ||||||
|   // constants |   // constants | ||||||
|   monthAbbr, |   monthAbbr, | ||||||
| @@ -229,7 +218,6 @@ export { | |||||||
|   formatDateRange, |   formatDateRange, | ||||||
|   formatDateShort, |   formatDateShort, | ||||||
|   formatDateLong, |   formatDateLong, | ||||||
|   formatDateCompact, |  | ||||||
|   formatTodayString, |   formatTodayString, | ||||||
|   lunarPhaseSymbol, |   lunarPhaseSymbol, | ||||||
|   // iso helpers re-export |   // iso helpers re-export | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Leo Vasanko
					Leo Vasanko