17
loading...
This website collects cookies to deliver better user experience
Snackbar
component:Snackbar
and a function addSnackbarItem
to operate it.Snackbar
component, as it provides brief messages about app processes at the bottom of the screen.import { Controller } from 'stimulus';
// Wraps the Preact Snackbar component into a Stimulus controller
export default class SnackbarController extends Controller {
static targets = ['snackZone'];
async connect() {
const [{ h, render }, { Snackbar }] = await Promise.all([
// eslint-disable-next-line import/no-unresolved
import('preact'),
import('Snackbar'),
]);
render(<Snackbar lifespan="3" />, this.snackZoneTarget);
}
async disconnect() {
const { render } = await import('preact');
render(null, this.snackZoneTarget);
}
// Any controller (or vanilla JS) can add an item to the Snackbar by dispatching a custom event.
// Stimulus needs to listen via this HTML's attribute: data-action="snackbar:add@document->snackbar#addItem"
async addItem(event) {
const { message, addCloseButton = false } = event.detail;
const { addSnackbarItem } = await import('Snackbar');
addSnackbarItem({ message, addCloseButton });
}
}
static targets = ['snackZone'];
<div>
in the page:<div data-snackbar-target="snackZone"></div>
this.snackZoneTarget
. Stimulus documentation has more information on targets.Snackbar
's container is called inside Forem's frontend code, I kept the name :D)Snackbar
component, when initialized, doesn't render anything visible to the user. It waits for a message to be added to the stack of disappearing messages that are shown to the user after an action is performed. For this reason, we can use Stimulus lifecycle callbacks to mount it and unmount it.connect()
and disconnect()
, that we can use to initialize and cleanup our Preact component.connect()
method, in our case we take advantage of this by loading Preact and the Snackbar component:async connect() {
const [{ h, render }, { Snackbar }] = await Promise.all([
import('preact'),
import('Snackbar'),
]);
render(<Snackbar lifespan="3" />, this.snackZoneTarget);
}
Snackbar
component
async disconnect() {
const { render } = await import('preact');
render(null, this.snackZoneTarget);
}
<body
data-controller="snackbar"
data-action="snackbar:add@document->snackbar#addItem">
data-controller="snackbar"
attaches Stimulus SnackbarController
, defined in the first section of this post, to the <body>
of the page.data-action="snackbar:add@document->snackbar#addItem"
instructs the framework to listen to the custom event snackbar:add
on window.document
and when received to send it to the SnackbarController
by invoking its addItem
method acting as en event handler.addItem
is defined as:async addItem(event) {
const { message, addCloseButton = false } = event.detail;
const { addSnackbarItem } = await import('Snackbar');
addSnackbarItem({ message, addCloseButton });
}
addSnackbarItem
and invokes it with the correct arguments, to display a message to the user.EventTarget.dispatchEvent
method:document.dispatchEvent(new CustomEvent('snackbar:add', { detail: { message: 'MESSAGE' } }));
document.dispatchEvent(new CustomEvent('snackbar:add', { detail: { message: 'MESSAGE', addCloseButton: false } }));
document.dispatchEvent(new CustomEvent('snackbar:add', { detail: { message: 'MESSAGE', addCloseButton: true } }));
CustomEvent
interface is straightforward and flexible enough that can be used to create more advanced patterns like the, now defunct, Vue Events API which provided a global event bus in the page, out of scope for this post.