fix(mobile): Zoom blocken, Long-Press, KW-Bubble, Swipe-Nav, Safe-Area
- Viewport: maximum-scale=1, user-scalable=no — kein Pinch-Zoom mehr - Profil-Dropdown öffnet wieder: overflow:hidden auf .topbar-right in der Mobile-Media-Query entfernt (hatte das absolut positionierte Dropdown abgeschnitten) - Long-Press auf Kalenderzellen markiert keinen Text mehr: user-select/touch-callout/tap-highlight in der ganzen Mobile-UI aus - Long-Press auf Avatar zeigt nicht "Bild speichern": -webkit-touch-callout:none + pointer-events:none auf <img> - Kalenderwochen erscheinen als kleine Bubble oben links in jeder Zeile statt als eigene 38px-Spalte - Status-Bar-Overlap im Settings-Modal behoben: safe-area-inset-top auf .settings-page-header und Modal-Header in der Mobile-Media-Query - Swipe links/rechts auf #view-container navigiert prev/next (≥60 px, überwiegend horizontal, < 700 ms) - Version v3 → v4 (auch SW-Cache)
This commit is contained in:
@@ -1270,7 +1270,7 @@ a { color: var(--primary); text-decoration: none; }
|
||||
}
|
||||
.topbar-right {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
/* no overflow:hidden — would clip the user dropdown on tap */
|
||||
}
|
||||
.view-switcher {
|
||||
overflow-x: auto;
|
||||
@@ -1346,6 +1346,64 @@ a { color: var(--primary); text-decoration: none; }
|
||||
/* ── Misc safety: prevent overflow on flex topbar items ──── */
|
||||
.main-view { width: 100%; min-width: 0; }
|
||||
#view-container { max-width: 100%; overflow-x: hidden; }
|
||||
|
||||
/* ── Long-press / text-selection / image-save fixes ──────── */
|
||||
/* Calendar UI shouldn't be selectable on touch — long-press
|
||||
should reach our handlers, not trigger iOS text selection. */
|
||||
.topbar, .sidebar, .main-view,
|
||||
.month-view, .month-row, .month-col, .cell-day, .month-events-overlay, .month-span-event,
|
||||
.week-view, .day-view, .week-day-col, .week-day-header, .week-allday-row,
|
||||
.quarter-view, .agenda-view,
|
||||
.view-switcher, .view-btn, .btn, .icon-btn, .user-avatar, .user-dropdown, .dropdown-item {
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
/* Faster taps, no double-tap zoom on interactive elements */
|
||||
.icon-btn, .btn, .view-btn, .user-avatar, .month-col, .week-day-col, .dropdown-item,
|
||||
.sidebar-copyright, .impressum-link, .modal-close, [data-modal] {
|
||||
touch-action: manipulation;
|
||||
}
|
||||
/* Avatar long-press: don't show "Save Image" — taps reach parent */
|
||||
.user-avatar img {
|
||||
-webkit-touch-callout: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ── Calendar weeks (KW) shown as a small bubble, not a column ── */
|
||||
.month-header {
|
||||
grid-template-columns: repeat(7, 1fr) !important;
|
||||
}
|
||||
.month-kw-header { display: none !important; }
|
||||
.month-row-right {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
.month-kw-cell {
|
||||
position: absolute;
|
||||
left: 3px; top: 3px;
|
||||
width: auto; height: auto;
|
||||
bottom: auto;
|
||||
padding: 1px 7px;
|
||||
background: var(--bg-active);
|
||||
border: none !important;
|
||||
border-radius: 10px;
|
||||
font-size: 10px; font-weight: 600;
|
||||
color: var(--text-2);
|
||||
z-index: 5;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ── Status-bar safe area inside full-screen modals (PWA) ── */
|
||||
.modal-card .modal-header,
|
||||
.settings-page-header {
|
||||
padding-top: calc(16px + env(safe-area-inset-top, 0px));
|
||||
}
|
||||
.modal-footer,
|
||||
.settings-page-body {
|
||||
padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
|
||||
}
|
||||
}
|
||||
|
||||
/* iOS notch / home-indicator safe areas (PWA standalone) */
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||
<!-- APP_VERSION: update here + version.js on every release -->
|
||||
<title>Calendarr v3</title>
|
||||
<title>Calendarr v4</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="theme-color" content="#4285f4" />
|
||||
@@ -77,7 +77,7 @@
|
||||
<button type="submit" class="btn btn-primary btn-full">Anmelden</button>
|
||||
</form>
|
||||
</div>
|
||||
<button class="impressum-link" onclick="openImpressum()">© 2026 Scarriffleservices · v3</button>
|
||||
<button class="impressum-link" onclick="openImpressum()">© 2026 Scarriffleservices · v4</button>
|
||||
</div>
|
||||
|
||||
<!-- ─── MAIN APP ──────────────────────────────────────────── -->
|
||||
@@ -179,7 +179,7 @@
|
||||
<div id="cal-list-items"></div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="sidebar-copyright" onclick="openImpressum()">© 2026 Scarriffleservices · v3</button>
|
||||
<button class="sidebar-copyright" onclick="openImpressum()">© 2026 Scarriffleservices · v4</button>
|
||||
</aside>
|
||||
<div id="sidebar-backdrop" class="sidebar-backdrop"></div>
|
||||
|
||||
@@ -841,7 +841,7 @@
|
||||
<a href="mailto:scarriffleservices@gmail.com">scarriffleservices@gmail.com</a></p>
|
||||
</div>
|
||||
<div class="modal-footer" style="justify-content:space-between;align-items:center">
|
||||
<span style="font-size:12px;color:var(--text-3)">Calendarr v3</span>
|
||||
<span style="font-size:12px;color:var(--text-3)">Calendarr v4</span>
|
||||
<button class="btn btn-ghost" onclick="closeImpressum()">Schliessen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -76,6 +76,7 @@ export async function initCalendar() {
|
||||
bindHAAccountModal();
|
||||
bindSettingsModal();
|
||||
bindProfileModal();
|
||||
bindSwipeNavigation();
|
||||
handleHAOAuthReturn();
|
||||
}
|
||||
|
||||
@@ -755,6 +756,33 @@ function renderCalendarList() {
|
||||
});
|
||||
}
|
||||
|
||||
// ── Swipe navigation (mobile) ─────────────────────────────
|
||||
function bindSwipeNavigation() {
|
||||
const container = document.getElementById('view-container');
|
||||
if (!container) return;
|
||||
let startX = 0, startY = 0, startT = 0, active = false;
|
||||
container.addEventListener('touchstart', e => {
|
||||
if (e.touches.length !== 1) { active = false; return; }
|
||||
startX = e.touches[0].clientX;
|
||||
startY = e.touches[0].clientY;
|
||||
startT = Date.now();
|
||||
active = true;
|
||||
}, { passive: true });
|
||||
container.addEventListener('touchend', e => {
|
||||
if (!active) return;
|
||||
active = false;
|
||||
const t = e.changedTouches[0];
|
||||
const dx = t.clientX - startX;
|
||||
const dy = t.clientY - startY;
|
||||
const dt = Date.now() - startT;
|
||||
// Horizontal swipe: ≥ 60px, mostly horizontal, faster than 700ms
|
||||
if (Math.abs(dx) > 60 && Math.abs(dx) > Math.abs(dy) * 1.5 && dt < 700) {
|
||||
navigate(dx < 0 ? 1 : -1);
|
||||
fetchAndRender();
|
||||
}
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// ── Navigation ────────────────────────────────────────────
|
||||
function navigate(dir) {
|
||||
const d = state.currentDate;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Increment APP_VERSION with every code change
|
||||
export const APP_VERSION = 'v3';
|
||||
export const APP_VERSION = 'v4';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Calendarr Service Worker
|
||||
// Cache-first for static assets, network-first for /api/* (graceful offline)
|
||||
|
||||
const CACHE_VERSION = 'calendarr-v3';
|
||||
const CACHE_VERSION = 'calendarr-v4';
|
||||
const STATIC_ASSETS = [
|
||||
'/',
|
||||
'/index.html',
|
||||
|
||||
Reference in New Issue
Block a user