Benachrichtigungen
Nutzen Sie den Notifier für Benachrichtigungen im Fenster, die die aktuelle Aufgabe nie blockieren — Erfolg, Fehler, Fortschritt, Rückgängig und Nachrichten —, wobei Stapeln, Aufklappen bei Hover und acht Andockpositionen für Sie übernommen werden.
Was der Notifier ist
ui.widgets.Notifier ist ein Dienst auf Fensterebene: einmal initialisieren, dann show von überall aufrufen. Die Karten liegen auf der Toast-Ebene des Overlay-Portals des Fensters (z 2000 — über Dialogen, unter Tooltips) und nehmen nie am Seitenlayout teil. Jede Karte besitzt einen festen Node, sodass das Entfernen einer Karte die anderen nie neu aufbaut oder ihren Auftritt erneut abspielt. Er ersetzt den früheren ToastManager.
Die API Feld für Feld (Notification, NotifierOptions und jede Methodensignatur) finden Sie auf der Notification-Seite der Komponenten-Website.
Einen Notifier erstellen
Erstellen Sie ihn einmal in Ihrer Root-Mount-Funktion und reichen Sie den Zeiger an alles weiter, was benachrichtigen muss (etwa App-State, der per cx.bindState gehalten wird). Der Notifier gehört dem übergebenen Scope und verschwindet mit ihm.
const notifier = try ui.widgets.Notifier.init(scope, cx, .{
.position = .bottom_center,
// Keep the stack clear of a 56px toolbar.
.content_insets = .{ .top = 56 },
.max_visible = 4,
});positionEine von acht Positionen, Standard .bottom_center; zur Laufzeit mit setPosition umschaltbarmax_visibleWie viele Karten aufgeklappt sichtbar sind (2–6, Standard 4)expand_on_hoverBei false pausiert Hover nur die Timer und klappt nicht aufcontent_insetsAbstände zu den Fensterrändern (Titelleiste, Toolbar, Statusleiste); der 22px-Andockabstand beginnt hierwidthKartenbreite, Standard 392stringsUI-Texte ("now", "Undo", "Reply…", das eingeklappte Badge …) für die LokalisierunglistenerCallback für Benutzeraktionen und Ablauf; auch per setListener setzbarEine Benachrichtigung anzeigen
show nimmt einen ui.widgets.Notification-Wert und gibt dessen id zurück. Der Notifier kopiert jeden String, Ihre Puffer sind also sofort wiederverwendbar. Ab mehr als sechs Karten auf dem Bildschirm verschwindet die älteste mit ihrer normalen Abgangsanimation.
// Success: auto-dismisses after 3.6 s.
_ = try notifier.show(.{
.tone = .success,
.title = "Saved to iCloud",
.body = "3 files synced · 2.1 MB",
});
// Error with two actions: errors stay until the user acts.
const failed = try notifier.show(.{
.tone = .@"error",
.title = "Upload failed",
.body = "network timeout · dist/app.zip",
.actions = &.{
.{ .label = "Retry", .primary = true, .tag = "retry" },
.{ .label = "View log", .tag = "log" },
},
});
// Undo: a 7 s countdown ring and a default "Undo" button.
_ = try notifier.show(.{
.kind = .undo,
.title = "Moved 4 items to the Trash",
.body = "You can undo this for 7 seconds.",
});
// A person's message with an inline reply box.
_ = try notifier.show(.{
.tone = .violet,
.avatar = "L",
.title = "Lin",
.body = "I finished the table widths in section three.",
.reply = true,
});Arten, Töne und Anzeigedauer
Das Aussehen einer Karte ergibt sich aus zwei Achsen: kind (Layout) und tone (semantische Farbe). Das vorangestellte Icon wird aus beiden abgeleitet oder explizit mit lead / glyph gesetzt.
.defaultIcon, Titel und Text, optional mit Buttons, Antwortfeld oder Avatar.progressSpinner, determinierter Balken und ein n / N-Zähler; kein Lebensdauerbalken.undoCountdown-Ring mit den verbleibenden Sekunden, dazu ein Standard-Button "Undo".successEtwas ist fertig3.6 s.@"error"Ein Fehler, der Aufmerksamkeit brauchtdauerhaft.warningBeachtenswert, nicht blockierend5.6 s.infoAllgemeine Information (Standard)4.2 s.violetPersonen, Einladungen und andere soziale Nachrichten4.2 s; mit Buttons dauerhaft.quietDezenter Status (ein kleiner Punkt statt eines Icons)2.8 s- ✓
Standardmäßig dauerhaft: progress-Karten, Karten mit Antwortfeld, der Ton error und violet-Karten mit Buttons schließen sich nie von selbst.
- ✓
Explizite Überschreibungen:
sticky = trueerzwingt Dauerhaftigkeit;duration_msersetzt die Standarddauer;life_barschaltet den Lebensdauerbalken um. - ✓
Undo dauert standardmäßig 7 Sekunden, der Ring folgt dem Timer.
Aktualisierung vor Ort und Fortschritt
update(id, …) schreibt Art, Ton, Titel und Text derselben Karte neu und startet ihre Lebensdauer neu — kein Zerstören, kein Neuaufbau, kein erneuter Auftritt. Treiben Sie eine Fortschrittskarte mit setProgress an, das nur die Fortschrittsebene berührt. Auch show mit einer vorhandenen id verwandelt die Karte vor Ort.
const id = try notifier.show(.{
.kind = .progress,
.title = "Uploading attachment",
.body = "assets/hero-shot.png",
.progress = .{ .value = 0, .label = "Uploading · about 5 s", .count = "0 / 5" },
});
// Each tick: only the progress layer is touched, the card isn't rebuilt.
notifier.setProgress(id, 0.6, "Uploading · about 2 s", "3 / 5");
// Done: the same card turns into a success card in place —
// no new entrance, and it now dismisses itself after 3.4 s.
try notifier.update(id, .{
.tone = .success,
.title = "Upload complete",
.body = "assets/hero-shot.png",
.duration_ms = 3400,
});Benutzeraktionen verarbeiten
Der Notifier meldet über einen einzigen ui.widgets.NotificationListener. Ein NotificationEvent enthält die Karten-id, die Event-kind und das tag / den index des Buttons oder den Antwort-text.
.actionEin Button wurde geklickt (per tag unterscheiden).undoUndo wurde geklickt; der Ring ist bereits zu einem ✓ geworden.replyDas Antwortfeld wurde abgeschickt (Enter oder Send); text enthält den Inhalt.dismissedDer Benutzer hat sie geschlossen (✕ oder Wegwischen).expiredSie ist abgelaufenconst Uploads = struct {
fn onEvent(ctx: ?*anyopaque, n: *ui.widgets.Notifier, e: ui.widgets.NotificationEvent) void {
const self: *Uploads = @ptrCast(@alignCast(ctx orelse return));
switch (e.kind) {
.action => if (std.mem.eql(u8, e.tag, "retry")) {
// Turn the sticky error card into a progress card in place.
n.update(e.id, .{
.kind = .progress,
.title = "Retrying upload",
.progress = .{ .value = 0, .label = "Uploading", .count = "0 / 5" },
}) catch return;
self.restart(e.id);
} else n.dismiss(e.id),
.reply => self.send(e.text),
.undo => self.restoreTrash(),
.dismissed, .expired => {},
}
}
// …restart / send / restoreTrash
};
const uploads = try cx.bindState(Uploads, .{});
notifier.setListener(.{ .context = @ptrCast(uploads), .callback = Uploads.onEvent });Stapeln und Hover
Mehrere Benachrichtigungen klappen zu einem Stapel zusammen: Die neueste liegt dem Anker am nächsten mit dem höchsten z; jede Ebene dahinter lugt 9px hervor und schrumpft um 3.8 %, bis zu drei Ebenen, mit einem Badge „N weitere · Hover zum Aufklappen“. Betritt der Zeiger die Stapelgrenzen (um 18px erweitert), klappt er mit 9px Abstand auf und alle Timer pausieren; beim Verlassen klappt er zu und die Timer laufen weiter.
Neben dem Badge sitzt ein Button zum Löschen aller Karten: Der erste Klick zeigt die Beschriftung, der zweite löscht tatsächlich, und beim Wegbewegen klappt er wieder ein. Im Code rufen Sie dismissAll() auf.
Eine einzelne Karte lässt sich wegwischen, was dismissed meldet.
Beim Ändern der Fenstergröße folgt der Stapel seinem Anker starr, ohne nachziehende Interpolation.
Positionen
setPosition ändert nur Anker, Wachstumsrichtung und Auftrittsversatz — Karten werden nicht neu aufgebaut, und eine Karte, die gerade abgeht, beendet ihren Abgang in der neuen Richtung.
Die Mitteilungszentrale gehört Ihnen
Verlauf, Nicht stören und ein Glocken-Einstieg — die Funktionen einer Mitteilungszentrale — sind App-Logik und liegen außerhalb von zenit: Protokollieren Sie den Verlauf dort, wo Sie show aufrufen, entscheiden Sie, ob überhaupt eine Karte erscheint, und erfahren Sie über den Listener von Schließen, Ablauf und Button-Aktionen. Die Zusammenfassungskarte im Storybook zeigt das Muster: Nach dem Ende von Nicht stören fasst eine quiet-Karte zusammen, was eingetroffen ist.