Eine Zahl, die man nicht aufschluesseln kann, muss man glauben. Genau das war der Streitpunkt: Die Karte nannte fuer Lebensmittel einen Betrag, und es gab keine Moeglichkeit nachzusehen, welche Zahlungen darin einbezogen sind und welche nicht. Jetzt oeffnet ein Klick auf einen Anteil - in der Liste wie in der Legende des Kreises - die Buchungen dahinter: Datum, Empfaenger, Konto, Betrag. Die Liste kommt aus derselben Stelle, die auch die Summe bildet (`budget.expense_rows`), sie kann also gar nicht etwas anderes zeigen als das, was gezaehlt wurde. Ein Test haelt das fest. Steht eine Buchung darin, obwohl ihr Datum ausserhalb des Zeitraums liegt, traegt sie die Kalendermarke: Dann wurde ihr Budget-Monat von Hand gesetzt, und das ist der Grund. Nebenbei: Dialoge haengen jetzt am Seitenkoerper. Kacheln der Uebersicht werden per `transform` positioniert - damit war die Kachel der Bezugsrahmen fuer `position: fixed`, und der Dialog sass eingeklemmt darin statt ueber der Seite. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
40 lines
1.7 KiB
React
40 lines
1.7 KiB
React
import { useRef } from "react";
|
||
import { createPortal } from "react-dom";
|
||
|
||
import Icon from "./Icon";
|
||
|
||
// Einfacher, gestalteter Dialog (nutzt die modal-Klassen der Vorlage).
|
||
export default function Modal({ title, onClose, children, footer, wide = false }) {
|
||
// Nur schliessen, wenn der Klick auch AUF dem Hintergrund begonnen hat.
|
||
// Markiert man Text in einem Feld und zieht dabei über den Rand hinaus,
|
||
// liegt das Mouseup auf dem Hintergrund; der Klick feuert dann auf dem
|
||
// gemeinsamen Elternelement und schloss den Dialog mitten im Markieren.
|
||
const startAussen = useRef(false);
|
||
// An den Seitenkoerper, nicht an die Stelle im Baum: Steht der Aufrufer in
|
||
// einem Element mit `transform` – jede Kachel der Uebersicht tut das –, waere
|
||
// das sonst der Bezugsrahmen fuer `position: fixed`, und der Dialog sitzt
|
||
// eingeklemmt in der Kachel statt ueber der Seite.
|
||
return createPortal(
|
||
<div
|
||
className="modal-backdrop"
|
||
onMouseDown={(e) => { startAussen.current = e.target === e.currentTarget; }}
|
||
onClick={(e) => {
|
||
if (e.target === e.currentTarget && startAussen.current) onClose();
|
||
startAussen.current = false;
|
||
}}
|
||
>
|
||
<div className="modal" role="dialog" aria-modal="true" style={{ maxWidth: wide ? 640 : 480 }}>
|
||
<div className="modal-head">
|
||
<h2 style={{ margin: 0 }}>{title}</h2>
|
||
<button className="btn-icon" style={{ marginLeft: "auto" }} onClick={onClose} title="Schließen">
|
||
<Icon name="close" size={18} />
|
||
</button>
|
||
</div>
|
||
<div className="modal-body">{children}</div>
|
||
{footer && <div className="btn-pair">{footer}</div>}
|
||
</div>
|
||
</div>,
|
||
document.body,
|
||
);
|
||
}
|