Files
DOSSIER-STANDALONE/src/ui/Toast.tsx
T
karim 35299307d6 Akkumulierten grünen Arbeitsstand landen (Basis für Weiterarbeit)
Bündelt den über mehrere Sessions gewachsenen, uncommitteten Stand in
einem Basis-Commit, damit Folge-Features isoliert darauf aufsetzen.
Verifikation: tsc --noEmit sauber, vitest 600/600 grün.

Enthalten (Details in PENDENZEN.md ✅-Liste / HANDOVER.md):
- truck-Integration: Profil-Extrusion + Verjüngung + Boolean-CSG (csgrs),
  Crate src-tauri/trucksolid, Werkzeug `extrude`, ExtrudedSolid-Modell.
- kernel2d-Port nach Rust/WASM (Phasen 1–5, Diff-Harness).
- render3d 3D-Live-Schnitt = 2D-Schnitt: geschichteter Bodenaufbau,
  Prioritäts-Verschneidung (section_boolean.rs), einstellbare
  Schichttrennlinien, per-Hatch-Strichstärke, relativeToWall-Orientierung.
- Interop-Export IFC4/STL/OBJ (Loch-Ausschnitt wallMeshCut), Schnellexport.
- Projektdatei .obp + OS-Lock (lock.rs, LockConflictDialog).
- Layout-Blätter (Modell/Editor/Panel/PDF), Ausschnitte, Override-Engine,
  Tragwerk-Stützen (Column), BIM-Tree-Panel.
- Bauteil-Typsystem (Tür/Fenster/Treppe-Typen), Betontreppe mit schräger
  Laufplatte, Text-/Textbox-Werkzeug, Mess-Werkzeug, 2D/3D-Griffe für
  Öffnungen/Treppen, Snap-Symbol-Restyle.
2026-07-09 00:57:29 +02:00

82 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Toast — nicht-blockierende Kurzmeldungen (Ersatz für `window.alert`, das im
// Tauri-WKWebView deaktiviert ist und lautlos nichts tut, siehe
// state/notifySlice.ts). Liest `notifications` aus dem Store und rendert
// einen gestapelten Container oben rechts; jede Meldung verschwindet
// automatisch nach ~4s oder per Klick sofort.
//
// Auto-Dismiss-Timer sitzt HIER (nicht in der Slice): jede gemountete Meldung
// bekommt so GENAU einen Timer, sauber an ihren Lebenszyklus gebunden (kein
// Doppel-Timer bei erneutem `notify()`-Aufruf mit identischer Nachricht).
//
// Bezeichner englisch, UI-Text/Kommentare deutsch (CONVENTIONS.md).
import { useEffect } from "react";
import { useStore } from "../state/appStore";
import { t } from "../i18n";
import type { NotifyKind } from "../state/notifySlice";
const AUTO_DISMISS_MS = 4000;
/** Top-level Toast-Stack — einmal in App.tsx gerendert. */
export function Toast() {
const notifications = useStore((s) => s.notifications);
const dismissNotify = useStore((s) => s.dismissNotify);
if (notifications.length === 0) return null;
return (
<div className="toast-stack" role="status" aria-live="polite">
{notifications.map((n) => (
<ToastItem
key={n.id}
id={n.id}
message={n.message}
kind={n.kind}
onDismiss={dismissNotify}
/>
))}
</div>
);
}
function ToastItem({
id,
message,
kind,
onDismiss,
}: {
id: string;
message: string;
kind: NotifyKind;
onDismiss: (id: string) => void;
}) {
// Auto-Dismiss: EIN Timer pro gemounteter Meldung, an ihre id gebunden.
useEffect(() => {
const timer = window.setTimeout(() => onDismiss(id), AUTO_DISMISS_MS);
return () => window.clearTimeout(timer);
// onDismiss ist referenzstabil (Store-Action) — bewusst nur auf `id` reagieren.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [id]);
return (
<div
className={`toast-item toast-${kind}`}
onClick={() => onDismiss(id)}
role="alert"
>
<span className="toast-message">{message}</span>
<button
type="button"
className="toast-close"
aria-label={t("dock.close")}
onClick={(e) => {
e.stopPropagation();
onDismiss(id);
}}
>
×
</button>
</div>
);
}