Fix pull-to-refresh on touch devices, add drag/paste receipt upload

The pull-to-refresh gesture logic was already correct (verified by driving
real pointer events through it); what was missing was overscroll-behavior
on the scroll container, so on real touch hardware the browser's own
native overscroll could take the gesture over before it hit the threshold.

"Beleg hochladen" now opens a dialog instead of the native file picker:
drag a PDF in at the computer, paste it from the clipboard on the phone
(copied out of a mail), file picker kept as a third route. All three
sources share one upload path.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-08-17 00:29:05 +02:00
co-authored by Claude Sonnet 5
parent 8a751fe441
commit 5aaf1cd958
9 changed files with 374 additions and 55 deletions
@@ -83,6 +83,12 @@
height: min(880px, calc(100vh - 24px));
height: min(880px, calc(100dvh - 24px));
min-height: 0;
/* Verhindert, dass eine Zieh-Geste, die main#view unten nach oben hin
verlaesst, an die dahinterliegende HA-Seite weitergereicht wird - sonst
kann der Browser selbst ein natives Overscroll/Pull-to-Refresh starten
und der eigene Ziehen-zum-Aktualisieren-Indikator bekommt die Geste nie
zu Ende. */
overscroll-behavior: none;
}
/* ------------------------------------------------------------- Gerüst */
@@ -226,6 +232,12 @@ main#view {
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
/* Ohne das hier greift auf echten Touch-Geraeten oft das native
Overscroll/Pull-to-Refresh des Browsers, sobald am oberen Rand weiter
nach unten gezogen wird - das nimmt der eigenen "Ziehen zum
Aktualisieren"-Geste (siehe ptrY0-Logik in ereignisseVerdrahten()) die
Kontrolle weg, obwohl deren JS/CSS für sich genommen korrekt arbeitet. */
overscroll-behavior-y: contain;
}
main#view::-webkit-scrollbar { width: 0; height: 0; }
@@ -915,6 +927,34 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
padding: var(--sp-4);
}
/* Beleg-Fenster: PDF hineinziehen (Rechner) oder einfügen (Telefon). */
.beleg-catcher {
position: absolute; inset: 0; z-index: 1000;
background: rgba(0,0,0,.45);
animation: sheet-fade .18s ease-out;
}
.beleg-popup {
position: absolute; z-index: 1001;
left: var(--sp-5); right: var(--sp-5);
top: 50%; transform: translateY(-50%);
/* Wie .setup-popup: --tile-2 ist im Nacht-Theme transluzent, ein frei
schwebendes Fenster braucht einen blickdichten Grund. */
background: var(--tile-deckend, var(--canvas));
border-radius: var(--r-tile);
padding: var(--sp-4);
}
.beleg-drop {
margin-top: var(--sp-3);
border: 1px dashed var(--line-strong);
border-radius: 14px;
padding: 28px var(--sp-4);
text-align: center;
display: flex; flex-direction: column; gap: 6px;
}
.beleg-drop strong { font-size: 15px; color: var(--fg); }
.beleg-drop span { font-size: 12.5px; color: var(--fg2); }
.beleg-drop.darueber { border-color: var(--fg); border-style: solid; background: var(--shade); }
/* Kopfzeile der Reifen-Kachel: Überschrift+km links, Radfoto rechts, beide
oben bündig - ersetzt die frühere Überlagerung (Montiert absolut über dem
Foto), seit Überschrift und km ihren eigenen Platz über Montiert bekommen