irekasoft

Framework

Overlays

Overlays layer transient surfaces on top of the current screen. Some are dismissible — bottom sheets, dropdown menus, alerts and popups wait for the user and plug into a single back-button system. Others are feedback — HUDs and toasts appear, then auto-dismiss on their own. This page covers both, and the back handling that keeps them in order.

BottomSheet

BottomSheet slides a panel up from the bottom over a dimmed backdrop. It's drag-dismissible — flicking it down past the threshold (or tapping the backdrop) calls onClose. Best for short, contextual actions.

jsx
import { useState } from 'react';
import { BottomSheet } from '../framework';
import { SheetAction } from '../components/ui';

function ShareButton() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <button onClick={() => setOpen(true)}>Share</button>

      <BottomSheet open={open} onClose={() => setOpen(false)}>
        <SheetAction onClick={() => setOpen(false)}>Copy link</SheetAction>
        <SheetAction onClick={() => setOpen(false)}>Share to…</SheetAction>
      </BottomSheet>
    </>
  );
}

Alert

Alert is the centered, iOS-style confirmation dialog over a dimmed backdrop. Drive it with open / onClose and a buttons array — each button fires its onPress and then closes the alert. One or two buttons sit side by side; three or more stack vertically.

Button variant accepts destructive (red) and cancel (muted); omitting it gives the default tint.

jsx
import { useState } from 'react';
import { Alert } from '../components/ui';

function DeleteButton() {
  const [open, setOpen] = useState(false);
  return (
    <>
      <button onClick={() => setOpen(true)}>Delete</button>

      <Alert
        open={open}
        onClose={() => setOpen(false)}
        title="Delete order?"
        message="This can't be undone."
        buttons={[
          { label: 'Cancel', variant: 'cancel' },
          { label: 'Delete', variant: 'destructive', onPress: () => removeOrder() },
        ]}
      />
    </>
  );
}

HUD

HUD is the centered heads-up indicator — a blurred dark tile with an icon and label. type is done, error, info, or loading. The first three auto-dismiss after ~1.8s (firing onClose); loading spins until you set open={false} yourself.

jsx
import { useState } from 'react';
import { HUD } from '../components/ui';

function SaveButton() {
  const [saving, setSaving] = useState(false);
  const [done, setDone] = useState(false);

  async function save() {
    setSaving(true);
    await persist();
    setSaving(false);
    setDone(true);
  }

  return (
    <>
      <button onClick={save}>Save</button>
      <HUD open={saving} type="loading" label="Saving…" />
      <HUD open={done} type="done" label="Saved" onClose={() => setDone(false)} />
    </>
  );
}

Toast

Toast is the global, stacked notification that drops in from the top. The ToastProvider is already mounted at the app root, so any component can fire one through the useToast() hook — no local state to manage. Each toast auto-dismisses (default 3s) and can be swiped away.

useToast() returns success, error, and info helpers (title, message, opts), plus a lower-level show({ variant, title, message, duration }).

jsx
import { useToast } from '../components/ui/Toast';

function CheckoutButton() {
  const toast = useToast();
  return (
    <button onClick={() => toast.success('Order placed', 'We\'ll text you when it\'s ready')}>
      Place order
    </button>
  );
}

ToastOverlay

ToastOverlay is a lightweight, local pill toast — good for a quick inline message scoped to one screen rather than the whole app. Render it inside a relative container and feed it a message; it slides in from the top (default) or bottom and hides itself after ~2s.

Pass an { text, id } object (rather than a bare string) when the same message may repeat — changing the id re-triggers the animation.

jsx
import { useState } from 'react';
import ToastOverlay from '../components/ui/ToastOverlay';

function CopyRow() {
  const [msg, setMsg] = useState(null);
  return (
    <div className="relative">
      <button onClick={() => setMsg({ text: 'Copied', id: Date.now() })}>
        Copy code
      </button>
      <ToastOverlay message={msg} position="bottom" />
    </div>
  );
}

Android back handling

Every overlay registers itself with a shared back stack via the useOverlayBack(active, onBack) hook, so the Android hardware back key dismisses the topmost overlay first, then walks back through the page stack, and only minimizes the app when nothing is left to close. ModalPage, BottomSheet, and StackNav already wire this up — you only need the hook when building a custom overlay.

The handler is installed once at startup via installAndroidBackHandler() and is a no-op on web and iOS, where the platform's own back gestures apply.

jsx
import { useOverlayBack } from '../framework';

function CustomOverlay({ open, onClose, children }) {
  useOverlayBack(open, onClose);
  if (!open) return null;
  return <div className="overlay">{children}</div>;
}