Styling changes, avoid glitches even on very small screens.
This commit is contained in:
		| @@ -7,7 +7,9 @@ | |||||||
| * { | * { | ||||||
|   box-sizing: border-box; |   box-sizing: border-box; | ||||||
| } | } | ||||||
|  | html { | ||||||
|  |   font-size: min(3vmin, 16px); | ||||||
|  | } | ||||||
| html, | html, | ||||||
| body { | body { | ||||||
|   height: 100%; |   height: 100%; | ||||||
| @@ -16,7 +18,7 @@ body { | |||||||
| body { | body { | ||||||
|   margin: 0; |   margin: 0; | ||||||
|   font: |   font: | ||||||
|     500 14px/1.2 ui-sans-serif, |     500 1rem/1.2 ui-sans-serif, | ||||||
|     system-ui, |     system-ui, | ||||||
|     -apple-system, |     -apple-system, | ||||||
|     Segoe UI, |     Segoe UI, | ||||||
|   | |||||||
| @@ -206,7 +206,7 @@ onUnmounted(() => { | |||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <style scoped> | <style> | ||||||
| .ec-modal { | .ec-modal { | ||||||
|   position: fixed; /* still fixed for overlay & dragging, but now top/left are set dynamically */ |   position: fixed; /* still fixed for overlay & dragging, but now top/left are set dynamically */ | ||||||
|   background: color-mix(in srgb, var(--panel) 85%, transparent); |   background: color-mix(in srgb, var(--panel) 85%, transparent); | ||||||
|   | |||||||
| @@ -9,14 +9,12 @@ const props = defineProps({ | |||||||
|  |  | ||||||
| // Reactive viewport width detection | // Reactive viewport width detection | ||||||
| const isNarrowView = ref(false) | const isNarrowView = ref(false) | ||||||
| const isVeryNarrowView = ref(false) |  | ||||||
| const isSmallView = ref(false) | const isSmallView = ref(false) | ||||||
|  |  | ||||||
| function checkViewportWidth() { | function checkViewportWidth() { | ||||||
|   const width = window.innerWidth |   const width = window.innerWidth | ||||||
|   isSmallView.value = width < 800 |   isSmallView.value = width < 800 | ||||||
|   isNarrowView.value = width < 600 |   isNarrowView.value = width < 600 | ||||||
|   isVeryNarrowView.value = width < 400 |  | ||||||
| } | } | ||||||
|  |  | ||||||
| onMounted(() => { | onMounted(() => { | ||||||
| @@ -30,27 +28,12 @@ onBeforeUnmount(() => { | |||||||
|  |  | ||||||
| const formattedDate = computed(() => { | const formattedDate = computed(() => { | ||||||
|   const date = fromLocalString(props.day.date) |   const date = fromLocalString(props.day.date) | ||||||
|    |   let options = { weekday: 'short', day: 'numeric', month: 'short' } | ||||||
|   let options = { day: 'numeric', month: 'short' } |   // Remove weekday on very small viewports | ||||||
|    |   if (isNarrowView.value) 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) |   let formatted = date.toLocaleDateString(undefined, options) | ||||||
|    |   // Split between weekday and day/month on small viewports | ||||||
|   // Below 700px, replace first space with newline to force weekday on separate line |   if (isSmallView.value) formatted = formatted.replace(/\s/, '\n') | ||||||
|   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 |   // 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 |   // but keep the space after weekday (if present) as regular space to allow wrapping | ||||||
|   formatted = formatted.replace(/\s+(?=\S+$)/, '\u00A0') |   formatted = formatted.replace(/\s+(?=\S+$)/, '\u00A0') | ||||||
| @@ -167,10 +150,9 @@ const formattedDate = computed(() => { | |||||||
| .lunar-phase { | .lunar-phase { | ||||||
|   grid-area: lunar-phase; |   grid-area: lunar-phase; | ||||||
|   position: absolute; |   position: absolute; | ||||||
|   inset-block-start: 0.5em; |   inset-block-start: 0.1em; | ||||||
|   inset-inline-end: 0.2em; |   inset-inline-end: 0.1em; | ||||||
|   font-size: 0.8em; |   font-size: 0.8rem; | ||||||
|   opacity: 0.7; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .compact-date { | .compact-date { | ||||||
| @@ -178,7 +160,8 @@ const formattedDate = computed(() => { | |||||||
|   top: 0.25em; |   top: 0.25em; | ||||||
|   left: 0.25em; |   left: 0.25em; | ||||||
|   inset-inline-end: 1rem; /* Space for lunar phase */ |   inset-inline-end: 1rem; /* Space for lunar phase */ | ||||||
|   font-weight: 400; |   font-weight: 300; | ||||||
|  |   font-size: 0.8rem; | ||||||
|   color: var(--ink); |   color: var(--ink); | ||||||
|   line-height: 1; |   line-height: 1; | ||||||
|   pointer-events: none; |   pointer-events: none; | ||||||
| @@ -204,7 +187,7 @@ const formattedDate = computed(() => { | |||||||
|   overflow: hidden; |   overflow: hidden; | ||||||
|   max-width: 100%; |   max-width: 100%; | ||||||
|   color: var(--holiday); |   color: var(--holiday); | ||||||
|   font-size: 1em; |   font-size: 0.8em; | ||||||
|   font-weight: 400; |   font-weight: 400; | ||||||
|   line-height: 1.0; |   line-height: 1.0; | ||||||
|   padding-inline: 0.15em; |   padding-inline: 0.15em; | ||||||
|   | |||||||
| @@ -119,7 +119,7 @@ const weekdayNames = computed(() => { | |||||||
| .calendar-header { | .calendar-header { | ||||||
|   display: grid; |   display: grid; | ||||||
|   grid-template-columns: var(--week-w) repeat(7, 1fr) var(--month-w); |   grid-template-columns: var(--week-w) repeat(7, 1fr) var(--month-w); | ||||||
|   border-bottom: 2px solid var(--muted); |   border-bottom: .1rem solid var(--muted); | ||||||
|   align-items: last baseline; |   align-items: last baseline; | ||||||
|   flex-shrink: 0; |   flex-shrink: 0; | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ import WeekdaySelector from './WeekdaySelector.vue' | |||||||
| import Numeric from './Numeric.vue' | import Numeric from './Numeric.vue' | ||||||
| import { | import { | ||||||
|   addDaysStr, |   addDaysStr, | ||||||
|   getMondayOfISOWeek, |  | ||||||
|   fromLocalString, |   fromLocalString, | ||||||
|   formatDateShort, |   formatDateShort, | ||||||
|   formatDateLong, |   formatDateLong, | ||||||
| @@ -683,7 +682,7 @@ const recurrenceSummary = computed(() => { | |||||||
| .ec-field input[type='time'], | .ec-field input[type='time'], | ||||||
| .ec-field input[type='number'], | .ec-field input[type='number'], | ||||||
| .ec-field select { | .ec-field select { | ||||||
|   border: 1px solid var(--muted); |   border: .1rem solid var(--muted); | ||||||
|   border-radius: 0.4rem; |   border-radius: 0.4rem; | ||||||
|   padding: 0.5rem 0.6rem; |   padding: 0.5rem 0.6rem; | ||||||
|   width: 100%; |   width: 100%; | ||||||
| @@ -853,7 +852,7 @@ const recurrenceSummary = computed(() => { | |||||||
| .freq-select { | .freq-select { | ||||||
|   padding: 0.4rem 0.55rem; |   padding: 0.4rem 0.55rem; | ||||||
|   font-size: 0.75rem; |   font-size: 0.75rem; | ||||||
|   border: 1px solid var(--input-border); |   border: .1rem solid var(--input-border); | ||||||
|   background: var(--panel-alt); |   background: var(--panel-alt); | ||||||
|   color: var(--ink); |   color: var(--ink); | ||||||
|   border-radius: 0.45rem; |   border-radius: 0.45rem; | ||||||
| @@ -867,15 +866,15 @@ const recurrenceSummary = computed(() => { | |||||||
|   background: var(--panel-accent); |   background: var(--panel-accent); | ||||||
|   color: var(--ink); |   color: var(--ink); | ||||||
|   box-shadow: |   box-shadow: | ||||||
|     0 0 0 1px var(--input-focus), |     0 0 0 .1rem var(--input-focus), | ||||||
|     0 0 0 4px rgba(37, 99, 235, 0.15); |     0 0 0 .4rem rgba(37, 99, 235, 0.15); | ||||||
| } | } | ||||||
| .interval-input, | .interval-input, | ||||||
| .occ-input { | .occ-input { | ||||||
|   display: none; |   display: none; | ||||||
| } | } | ||||||
| .ec-field input[type='text'] { | .ec-field input[type='text'] { | ||||||
|   border: 1px solid var(--input-border); |   border: .1rem solid var(--input-border); | ||||||
|   background: var(--panel-alt); |   background: var(--panel-alt); | ||||||
|   border-radius: 0.45rem; |   border-radius: 0.45rem; | ||||||
|   padding: 0.4rem 0.5rem; |   padding: 0.4rem 0.5rem; | ||||||
| @@ -889,8 +888,8 @@ const recurrenceSummary = computed(() => { | |||||||
|   border-color: var(--input-focus); |   border-color: var(--input-focus); | ||||||
|   background: var(--panel-accent); |   background: var(--panel-accent); | ||||||
|   box-shadow: |   box-shadow: | ||||||
|     0 0 0 1px var(--input-focus), |     0 0 0 .1rem var(--input-focus), | ||||||
|     0 0 0 4px rgba(37, 99, 235, 0.15); |     0 0 0 .4rem rgba(37, 99, 235, 0.15); | ||||||
| } | } | ||||||
| .hint { | .hint { | ||||||
|   font-size: 0.65rem; |   font-size: 0.65rem; | ||||||
| @@ -908,7 +907,7 @@ const recurrenceSummary = computed(() => { | |||||||
|   align-items: center; |   align-items: center; | ||||||
|   width: 100%; |   width: 100%; | ||||||
|   padding: 0.6rem 0.8rem; |   padding: 0.6rem 0.8rem; | ||||||
|   border: 1px solid var(--muted); |   border: .1rem solid var(--muted); | ||||||
|   background: var(--panel); |   background: var(--panel); | ||||||
|   border-radius: 0.4rem; |   border-radius: 0.4rem; | ||||||
|   cursor: pointer; |   cursor: pointer; | ||||||
| @@ -931,7 +930,7 @@ const recurrenceSummary = computed(() => { | |||||||
|   display: grid; |   display: grid; | ||||||
|   gap: 0.6rem; |   gap: 0.6rem; | ||||||
|   padding: 0.6rem; |   padding: 0.6rem; | ||||||
|   border: 1px solid var(--muted); |   border: .1rem solid var(--muted); | ||||||
|   border-radius: 0.4rem; |   border-radius: 0.4rem; | ||||||
|   background: color-mix(in srgb, var(--muted) 20%, transparent); |   background: color-mix(in srgb, var(--muted) 20%, transparent); | ||||||
| } | } | ||||||
| @@ -945,7 +944,7 @@ const recurrenceSummary = computed(() => { | |||||||
| .ec-repeat-modes .mode-btn { | .ec-repeat-modes .mode-btn { | ||||||
|   flex: 1 1 auto; |   flex: 1 1 auto; | ||||||
|   padding: 0.4rem 0.6rem; |   padding: 0.4rem 0.6rem; | ||||||
|   border: 1px solid var(--muted); |   border: .1rem solid var(--muted); | ||||||
|   background: var(--panel); |   background: var(--panel); | ||||||
|   border-radius: 0.4rem; |   border-radius: 0.4rem; | ||||||
|   cursor: pointer; |   cursor: pointer; | ||||||
|   | |||||||
| @@ -321,7 +321,6 @@ function startLocalDrag(init, evt) { | |||||||
|   let originalWeekday = null |   let originalWeekday = null | ||||||
|   let originalPattern = null |   let originalPattern = null | ||||||
|   if (init.mode === 'move') { |   if (init.mode === 'move') { | ||||||
|     try { |  | ||||||
|     originalWeekday = new Date(init.startDate + 'T00:00:00').getDay() |     originalWeekday = new Date(init.startDate + 'T00:00:00').getDay() | ||||||
|     const baseEv = store.getEventById(init.id) |     const baseEv = store.getEventById(init.id) | ||||||
|     if ( |     if ( | ||||||
| @@ -332,7 +331,6 @@ function startLocalDrag(init, evt) { | |||||||
|     ) { |     ) { | ||||||
|       originalPattern = [...baseEv.recur.weekdays] |       originalPattern = [...baseEv.recur.weekdays] | ||||||
|     } |     } | ||||||
|     } catch {} |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   dragState.value = { |   dragState.value = { | ||||||
| @@ -565,7 +563,7 @@ function applyRangeDuringDrag(st, startDate, endDate) { | |||||||
|   inset: 0; |   inset: 0; | ||||||
|   display: grid; |   display: grid; | ||||||
|   grid-template-columns: repeat(7, 1fr); |   grid-template-columns: repeat(7, 1fr); | ||||||
|   margin-top: 1.8em; |   margin-top: 1.0rem; | ||||||
|   pointer-events: none; |   pointer-events: none; | ||||||
| } | } | ||||||
| .segment-grid { | .segment-grid { | ||||||
|   | |||||||
| @@ -272,13 +272,13 @@ onBeforeUnmount(() => { | |||||||
| .header-controls-leave-to { | .header-controls-leave-to { | ||||||
|   opacity: 0; |   opacity: 0; | ||||||
|   max-height: 0; |   max-height: 0; | ||||||
|   transform: translateY(-20px); |   transform: translateY(-1rem); | ||||||
| } | } | ||||||
|  |  | ||||||
| .header-controls-enter-to, | .header-controls-enter-to, | ||||||
| .header-controls-leave-from { | .header-controls-leave-from { | ||||||
|   opacity: 1; |   opacity: 1; | ||||||
|   max-height: 100px; |   max-height: 4rem; | ||||||
|   transform: translateY(0); |   transform: translateY(0); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -487,7 +487,7 @@ function parseGoToDateCandidate(input, refStr) { | |||||||
|   padding: 0.32rem 0.5rem; |   padding: 0.32rem 0.5rem; | ||||||
|   padding-inline-start: 2.05rem; /* increased space for icon */ |   padding-inline-start: 2.05rem; /* increased space for icon */ | ||||||
|   border-radius: 0.45rem; |   border-radius: 0.45rem; | ||||||
|   border: 1px solid color-mix(in srgb, var(--muted) 35%, transparent); |   border: .1rem solid color-mix(in srgb, var(--muted) 35%, transparent); | ||||||
|   background: color-mix(in srgb, var(--panel) 88%, transparent); |   background: color-mix(in srgb, var(--panel) 88%, transparent); | ||||||
|   font: inherit; |   font: inherit; | ||||||
|   line-height: 1.1; |   line-height: 1.1; | ||||||
| @@ -532,7 +532,7 @@ function parseGoToDateCandidate(input, refStr) { | |||||||
|   background: color-mix(in srgb, var(--panel) 85%, transparent); |   background: color-mix(in srgb, var(--panel) 85%, transparent); | ||||||
|   padding: 0.15rem 0.3rem; |   padding: 0.15rem 0.3rem; | ||||||
|   border-radius: 0.25rem; |   border-radius: 0.25rem; | ||||||
|   border: 1px solid color-mix(in srgb, var(--muted) 25%, transparent); |   border: .1rem solid color-mix(in srgb, var(--muted) 25%, transparent); | ||||||
| } | } | ||||||
|  |  | ||||||
| .search-bar input:focus + .shortcut-hint, | .search-bar input:focus + .shortcut-hint, | ||||||
| @@ -551,7 +551,7 @@ function parseGoToDateCandidate(input, refStr) { | |||||||
|   background: color-mix(in srgb, var(--panel) 92%, transparent); |   background: color-mix(in srgb, var(--panel) 92%, transparent); | ||||||
|   backdrop-filter: blur(0.6em); |   backdrop-filter: blur(0.6em); | ||||||
|   -webkit-backdrop-filter: blur(0.6em); |   -webkit-backdrop-filter: blur(0.6em); | ||||||
|   border: 1px solid color-mix(in srgb, var(--muted) 35%, transparent); |   border: .1rem solid color-mix(in srgb, var(--muted) 35%, transparent); | ||||||
|   border-radius: 0.55rem; |   border-radius: 0.55rem; | ||||||
|   max-height: 16rem; |   max-height: 16rem; | ||||||
|   overflow: auto; |   overflow: auto; | ||||||
| @@ -593,7 +593,7 @@ function parseGoToDateCandidate(input, refStr) { | |||||||
|   background: color-mix(in srgb, var(--panel) 92%, transparent); |   background: color-mix(in srgb, var(--panel) 92%, transparent); | ||||||
|   backdrop-filter: blur(0.6em); |   backdrop-filter: blur(0.6em); | ||||||
|   -webkit-backdrop-filter: blur(0.6em); |   -webkit-backdrop-filter: blur(0.6em); | ||||||
|   border: 1px solid color-mix(in srgb, var(--muted) 35%, transparent); |   border: .1rem solid color-mix(in srgb, var(--muted) 35%, transparent); | ||||||
|   border-radius: 0.55rem; |   border-radius: 0.55rem; | ||||||
|   box-shadow: 0 0.5em 1.25em rgba(0, 0, 0, 0.3); |   box-shadow: 0 0.5em 1.25em rgba(0, 0, 0, 0.3); | ||||||
|   font-size: 0.7rem; |   font-size: 0.7rem; | ||||||
|   | |||||||
| @@ -253,7 +253,7 @@ defineExpose({ open }) | |||||||
|   border-inline-start: 2px solid var(--border-color); |   border-inline-start: 2px solid var(--border-color); | ||||||
| } | } | ||||||
| select { | select { | ||||||
|   border: 1px solid var(--muted); |   border: .1rem solid var(--muted); | ||||||
|   background: var(--panel-alt, transparent); |   background: var(--panel-alt, transparent); | ||||||
|   color: var(--ink); |   color: var(--ink); | ||||||
|   padding: 0.4rem 0.5rem; |   padding: 0.4rem 0.5rem; | ||||||
| @@ -291,7 +291,7 @@ select { | |||||||
|   gap: 0.5rem; |   gap: 0.5rem; | ||||||
| } | } | ||||||
| .ec-btn { | .ec-btn { | ||||||
|   border: 1px solid var(--muted); |   border: .1rem solid var(--muted); | ||||||
|   background: transparent; |   background: transparent; | ||||||
|   color: var(--ink); |   color: var(--ink); | ||||||
|   padding: 0.5rem 0.8rem; |   padding: 0.5rem 0.8rem; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Leo Vasanko
					Leo Vasanko