fix(mobile): Monatstitel sichtbar, KW-Bubble unten, Termine mit Text, Long-Press, Settings-Hamburger
- View-Switcher auf Mobile in Popup-Menü ausgelagert (neuer Icon-Button
rechts in der Topbar). Dadurch wird in der Topbar Platz frei für
prev/next + Monatstitel ("Mai 2026" usw.).
- Topbar-Settings-Icon auf Mobile ausgeblendet, dafür neuer
"Einstellungen"-Eintrag im User-Dropdown. "Heute" wandert ins
View-Popup.
- KW-Bubble: von oben-links nach unten-links verschoben — überlappt
jetzt nicht mehr die Tagesnummer.
- Termine in der Monatsansicht zeigen wieder ihren Text (kleinere
14px-Höhe, 9px Schrift) statt nur farbiger Punkte.
- Long-Press auf einen Tag öffnet das Kontextmenü "Termin erstellen"
(synthetisches contextmenu-Event nach 500 ms ohne Bewegung). Der
nachfolgende synthetische Click wird unterdrückt.
- Settings-Modal: Sidebar (Darstellung/Konten/Benutzerverwaltung) auf
Mobile als slide-in Overlay mit Hamburger-Toggle. Auf Desktop bleibt
sie immer sichtbar.
- Version v4 → v5 (auch SW-Cache)
This commit is contained in:
@@ -756,21 +756,57 @@ function renderCalendarList() {
|
||||
});
|
||||
}
|
||||
|
||||
// ── Swipe navigation (mobile) ─────────────────────────────
|
||||
// ── Swipe navigation + long-press → context menu (mobile) ──
|
||||
function bindSwipeNavigation() {
|
||||
const container = document.getElementById('view-container');
|
||||
if (!container) return;
|
||||
let startX = 0, startY = 0, startT = 0, active = false;
|
||||
let lpTimer = null, lpTarget = null, lpFired = 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;
|
||||
lpFired = false;
|
||||
|
||||
// Long-press → context menu (only on day cells, not on events)
|
||||
lpTarget = e.target.closest('.month-col, .week-day-col');
|
||||
if (lpTarget && !e.target.closest('.month-span-event, .week-event')) {
|
||||
lpTimer = setTimeout(() => {
|
||||
const t = e.touches[0];
|
||||
const ev = new MouseEvent('contextmenu', {
|
||||
bubbles: true, cancelable: true,
|
||||
clientX: t.clientX, clientY: t.clientY,
|
||||
});
|
||||
lpTarget.dispatchEvent(ev);
|
||||
lpFired = true;
|
||||
}, 500);
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
container.addEventListener('touchmove', e => {
|
||||
if (!active) return;
|
||||
const t = e.touches[0];
|
||||
if (Math.abs(t.clientX - startX) > 8 || Math.abs(t.clientY - startY) > 8) {
|
||||
if (lpTimer) { clearTimeout(lpTimer); lpTimer = null; }
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
container.addEventListener('touchend', e => {
|
||||
if (lpTimer) { clearTimeout(lpTimer); lpTimer = null; }
|
||||
if (!active) return;
|
||||
active = false;
|
||||
|
||||
// Suppress the click that follows a long-press
|
||||
if (lpFired) {
|
||||
const blocker = ev => { ev.stopPropagation(); ev.preventDefault(); };
|
||||
document.addEventListener('click', blocker, { capture: true, once: true });
|
||||
lpFired = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const t = e.changedTouches[0];
|
||||
const dx = t.clientX - startX;
|
||||
const dy = t.clientY - startY;
|
||||
@@ -781,6 +817,11 @@ function bindSwipeNavigation() {
|
||||
fetchAndRender();
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
container.addEventListener('touchcancel', () => {
|
||||
if (lpTimer) { clearTimeout(lpTimer); lpTimer = null; }
|
||||
active = false;
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// ── Navigation ────────────────────────────────────────────
|
||||
@@ -825,6 +866,59 @@ function bindTopbar() {
|
||||
document.getElementById('btn-settings').onclick = openSettingsModal;
|
||||
document.getElementById('btn-create-event').onclick = () => openNewEventModal(state.selectedDate || state.currentDate);
|
||||
|
||||
// Mobile view-toggle popup
|
||||
const viewMobileBtn = document.getElementById('btn-view-mobile');
|
||||
const viewMobileDropdown = document.getElementById('view-mobile-dropdown');
|
||||
if (viewMobileBtn && viewMobileDropdown) {
|
||||
viewMobileBtn.onclick = e => {
|
||||
e.stopPropagation();
|
||||
viewMobileDropdown.classList.toggle('hidden');
|
||||
};
|
||||
document.addEventListener('click', e => {
|
||||
if (!viewMobileDropdown.contains(e.target) && !viewMobileBtn.contains(e.target)) {
|
||||
viewMobileDropdown.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
viewMobileDropdown.querySelectorAll('[data-mobile-view]').forEach(btn => {
|
||||
btn.onclick = () => {
|
||||
state.currentView = btn.dataset.mobileView;
|
||||
updateViewButtons();
|
||||
fetchAndRender();
|
||||
viewMobileDropdown.classList.add('hidden');
|
||||
};
|
||||
});
|
||||
const todayMobile = document.getElementById('btn-today-mobile');
|
||||
if (todayMobile) todayMobile.onclick = () => {
|
||||
state.currentDate = new Date();
|
||||
fetchAndRender();
|
||||
viewMobileDropdown.classList.add('hidden');
|
||||
};
|
||||
}
|
||||
|
||||
// Settings entry inside the user dropdown (mobile)
|
||||
const settingsFromUser = document.getElementById('btn-settings-from-user');
|
||||
if (settingsFromUser) settingsFromUser.onclick = () => {
|
||||
document.getElementById('user-dropdown').classList.add('hidden');
|
||||
openSettingsModal();
|
||||
};
|
||||
|
||||
// Settings nav hamburger (only does something on mobile via CSS)
|
||||
const settingsNavToggle = document.getElementById('settings-nav-toggle');
|
||||
const settingsCard = document.querySelector('#modal-settings .settings-page-card');
|
||||
const settingsNavBackdrop = document.getElementById('settings-nav-backdrop');
|
||||
if (settingsNavToggle && settingsCard) {
|
||||
settingsNavToggle.onclick = () => settingsCard.classList.toggle('nav-open');
|
||||
}
|
||||
if (settingsNavBackdrop && settingsCard) {
|
||||
settingsNavBackdrop.onclick = () => settingsCard.classList.remove('nav-open');
|
||||
}
|
||||
// After picking a section in the nav, close the overlay (mobile UX)
|
||||
document.querySelectorAll('.settings-nav-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
if (settingsCard) settingsCard.classList.remove('nav-open');
|
||||
});
|
||||
});
|
||||
|
||||
// Mouse wheel / trackpad scroll navigation – only for month & quarter
|
||||
let _wheelLast = 0;
|
||||
document.getElementById('view-container').addEventListener('wheel', e => {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Increment APP_VERSION with every code change
|
||||
export const APP_VERSION = 'v4';
|
||||
export const APP_VERSION = 'v5';
|
||||
|
||||
Reference in New Issue
Block a user