Skip to content

GeoLeaf Notifications — Public API ​

Module: @geoleaf/core — available from boot Namespace: GeoLeaf.notify() (shortcut) + GeoLeaf.Notifications.* (full namespace) ESM export: import { Notifications } from "@geoleaf/core"


Contents ​

  1. Overview
  2. CDN / ESM usage
  3. ESM usage (bundler)
  4. API reference
  5. Types
  6. Option details
  7. Internal architecture — Priority queue
  8. DOM structure and CSS classes
  9. Integration examples
  10. Accessibility
  11. Responsive (mobile)
  12. Debugging
  13. Security notes

Overview ​

GeoLeaf ships an internal toast notification system (NotificationSystem) used by the core to inform the user (data loading, network errors, and so on).

Since version 2.0.0 this system is publicly exposed to integrators:

Entry pointUsage
GeoLeaf.notify(msg, level)Top-level shortcut, simple usage
GeoLeaf.Notifications.success(msg)Full namespace, typed methods
import { Notifications } from "@geoleaf/core"ESM import for third-party bundlers

Main characteristics:

  • Priority queue: errors > warnings > info/success
  • 3 temporary toasts + 2 persistent toasts visible at the same time
  • Smooth animations with automatic reordering by priority
  • Accessibility: aria-live="assertive" for errors
  • prefers-reduced-motion support

CDN / ESM usage ​

html
<script type="module" src="geoleaf.esm.js"></script>
<script>
    // After GeoLeaf.boot() / geoleaf:app:ready event

    // Top-level shortcut
    GeoLeaf.notify("Welcome to the map", "info");

    // Full namespace
    GeoLeaf.Notifications.success("Data loaded successfully");
    GeoLeaf.Notifications.warning("Unstable connection", { duration: 6000 });
    GeoLeaf.Notifications.error("Unable to load the layer", {
        persistent: true,
        dismissible: true,
    });
</script>

Listening for the geoleaf:app:ready event ​

js
document.addEventListener("geoleaf:app:ready", () => {
    GeoLeaf.Notifications.show("Map ready", "success", 2000);
});

ESM usage (bundler) ​

ts
import { Notifications } from "@geoleaf/core";
// `contracts/notification.contract` is not a valid import path. The contract file is
// `notify.contract.ts` and is not published; both types live in the capability and are
// reachable through the `capabilities/*` subpath export.
import type { NotifyType, NotifyOptions } from "@geoleaf/core/capabilities/toast-renderer/types.js";

// Simple notification
Notifications.info("New update available");

// With options
Notifications.error("Synchronisation failed", {
    duration: 8000,
    dismissible: true,
});

// Generic signature
// `notify()` has TWO forms, and they do not mix:
//   notify(message, type, duration)  — positional, the 3rd argument is a NUMBER
//   notify(message, options)         — object, the type travels INSIDE the options
// Passing options in 3rd position does not compile. To combine a type and options,
// merge both into the second argument:
function notifyUser(message: string, type: NotifyType, opts?: NotifyOptions) {
    Notifications.notify(message, { ...opts, type });
}

// Check system status
const status = Notifications.getStatus();
console.log(`${status.activeToasts} active toast(s)`);

API reference ​

GeoLeaf.notify(message, level?) (top-level shortcut) ​

Displays a toast notification. Available directly on the GeoLeaf namespace, including before the application has booted: a message sent early is buffered and shown once the toast system is ready.

js
GeoLeaf.notify(message);
GeoLeaf.notify(message, level);
ParameterTypeDefaultDescription
messagestring—Text displayed in the toast
levelNotifyLevel"info""info", "success", "warning" or "error"

The shortcut takes neither a duration nor an options object: the toast uses the default duration of its level. To set a duration or options, use GeoLeaf.Notifications below.


GeoLeaf.Notifications.* / Notifications.* ​

.notify(message, typeOrOptions?, duration?) ​

Generic method. Supports both the positional and the object signature.

.success(message, options?) ​

Green toast — confirmation of a successful action. Default duration: 3,000 ms.

.error(message, options?) ​

Red toast — critical error. Default duration: 5,000 ms. Highest priority in the queue.

.warning(message, options?) ​

Orange toast — non-blocking alert. Default duration: 4,000 ms.

.info(message, options?) ​

Blue/neutral toast — information. Default duration: 3,000 ms.

.dismiss(toastEl) ​

Closes a specific toast from its DOM element.

.clearAll() ​

Immediately removes every visible toast and empties the queue.

.getStatus() → NotifyStatus ​

Returns a snapshot of the current state of the system.

js
const s = GeoLeaf.Notifications.getStatus();
// {
//   enabled: true,
//   initialized: true,
//   activeToasts: 1,
//   temporaryToasts: 1,
//   persistentToasts: 0,
//   queued: 0,
//   maxVisible: 3,
//   maxPersistent: 2,
//   position: "bottom-center"
// }

Types ​

NotifyType ​

ts
type NotifyType = "info" | "success" | "warning" | "error";

NotifyOptions ​

ts
interface NotifyOptions {
    type?: NotifyType; // type override (useful with notify())
    duration?: number; // display duration in ms (ignored if persistent: true)
    persistent?: boolean; // true → no auto-dismiss (default: false)
    dismissible?: boolean; // true → close button (default: true)
}

NotifyStatus ​

ts
interface NotifyStatus {
    enabled: boolean;
    initialized: boolean;
    activeToasts: number;
    temporaryToasts: number;
    persistentToasts: number;
    queued: number;
    maxVisible: number;
    maxPersistent: number;
    position: string;
}

Option details ​

duration ​

Time before the toast closes automatically, in milliseconds.

js
GeoLeaf.Notifications.info("Message", { duration: 8000 }); // 8 seconds

Default durations:

TypeDefault duration
info3,000 ms
success3,000 ms
warning4,000 ms
error5,000 ms

persistent ​

A persistent toast never closes on its own. It stays visible until an explicit dismiss() or clearAll().

js
GeoLeaf.Notifications.error("Lost connection to the server", { persistent: true });

// Later, when connection is restored:
GeoLeaf.Notifications.clearAll();
GeoLeaf.Notifications.success("Connection restored");

dismissible ​

Displays a × button that lets the user close the toast manually.

js
GeoLeaf.Notifications.warning("Update available", {
    persistent: true,
    dismissible: true, // user can dismiss
});

Internal architecture — Priority queue ​

Priorities ​

TypePriority
error3 (high)
warning2 (medium)
success / info1 (low)

Queue behaviour ​

  • Limit: 15 pending notifications at most
  • Eviction: the lowest-priority entries are dropped when the queue is full
  • Counters: 3 temporary toasts and 2 persistent toasts visible at the same time, at most

Display flow ​

  1. Toast added with a priority derived from its type
  2. Queue sorted by priority (desc) then timestamp (asc)
  3. Toasts displayed as slots become available
  4. Reordering: when an error arrives while the queue is full, an info/success toast is removed with a slideUp animation
  5. Eviction: when 15 toasts are pending, the lowest-priority one is dropped

Behaviour example ​

js
// Initial state: 3 info toasts visible + 5 queued
GeoLeaf.Notifications.info("Info 1");
GeoLeaf.Notifications.info("Info 2");
GeoLeaf.Notifications.info("Info 3");
// ... 5 more queued

// A priority error arrives
GeoLeaf.Notifications.error("Critical error");

// Result:
// - 1 info toast removed with slideUp animation
// - Error displayed immediately
// - 2 info toasts remain visible

DOM structure and CSS classes ​

Generated DOM structure ​

html
<div id="gl-notifications" class="gl-notifications gl-notifications--bottom-center">
    <div class="gl-toast gl-toast--success gl-toast--visible" role="alert" aria-live="polite">
        <span class="gl-toast__message">Success message</span>
        <button class="gl-toast__close" aria-label="Close">×</button>
    </div>
</div>

Required HTML container ​

html
<div id="gl-notifications" class="gl-notifications gl-notifications--bottom-center"></div>

Main classes ​

ClassDescription
.gl-notificationsFixed container
.gl-notifications--bottom-centerPosition variant
.gl-toastIndividual toast
.gl-toast--visibleVisible state (opacity: 1)
.gl-toast--removingExit animation
.gl-toast--sliding-upReordering animation (evicted toast)
.gl-toast--sliding-downReordering animation (toast moved down)
.gl-toast--successSuccess type (green)
.gl-toast--errorError type (red)
.gl-toast--warningWarning type (orange)
.gl-toast--infoInfo type (blue)
.gl-toast__messageMessage content
.gl-toast__closeClose button

Available positions ​

  • bottom-center (default, recommended)
  • top-right
  • bottom-right
  • top-center

CSS animations ​

css
/* Slide-up animation (toast evicted by higher priority) */
@keyframes slideUp {
    from {
        transform: translateY(0);
        opacity: 1;
    }
    to {
        transform: translateY(-100%);
        opacity: 0;
    }
}

/* Slide-down animation (toast moved down in stack) */
@keyframes slideDown {
    from {
        transform: translateY(-20px);
        opacity: 0.5;
    }
    to {
        transform: translateY(0);
        opacity: 1;
    }
}

Integration examples ​

Hook on a layer toggle event ​

js
document.addEventListener("geoleaf:layer:toggle", (e) => {
    const { layerId, visible } = e.detail;
    if (visible) {
        GeoLeaf.Notifications.show(`Layer "${layerId}" enabled`, "info", 2000);
    }
});

Hook on a layer loading error ​

js
document.addEventListener("geoleaf:layer:error", (e) => {
    GeoLeaf.Notifications.error(`Unable to load layer "${e.detail.layerId}"`, {
        persistent: true,
        dismissible: true,
    });
});

Notification after adding a POI (AddPOI plugin) ​

js
document.addEventListener("geoleaf:poi:added", (e) => {
    GeoLeaf.Notifications.success(`POI "${e.detail.name}" added successfully`);
});

Offline cache (Storage plugin) ​

js
// Download success
GeoLeaf.Notifications.success(`Profile downloaded: ${sizeMB} MB`, 4000);

// Storage error
GeoLeaf.Notifications.error("Offline storage unavailable", 5000);

// Download stopped
GeoLeaf.Notifications.warning("Download stopped", 3000);

POI synchronisation ​

js
// Start info (persistent)
GeoLeaf.Notifications.info("Synchronisation in progress...", {
    persistent: true,
    dismissible: false,
});

// Conditional success/warning
if (results.failed > 0) {
    GeoLeaf.Notifications.warning(
        `Sync finished: ${results.synced} succeeded, ${results.failed} failed`,
        5000
    );
} else {
    GeoLeaf.Notifications.success(`Sync finished: ${results.synced} succeeded`, 5000);
}

Checking the system before notifying ​

js
const status = GeoLeaf.Notifications.getStatus();

if (status.initialized && status.enabled) {
    GeoLeaf.Notifications.info("Notification system operational");
} else {
    console.warn("[GeoLeaf] Notifications unavailable", status);
}

Accessibility ​

  • role="alert" on every toast
  • aria-live="assertive" for errors and priority toasts
  • aria-live="polite" for success/warning/info
  • aria-label on the close button
  • prefers-reduced-motion support (transitions disabled when requested)
  • Focus management (:focus-within)
css
@media (prefers-reduced-motion: reduce) {
    .gl-toast,
    .gl-notifications {
        transition: none !important;
    }
}

Responsive (mobile) ​

On mobile (< 768 px) toasts span the full width of the screen:

css
@media (max-width: 768px) {
    .gl-notifications--bottom-center {
        left: 10px;
        right: 10px;
        transform: none;
    }
}

Debugging ​

js
// Check current state
console.log(GeoLeaf.Notifications.getStatus());

// Test queue behavior
for (let i = 0; i < 20; i++) {
    GeoLeaf.Notifications.info(`Test ${i}`);
}
// Expected: 3 visible, 12 queued, 5 dropped

Security notes ​

WARNING

Messages passed to GeoLeaf.notify() are handled as plain text — they are inserted through textContent, never innerHTML.

When a message is built from user input or an external source, never inject HTML into it. GeoLeaf guards against XSS injection at its own level, but composing the message remains the integrator's responsibility.

js
// Correct — static text or trusted source
GeoLeaf.notify("Connection restored", "success");

// Correct — internal GeoLeaf data
GeoLeaf.notify(`POI "${poi.name}" loaded`, "success");

// Avoid — raw HTML from user input
GeoLeaf.notify(`<b>${userInput}</b>`, "info"); // do not do this

Related documentation:

Released under the MIT License.