Major new version #2
@ -1,14 +1,9 @@
|
|||||||
/* Layout variables */
|
|
||||||
:root {
|
:root {
|
||||||
/* Layout */
|
--week-w: 3rem;
|
||||||
--row-h: 2.2em;
|
|
||||||
--week-w: 3em;
|
|
||||||
--day-w: 1fr;
|
--day-w: 1fr;
|
||||||
--row-h: 12vh;
|
--month-w: 2rem;
|
||||||
--month-w: 4em;
|
--row-h: 15vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Layout & typography */
|
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
@ -100,35 +95,10 @@ header {
|
|||||||
#calendar-viewport::-webkit-scrollbar {
|
#calendar-viewport::-webkit-scrollbar {
|
||||||
display: none;
|
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,
|
||||||
#calendar-content {
|
#calendar-content {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Week row: label + 7-day grid + jogwheel column */
|
/* Week row: label + 7-day grid + jogwheel column */
|
||||||
.week-row {
|
.week-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -347,7 +347,7 @@ function createWeek(virtualWeek) {
|
|||||||
text: `${getLocalizedMonthName(monthToLabel)} '${year}`,
|
text: `${getLocalizedMonthName(monthToLabel)} '${year}`,
|
||||||
month: monthToLabel,
|
month: monthToLabel,
|
||||||
weeksSpan: weeksSpan,
|
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.
|
// 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.
|
||||||
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.
|
// Keep roughly same visible date when first_day setting changes.
|
||||||
watch(
|
watch(
|
||||||
() => calendarStore.config.first_day,
|
() => calendarStore.config.first_day,
|
||||||
@ -723,19 +711,6 @@ window.addEventListener('resize', () => {
|
|||||||
@day-touchstart="handleDayTouchStart"
|
@day-touchstart="handleDayTouchStart"
|
||||||
@event-click="handleEventClick"
|
@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>
|
||||||
</div>
|
</div>
|
||||||
<!-- Jogwheel as sibling to calendar-viewport -->
|
<!-- Jogwheel as sibling to calendar-viewport -->
|
||||||
@ -796,34 +771,6 @@ header h1 {
|
|||||||
width: 100%;
|
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 {
|
.row-height-probe {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
|
@ -33,6 +33,16 @@ const handleDayTouchStart = (dateStr) => {
|
|||||||
const handleEventClick = (payload) => {
|
const handleEventClick = (payload) => {
|
||||||
emit('event-click', 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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -51,13 +61,24 @@ const handleEventClick = (payload) => {
|
|||||||
/>
|
/>
|
||||||
<EventOverlay :week="props.week" @event-click="handleEventClick" />
|
<EventOverlay :week="props.week" @event-click="handleEventClick" />
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.week-row {
|
.week-row {
|
||||||
display: grid;
|
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;
|
position: absolute;
|
||||||
height: var(--row-h);
|
height: var(--row-h);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -70,15 +91,8 @@ const handleEventClick = (payload) => {
|
|||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
/* Prevent text selection */
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-moz-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
-webkit-touch-callout: none;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.days-grid {
|
.days-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(7, 1fr);
|
grid-template-columns: repeat(7, 1fr);
|
||||||
@ -86,10 +100,32 @@ const handleEventClick = (payload) => {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fixed heights for cells and labels (from cells.css) */
|
|
||||||
.week-row :deep(.cell),
|
|
||||||
.week-label {
|
.week-label {
|
||||||
height: var(--row-h);
|
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>
|
</style>
|
||||||
|
@ -183,20 +183,12 @@ defineExpose({
|
|||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 3rem; /* Use fixed width since minmax() doesn't work for absolute positioning */
|
width: var(--month-w);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
cursor: ns-resize;
|
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 {
|
.jogwheel-viewport::-webkit-scrollbar {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user