Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5bfd1be3d | ||
|
|
163ab88c35 |
No files matched your search
+41
-15
@@ -1379,6 +1379,22 @@ function onAppViewShown(name) {
|
||||
updateAppBar()
|
||||
}
|
||||
|
||||
/* Chat-Eingabe ueber der Tastatur halten: nur nachschieben, wenn sie wirklich verdeckt ist, gemessen am
|
||||
sichtbaren Bereich (visualViewport). Die Seite nicht nach oben pinnen – das kaempft auf iOS gegen
|
||||
Safaris eigenes Hochschieben und laesst die Eingabe aus dem Bild springen. */
|
||||
function revealChatComposer() {
|
||||
const composer = document.getElementById('chat-composer')
|
||||
const vv = window.visualViewport
|
||||
if (!composer || !vv || !isAppLayout() || !composer.contains(document.activeElement)) return
|
||||
const rect = composer.getBoundingClientRect()
|
||||
const visibleTop = vv.offsetTop
|
||||
const visibleBottom = vv.offsetTop + vv.height
|
||||
let delta = 0
|
||||
if (rect.bottom > visibleBottom - 8) delta = rect.bottom - visibleBottom + 8
|
||||
else if (rect.top < visibleTop + 8) delta = rect.top - visibleTop - 8
|
||||
if (Number.isFinite(delta) && Math.abs(delta) > 1) window.scrollBy(0, delta)
|
||||
}
|
||||
|
||||
/* Wartungs-Tab: Hinweis bei fehlgeschlagener Zahlung oder offener Rückfrage zur Gutschrift */
|
||||
function updateWartungBadge() {
|
||||
const card = document.getElementById('abo-card')
|
||||
@@ -1563,9 +1579,16 @@ function initAppShell() {
|
||||
})
|
||||
}, { passive: true })
|
||||
|
||||
// Tastatur offen (Touch): Tab-Leiste ausblenden, damit Eingabe und Chat Platz haben
|
||||
// Tastatur offen (Touch): Tab-Leiste ausblenden. Auf Android schrumpft die Seite mit der Tastatur
|
||||
// (interactive-widget=resizes-content), dort nutzt der Chat den frei werdenden Platz. Auf iOS liegt
|
||||
// die Tastatur ueber der Seite und Safari schiebt das Feld selbst hoch – dort darf sich beim Fokus
|
||||
// keine Hoehe aendern (kb-ios, siehe kundenbereich.css), sonst rutscht das Feld wieder aus dem Bild.
|
||||
if (isIosDevice()) document.body.classList.add('kb-ios')
|
||||
document.addEventListener('focusin', (ev) => {
|
||||
if (coarsePointer.matches && ev.target.matches?.(TEXT_INPUT)) document.body.classList.add('kb-typing')
|
||||
if (!coarsePointer.matches || !ev.target.matches?.(TEXT_INPUT)) return
|
||||
document.body.classList.add('kb-typing')
|
||||
// Nach der Tastatur-Animation (iOS ca. 350 ms) pruefen, ob die Chat-Eingabe sichtbar ist
|
||||
if (ev.target.closest('#chat-composer')) setTimeout(revealChatComposer, 350)
|
||||
})
|
||||
document.addEventListener('focusout', () => {
|
||||
setTimeout(() => {
|
||||
@@ -1573,7 +1596,6 @@ function initAppShell() {
|
||||
}, 80)
|
||||
})
|
||||
|
||||
// Sichtbare Hoehe (ohne Tastatur) fuer den Chat; neueste Nachricht bleibt sichtbar.
|
||||
// Schliesst die Tastatur, ohne dass das Feld den Fokus verliert (Android-Zurueck-Geste),
|
||||
// kommt die Tab-Leiste zurueck; tippt man erneut hinein, verschwindet sie wieder.
|
||||
const vv = window.visualViewport
|
||||
@@ -1581,8 +1603,7 @@ function initAppShell() {
|
||||
let fullHeight = vv.height
|
||||
let width = vv.width
|
||||
let keyboardOpen = false
|
||||
const syncHeight = () => {
|
||||
document.documentElement.style.setProperty('--kb-vvh', `${Math.round(vv.height)}px`)
|
||||
vv.addEventListener('resize', () => {
|
||||
if (Math.abs(vv.width - width) > 1) {
|
||||
width = vv.width
|
||||
fullHeight = vv.height
|
||||
@@ -1594,20 +1615,15 @@ function initAppShell() {
|
||||
if (!open) document.body.classList.remove('kb-typing')
|
||||
else if (coarsePointer.matches && document.activeElement?.matches?.(TEXT_INPUT)) document.body.classList.add('kb-typing')
|
||||
}
|
||||
// Chat mit Tastatur: iOS schiebt die Seite hoch; App-Leiste, Verlauf und Eingabe passen genau hinein
|
||||
if (keyboardOpen && appShell.view === 'chat' && isAppLayout()) window.scrollTo(0, 0)
|
||||
}
|
||||
vv.addEventListener('resize', syncHeight)
|
||||
syncHeight()
|
||||
if (open) requestAnimationFrame(() => requestAnimationFrame(revealChatComposer))
|
||||
})
|
||||
}
|
||||
// Wird der Verlauf kleiner (Tastatur, Tab-Leiste), bleibt die neueste Nachricht sichtbar – sofern
|
||||
// man unten war (data-stick setzt ChatComposer.trackScroll)
|
||||
const messages = document.getElementById('chat-messages')
|
||||
if (messages && typeof ResizeObserver !== 'undefined') {
|
||||
let atBottom = true
|
||||
messages.addEventListener('scroll', () => {
|
||||
atBottom = messages.scrollHeight - messages.scrollTop - messages.clientHeight < 48
|
||||
}, { passive: true })
|
||||
new ResizeObserver(() => {
|
||||
if (atBottom) messages.scrollTop = messages.scrollHeight
|
||||
if (messages.dataset.stick !== 'false') messages.scrollTop = messages.scrollHeight
|
||||
}).observe(messages)
|
||||
}
|
||||
|
||||
@@ -1652,6 +1668,16 @@ function initAppShell() {
|
||||
if (isAppLayout() && getQueryParam('abo') === 'success') showView('wartung')
|
||||
}
|
||||
|
||||
// iOS zoomt beim Tippen in ein Feld hinein, wenn dessen Schrift effektiv unter 16 px liegt – z. B. bei
|
||||
// verkleinertem Seitenzoom in Safari, obwohl die Felder 16 px haben. maximum-scale=1 verhindert nur
|
||||
// diesen automatischen Zoom; mit zwei Fingern zoomen bleibt auf iOS moeglich (WebKit ignoriert die
|
||||
// Sperre dafuer seit iOS 10). Nur iOS: Android wuerde damit jedes Zoomen sperren.
|
||||
function preventIosFocusZoom() {
|
||||
const meta = document.querySelector('meta[name="viewport"]')
|
||||
if (meta && isIosDevice() && !/maximum-scale/.test(meta.content)) meta.content += ', maximum-scale=1'
|
||||
}
|
||||
preventIosFocusZoom()
|
||||
|
||||
// App-Installation (Homescreen) und Offline-Seite; zwischengespeichert wird nur die Offline-Seite (sw.js)
|
||||
if ('serviceWorker' in navigator && window.isSecureContext) {
|
||||
window.addEventListener('load', () => {
|
||||
|
||||
+13
-14
@@ -441,7 +441,8 @@ button.kb-avatar:disabled { cursor: default; box-shadow: none; }
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.kb-page.kb-typing { --kb-tabbar-h: 0px; }
|
||||
/* Tastatur offen: auf Android wird der Platz der Tab-Leiste frei; auf iOS keine Hoehenaenderung (app.js) */
|
||||
.kb-page.kb-typing:not(.kb-ios) { --kb-tabbar-h: 0px; }
|
||||
html.kb-sheet-open { overflow: hidden; }
|
||||
.kb-desk-only { display: none; }
|
||||
.kb-app-only { display: inline; }
|
||||
@@ -588,22 +589,19 @@ button.kb-avatar:disabled { cursor: default; box-shadow: none; }
|
||||
.kb-invoices .inv-actions .btn-link:first-child { background: rgba(157, 61, 254, 0.18); border-color: var(--kb-purple-line); }
|
||||
.kb-invoices .inv-actions .btn-link:first-child:not(:last-child) { flex: 1.7; }
|
||||
|
||||
/* Chat: fuellt den Platz zwischen App-Leiste und Tab-Leiste (bzw. Tastatur) */
|
||||
/* Chat: fuellt den Platz zwischen App-Leiste und Tab-Leiste. 100dvh folgt auf Android der Tastatur
|
||||
(interactive-widget=resizes-content); auf iOS bleibt die Hoehe, Safari schiebt das Feld selbst hoch */
|
||||
#view-chat.active {
|
||||
display: flex; flex-direction: column;
|
||||
height: calc(var(--kb-vvh, 100dvh) - var(--kb-appbar-h) - var(--kb-tabbar-h) - 20px);
|
||||
height: calc(100vh - var(--kb-appbar-h) - var(--kb-tabbar-h) - 20px);
|
||||
height: calc(100dvh - var(--kb-appbar-h) - var(--kb-tabbar-h) - 20px);
|
||||
}
|
||||
#view-chat .kb-head { flex: none; margin-bottom: 12px; }
|
||||
#view-chat .chat-box { flex: 1 1 auto; min-height: 0; height: auto; padding: 10px; border-radius: 20px; }
|
||||
.chat-messages { overscroll-behavior: contain; padding: 4px 2px; }
|
||||
.chat-bubble { max-width: 84%; font-size: 15px; }
|
||||
.chat-input-row { gap: 8px; margin-top: 10px; }
|
||||
/* Eingabe des Chat-Composers als Pille, passend zu dessen runden Knoepfen (Senden-Knopf: chat-composer.css) */
|
||||
.chat-input-row textarea { box-sizing: border-box; min-height: 46px; padding: 11px 16px; border-radius: 23px; font-size: 16px; line-height: 1.35; }
|
||||
/* Senden als runder Pfeil-Button; der Text bleibt fuer Screenreader erhalten */
|
||||
.chat-send {
|
||||
flex: none; width: 46px; height: 46px; padding: 0; border-radius: 50%; font-size: 0; color: transparent;
|
||||
background: var(--kb-purple) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 19V5M5 12l7-7 7 7'/%3E%3C/svg%3E") center / 22px no-repeat;
|
||||
}
|
||||
|
||||
/* Tab "Wartung": Abo-Karte gross; darunter Unterseiten Stundenkonto/Bestellung und Gutschrift */
|
||||
#kb-abo-slot:has(> .kb-abo:not(.hidden)) > .kb-empty { display: none; }
|
||||
@@ -636,7 +634,8 @@ button.kb-avatar:disabled { cursor: default; box-shadow: none; }
|
||||
.kb-sheet { inset: auto 0 0 0; width: 100%; max-width: none; margin: 0; }
|
||||
.kb-sheet::backdrop { background: rgba(0, 0, 0, 0.55); }
|
||||
.kb-sheet-panel {
|
||||
max-height: calc(var(--kb-vvh, 100dvh) - env(safe-area-inset-top) - 24px); overscroll-behavior: contain;
|
||||
max-height: calc(100vh - env(safe-area-inset-top) - 24px); max-height: calc(100dvh - env(safe-area-inset-top) - 24px);
|
||||
overscroll-behavior: contain;
|
||||
padding: 8px max(18px, env(safe-area-inset-right)) calc(18px + env(safe-area-inset-bottom)) max(18px, env(safe-area-inset-left));
|
||||
border-radius: 24px 24px 0 0; border-bottom: none; box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.5);
|
||||
transition: transform 0.24s ease-in;
|
||||
@@ -649,8 +648,9 @@ button.kb-avatar:disabled { cursor: default; box-shadow: none; }
|
||||
.kb-sheet-links a, .kb-sheet-logout { padding-top: 14px; padding-bottom: 14px; }
|
||||
}
|
||||
|
||||
/* Touch: Tastatur-Hinweise zum Chat braucht es nicht */
|
||||
@media (pointer: coarse) {
|
||||
/* App-Ansicht mit Touch: der Hinweis unter dem Chat (Enter-Taste, Anhaenge) kostet zu viel Hoehe,
|
||||
Bueroklammer und Mikrofon sprechen fuer sich */
|
||||
@media (max-width: 880px) and (pointer: coarse), (pointer: coarse) and (max-height: 500px) {
|
||||
.chat-hint { display: none; }
|
||||
}
|
||||
|
||||
@@ -663,9 +663,8 @@ button.kb-avatar:disabled { cursor: default; box-shadow: none; }
|
||||
.kb-nav-item.active { color: #d4b4ff; }
|
||||
.kb-tab-ico { margin-top: 0; }
|
||||
.kb-nav-item .kb-badge { position: static; margin-left: -2px; }
|
||||
/* Chat: Ueberschrift weglassen (steht dann in der App-Leiste), einzeilige Eingabe */
|
||||
/* Chat: Ueberschrift weglassen, sie steht dann in der App-Leiste */
|
||||
#view-chat .kb-head { display: none; }
|
||||
.chat-input-row textarea { height: 46px; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
||||
Reference in new issue
Block a user