Responsive layout

This commit is contained in:
Leo Vasanko 2025-08-25 09:54:53 -06:00
parent dd8b231cbc
commit ff6657cbcc
4 changed files with 52 additions and 107 deletions

View File

@ -1,14 +1,9 @@
/* Layout variables */
:root {
/* Layout */
--row-h: 2.2em;
--week-w: 3em;
--week-w: 3rem;
--day-w: 1fr;
--row-h: 12vh;
--month-w: 4em;
--month-w: 2rem;
--row-h: 15vh;
}
/* Layout & typography */
* {
box-sizing: border-box;
}
@ -100,35 +95,10 @@ header {
#calendar-viewport::-webkit-scrollbar {
display: none;
}
.jogwheel-viewport,
#jogwheel-viewport {
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: var(--month-w);
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: none;
z-index: 20;
cursor: ns-resize;
}
.jogwheel-viewport::-webkit-scrollbar,
#jogwheel-viewport::-webkit-scrollbar {
display: none;
}
.jogwheel-content,
#jogwheel-content {
position: relative;
width: 100%;
}
.calendar-content,
#calendar-content {
position: relative;
}
/* Week row: label + 7-day grid + jogwheel column */
.week-row {
display: grid;

View File

@ -347,7 +347,7 @@ function createWeek(virtualWeek) {
text: `${getLocalizedMonthName(monthToLabel)} '${year}`,
month: monthToLabel,
weeksSpan: weeksSpan,
height: weeksSpan * rowHeight.value,
monthClass: monthAbbr[monthToLabel],
}
}
}
@ -651,18 +651,6 @@ function openSettings() {
// 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.
function shouldRotateMonth(label) {
if (!label) return false
// Rotate ONLY if any Latin script alphabetic character is present.
// Prefer Unicode script property when supported.
try {
if (/\p{Script=Latin}/u.test(label)) return true
} catch (e) {
// Fallback for environments lacking Unicode property escapes.
if (/[A-Za-z\u00C0-\u024F\u1E00-\u1EFF]/u.test(label)) return true
}
return false
}
// Keep roughly same visible date when first_day setting changes.
watch(
() => calendarStore.config.first_day,
@ -723,19 +711,6 @@ window.addEventListener('resize', () => {
@day-touchstart="handleDayTouchStart"
@event-click="handleEventClick"
/>
<!-- Month labels positioned absolutely -->
<div
v-for="week in visibleWeeks"
:key="`month-${week.virtualWeek}`"
class="month-name-label"
:class="{ 'no-rotate': !shouldRotateMonth(week.monthLabel?.text) }"
:style="{
top: week.top + 'px',
height: week.monthLabel?.height + 'px',
}"
>
<span>{{ week.monthLabel?.text }}</span>
</div>
</div>
</div>
<!-- Jogwheel as sibling to calendar-viewport -->
@ -796,34 +771,6 @@ header h1 {
width: 100%;
}
.month-name-label {
position: absolute;
right: 0;
width: 3rem; /* Match jogwheel width */
font-size: 2em;
font-weight: 700;
color: var(--muted);
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
z-index: 15;
overflow: visible;
}
.month-name-label > span {
display: inline-block;
white-space: nowrap;
writing-mode: vertical-rl;
text-orientation: mixed;
transform: rotate(180deg);
transform-origin: center;
}
.month-name-label.no-rotate > span {
transform: none;
}
.row-height-probe {
position: absolute;
visibility: hidden;

View File

@ -33,6 +33,16 @@ const handleDayTouchStart = (dateStr) => {
const handleEventClick = (payload) => {
emit('event-click', payload)
}
// Only apply upside-down rotation (bottomup) for Latin script month labels
function shouldRotateMonth(label) {
if (!label) return false
try {
return /\p{Script=Latin}/u.test(label)
} catch (e) {
return /[A-Za-z\u00C0-\u024F\u1E00-\u1EFF]/u.test(label)
}
}
</script>
<template>
@ -51,13 +61,24 @@ const handleEventClick = (payload) => {
/>
<EventOverlay :week="props.week" @event-click="handleEventClick" />
</div>
<!-- Month name label (only on first week containing the 1st of the month) -->
<div
v-if="props.week.monthLabel"
class="month-label"
:class="props.week.monthLabel.monthClass"
:style="{ height: `calc(var(--row-h) * ${props.week.monthLabel.weeksSpan})` }"
>
<span :class="{ bottomup: shouldRotateMonth(props.week.monthLabel.text) }">{{
props.week.monthLabel.text
}}</span>
</div>
</div>
</template>
<style scoped>
.week-row {
display: grid;
grid-template-columns: var(--week-w) repeat(7, 1fr) 3rem;
grid-template-columns: var(--week-w) repeat(7, 1fr) var(--month-w);
position: absolute;
height: var(--row-h);
width: 100%;
@ -70,15 +91,8 @@ const handleEventClick = (payload) => {
color: var(--muted);
font-size: 1.2em;
font-weight: 500;
/* Prevent text selection */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
}
.days-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
@ -86,10 +100,32 @@ const handleEventClick = (payload) => {
height: 100%;
width: 100%;
}
/* Fixed heights for cells and labels (from cells.css) */
.week-row :deep(.cell),
.week-label {
height: var(--row-h);
}
.month-label {
position: absolute;
top: 0;
right: 0;
width: var(--month-w);
background-image: linear-gradient(to bottom, rgba(186, 186, 186, 0.3), rgba(186, 186, 186, 0.2));
font-size: 2em;
font-weight: 700;
color: var(--muted);
display: flex;
align-items: center;
justify-content: center;
z-index: 15;
overflow: hidden;
}
.month-label > span {
display: inline-block;
white-space: nowrap;
writing-mode: vertical-rl;
text-orientation: mixed;
transform-origin: center;
}
.bottomup {
transform: rotate(180deg);
}
</style>

View File

@ -183,20 +183,12 @@ defineExpose({
top: 0;
right: 0;
bottom: 0;
width: 3rem; /* Use fixed width since minmax() doesn't work for absolute positioning */
width: var(--month-w);
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: none;
z-index: 20;
cursor: ns-resize;
background: rgba(0, 0, 0, 0.05); /* Temporary background to see the area */
/* Prevent text selection */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
}
.jogwheel-viewport::-webkit-scrollbar {