Notifications
Utilisez le Notifier pour des notifications dans la fenêtre qui ne bloquent jamais la tâche en cours — succès, erreur, progression, annulation et messages —, avec l’empilement, le déploiement au survol et huit positions d’ancrage gérés pour vous.
Qu’est-ce que le Notifier
ui.widgets.Notifier est un service au niveau de la fenêtre : initialisez-le une fois, puis appelez show depuis n’importe où. Les cartes vivent sur le niveau toast du portal de superposition de la fenêtre (z 2000 — au-dessus des dialogues, sous les tooltips) et ne participent jamais à la mise en page. Chaque carte possède un nœud fixe : en retirer une ne reconstruit jamais les autres et ne rejoue pas leur entrée. Il remplace l’ancien ToastManager.
L’API champ par champ (Notification, NotifierOptions et la signature de chaque méthode) se trouve sur la page Notification du site des composants.
Créer un Notifier
Créez-le une fois dans votre fonction de montage racine et passez le pointeur à ce qui doit notifier (par exemple l’état de l’application conservé par cx.bindState). Le Notifier appartient au Scope que vous passez et disparaît avec lui.
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,
});positionL’une des huit positions, .bottom_center par défaut ; modifiable à l’exécution avec setPositionmax_visibleNombre de cartes affichées une fois déployé (2–6, 4 par défaut)expand_on_hoverSi false, le survol met seulement les minuteurs en pause sans déployercontent_insetsRetraits depuis les bords de la fenêtre (barre de titre, barre d’outils, barre d’état) ; la marge d’ancrage de 22px part de làwidthLargeur des cartes, 392 par défautstringsTextes de l’UI ("now", "Undo", "Reply…", le badge replié…) pour la localisationlistenerCallback pour les actions de l’utilisateur et l’expiration ; aussi définissable avec setListenerAfficher une notification
show prend une valeur ui.widgets.Notification et renvoie son id. Le Notifier copie chaque chaîne, vos tampons sont donc réutilisables immédiatement. Au-delà de six cartes à l’écran, la plus ancienne part avec son animation de sortie normale.
// 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,
});Types, tons et durées
L’apparence d’une carte dépend de deux axes : kind (mise en page) et tone (couleur sémantique). L’icône de tête découle des deux, ou se définit explicitement avec lead / glyph.
.defaultIcône, titre et corps, avec boutons, zone de réponse ou avatar en option.progressSpinner, barre déterminée et un compteur n / N ; pas de barre de vie.undoAnneau de compte à rebours affichant les secondes restantes, plus un bouton "Undo" par défaut.successUne opération terminée3.6 s.@"error"Un échec qui demande une actionpersistante.warningÀ remarquer, sans bloquer5.6 s.infoInformation générale (par défaut)4.2 s.violetPersonnes, invitations et autres messages sociaux4.2 s ; persistante avec boutons.quietStatut discret (un petit point au lieu d’une icône)2.8 s- ✓
Persistantes par défaut : les cartes progress, celles avec une zone de réponse, le ton error et les cartes violet avec boutons ne se ferment jamais d’elles-mêmes.
- ✓
Surcharges explicites :
sticky = truela rend persistante ;duration_msremplace la durée par défaut ;life_baraffiche ou masque la barre de vie. - ✓
Undo dure 7 secondes par défaut, l’anneau suivant le minuteur.
Mises à jour sur place et progression
update(id, …) réécrit le type, le ton, le titre et le corps de la même carte et relance sa durée de vie — ni destruction, ni reconstruction, ni entrée rejouée. Faites avancer une carte de progression avec setProgress, qui ne touche que la couche de progression. Appeler show avec un id existant la transforme aussi sur place.
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,
});Gérer les actions de l’utilisateur
Le Notifier rend compte via un unique ui.widgets.NotificationListener. Un NotificationEvent transporte l’id de la carte, le kind de l’événement, et le tag / index du bouton ou le text de la réponse.
.actionUn bouton a été cliqué (distinguez-les par tag).undoUndo a été cliqué ; l’anneau est déjà devenu un ✓.replyLa zone de réponse a été envoyée (Entrée ou Send) ; text la contient.dismissedL’utilisateur l’a fermée (✕ ou d’un geste).expiredElle a expiréconst 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 });Empilement et survol
Plusieurs notifications se replient en pile : la plus récente est la plus proche de l’ancre, avec le z le plus élevé ; chaque couche derrière dépasse de 9px et rétrécit de 3.8 %, jusqu’à trois couches, avec un badge « N de plus · survoler pour déployer ». Quand le pointeur entre dans les limites de la pile (agrandies de 18px), elle se déploie avec des écarts de 9px et tous les minuteurs se mettent en pause ; en sortant, elle se replie et ils reprennent.
À côté du badge se trouve un bouton « tout effacer » : le premier clic révèle son libellé, le second efface vraiment, et s’éloigner le replie. Dans le code, appelez dismissAll().
Une carte isolée peut être écartée d’un geste, ce qui signale dismissed.
Au redimensionnement de la fenêtre, la pile suit son ancre de façon rigide, sans interpolation à la traîne.
Positions
setPosition ne change que l’ancre, la direction de croissance et le décalage d’entrée — les cartes ne sont pas reconstruites, et une carte en train de sortir termine sa sortie dans la nouvelle direction.
Le centre de notifications vous appartient
L’historique, le mode Ne pas déranger et un point d’entrée en forme de cloche — les fonctions d’un centre de notifications — relèvent de la logique de l’application et restent hors de zenit : enregistrez l’historique là où vous appelez show, décidez s’il faut afficher une carte, et suivez fermetures, expirations et actions de boutons via le Listener. La carte Résumé du Storybook illustre le principe : à la fin du mode Ne pas déranger, une seule carte quiet récapitule ce qui est arrivé.