browsertrix/frontend/src/controllers/notify.ts
Emma Segal-Grossman 46aab91ef5
Adds optional IDs to toasts to replace rather than add to toast stack (#2236)
This has been bugging me for a while — any type of repetitive action
that shows a toast will quickly cause a big stack of toasts to build up.
This fixes that by allowing different categories of toasts to exist,
where each category can only have one active toast. _Different_
categories of toast can coexist just fine, but toasts with the same `id`
will replace any existing toast with that same `id`.

It's been a pet peeve for a while, and it should make any sort of "power
user" use of Browsertrix a lot nicer :)

I've gone over all the existing toasts and applied what feel like decent
differentiations, but I'm not set on any of them — open to suggestions!


https://github.com/user-attachments/assets/dbe39141-9e56-427d-b702-124c45ef2b9a

---------

Co-authored-by: Ilya Kreymer <ikreymer@users.noreply.github.com>
2024-12-13 15:27:01 -05:00

66 lines
1.5 KiB
TypeScript

import type {
ReactiveController,
ReactiveControllerHost,
TemplateResult,
} from "lit";
export type NotifyEventDetail = {
/**
* Notification message body.
* Example:
* ```ts
* message: html`<strong>Look!</strong>`
* ```
*
* Note: In order for `this` methods to work, you'll
* need to bind `this` or use a fat arrow function.
* For example:
* ```ts
* message: html`<button @click=${this.onClick.bind(this)}>Go!</button>`
* ```
* Or:
* ```ts
* message: html`<button @click=${(e) => this.onClick(e)}>Go!</button>`
* ```
**/
message: string | TemplateResult;
/** Notification title */
title?: string;
/** Shoelace icon name */
icon?: string;
variant?: "success" | "warning" | "danger" | "primary" | "info";
duration?: number;
id?: string | number | symbol;
};
export interface NotifyEventMap {
"btrix-notify": CustomEvent<NotifyEventDetail>;
}
const NOTIFY_EVENT_NAME: keyof NotifyEventMap = "btrix-notify";
/**
* Manage global app notifications
*/
export class NotifyController implements ReactiveController {
private readonly host: ReactiveControllerHost & EventTarget;
constructor(host: NotifyController["host"]) {
this.host = host;
host.addController(this);
}
hostConnected() {}
hostDisconnected() {}
toast(detail: NotifyEventDetail) {
this.host.dispatchEvent(
new CustomEvent<NotifyEventDetail>(NOTIFY_EVENT_NAME, {
bubbles: true,
composed: true,
detail,
}),
);
}
}