Skip to main content
Introducing packages.sweber.dev
Documentation menuToasts, indicator, fall

Toasts, indicator, fall

A swipeable toast stack, a sliding tab indicator and a page effect that lets a section fall apart and come back.

import { fall, indicator, toasts } from "@weber-development/lagrangian-pro";

Toasts

const stack = toasts(corner, {
  edge: "bottom",   // new toasts arrive from this edge: "top" or "bottom"
  max: 4,           // older ones are dismissed
  duration: 5000,   // ms, 0 keeps toasts until dismissed
  swipe: true,      // throw a toast away sideways
});

const saved = stack.show("Saved");
const failed = stack.show("Upload failed", { role: "alert", duration: 0 });
await failed.dismiss();
stack.clear();

The host element is laid out by you, usually position: fixed with display: flex; flex-direction: column. A new toast springs in from the edge while the others move to make room; when one leaves, the rest spring into the gap. A swipe sideways throws a toast away at the speed of the throw, a slow one brings it back. Timers stop while the pointer or the keyboard focus is on the stack and continue with the time that was left. Toasts are role="status" (polite) or role="alert" (interrupts), and content can be a string or a DOM node, for example with an action button. With reduced motion toasts appear and leave without movement.

Indicator

const tabs = indicator(track, bar, {
  itemSelector: "button",
  onSelect: (index, item) => {},
});

tabs.select(2);
tabs.refresh(); // after a resize

track is positioned, bar is an absolutely positioned element at its left edge. The bar slides with x and stretches with scaleX to the width of the selected item, so it also works for tabs of different widths. A new selection while the bar is moving keeps its velocity. Clicking an item selects it (turn off with clickToSelect: false), and the items get aria-selected. Give the bar a plain background: stretching distorts rounded corners and borders.

Fall

const effect = fall(section, { gravity: 9.81, restitution: 0.3 });

effect.start();                                // the children let go, lowest first
effect.explode({ x: 400, y: 600 }, 900);       // throw them away from a point
await effect.restore();                        // spring back into the layout

Every child gets an invisible placeholder of the same size, so the page does not jump, and then becomes a box in a physics world with the viewport as floor and walls (bounds changes that). restore() springs each piece from wherever it lies, including its rotation, to its place. effect.world is the underlying World if you want to add bodies or listen for collisions. Needs @sweberdev/lagrangian 0.2 or later.

React

const toast = useToasts({ edge: "top" });             // ref, show, clear
const tabs = useIndicator({ itemSelector: "button" }); // ref (track), bar (ref), index, select
const effect = useFall();                              // ref, active, start, explode, restore
<div ref={tabs.ref} style={{ position: "relative" }}>
  <span ref={tabs.bar} className="bar" />
  <button>One</button>
  <button>Two</button>
</div>