SmartCore
Three optional global UI primitives — <smart-toast>, <smart-loader>, and <smart-modal> — that wire up in one script tag and communicate with every other SmartComponent automatically via window CustomEvents.
<script src="smart-core.js"></script>
Architecture
All three components follow the same decoupled pattern. They listen for events dispatched on window and react. Nothing else in your codebase needs a reference to them. If smart-core.js is not loaded, every other SmartComponent degrades silently — SmartTable falls back to window.confirm() for deletes, inline toasts for feedback, etc.
<!-- 1. Place singletons once, near the top of <body>. Loading order relative to the page doesn't matter. --> <smart-toast position="top-right" max="5"></smart-toast> <smart-modal></smart-modal> <smart-loader></smart-loader> <!-- 2. Fire from anywhere — no import, no reference required. --> window.dispatchEvent(new CustomEvent('smart-toast', { detail: { message, type } })); window.dispatchEvent(new CustomEvent('smart-loader', { detail: { action: 'show' } })); window.dispatchEvent(new CustomEvent('smart-confirm', { detail: { title, message, onConfirm }, cancelable: true }));
| Component | Listens for | Fallback when absent |
|---|---|---|
| <smart-toast> | smart-toast | SmartTable shows its own inline toast below the table. |
| <smart-loader> | smart-loader | No overlay shown — your app continues unaffected. |
| <smart-modal> | smart-confirm | SmartTable runs window.confirm() instead. |
SmartToast
Stacked, auto-dismissing toast notifications anchored to any corner of the viewport. Fire from anywhere with a single window.dispatchEvent() call — no import or element reference required.
Toast Types
Five semantic types — each with distinct icon and colour. Click any button to see a live toast fired on the real <smart-toast> instance mounted on this page.
Toasts appear bottom-right and auto-dismiss after 3.5 s. Hover to pause.
// Fire a toast from anywhere in your app window.dispatchEvent(new CustomEvent('smart-toast', { detail: { message: 'File saved successfully.', type: 'success', // 'success' | 'error' | 'warning' | 'info' | 'loading' duration: 3500, // ms — 0 = persistent until dismissed } })); // From a Django view response — call after fetch() async function save() { const res = await fetch('/api/save/', { method: 'POST', ... }); const type = res.ok ? 'success' : 'error'; window.dispatchEvent(new CustomEvent('smart-toast', { detail: { message: res.ok ? 'Saved!' : 'Save failed.', type } })); }
Promise Mode
Pass a promise in the detail object and SmartToast shows a loading state automatically. When the promise resolves it switches to success; on rejection it shows the error message. Perfect for wrapping fetch() calls.
Has a 65% chance of resolving. Watch the toast transition from loading → success / error.
// Wrap any Promise — toast handles all three states automatically const p = fetch('/api/process/', { method: 'POST' }); window.dispatchEvent(new CustomEvent('smart-toast', { detail: { promise: p, loading: 'Processing request…', success: 'Request completed!', error: 'Request failed — please retry.', } })); // Works with any thenable — not just fetch() // On resolve: switches to success toast, auto-dismisses after 3 s // On reject: switches to error toast, auto-dismisses after 3 s
Reference
Element attributes
| Attribute | Type | Description | Default |
|---|---|---|---|
| position | string | Stack anchor. One of: top-right, top-left, top-center, bottom-right, bottom-left, bottom-center. |
bottom-right |
| max | number | Maximum number of visible toasts. Oldest is dismissed when the limit is reached. | 5 |
Event detail (smart-toast)
| Key | Type | Description | Default |
|---|---|---|---|
| message | string | Toast body text. HTML is escaped automatically. | "" |
| type | string | success | error | warning | info | loading |
info |
| duration | number | Auto-dismiss delay in milliseconds. Pass 0 for a persistent toast. |
3000 |
| promise | Promise | Any thenable. When provided, shows a loading toast that transitions to success or error on settle. message is ignored. |
— |
| loading | string | Message shown while the promise is pending. | Loading… |
| success | string | Message shown when the promise resolves. | Done! |
| error | string | Message shown when the promise rejects. | Failed. |
<!-- Minimal — one singleton anywhere in your base template --> <smart-toast position="bottom-right" max="5"></smart-toast> // Standard toast window.dispatchEvent(new CustomEvent('smart-toast', { detail: { message: 'Done!', type: 'success', duration: 4000 } })); // Persistent — stays until user clicks ✕ window.dispatchEvent(new CustomEvent('smart-toast', { detail: { message: 'System maintenance in 5 min.', type: 'warning', duration: 0 } }));
SmartLoader
Full-page or element-scoped overlay loader with a 200ms flicker-prevention delay. Show and hide it from anywhere via smart-loader window events. Stacks gracefully — multiple concurrent show calls resolve correctly when all matching hide calls arrive.
Full-page & Scoped
The global loader covers the entire viewport with a blurred backdrop. The scoped variant positions itself absolutely inside any element with an id — set scope in the event detail to the target element's id.
// Full-page — covers entire viewport window.dispatchEvent(new CustomEvent('smart-loader', { detail: { action: 'show' } })); // Hide when done window.dispatchEvent(new CustomEvent('smart-loader', { detail: { action: 'hide' } })); // Scoped — overlays the element with id="myCard" // The element needs no extra CSS — position is set automatically window.dispatchEvent(new CustomEvent('smart-loader', { detail: { action: 'show', scope: 'myCard' } })); window.dispatchEvent(new CustomEvent('smart-loader', { detail: { action: 'hide', scope: 'myCard' } })); // Wrap an async operation async function loadData() { window.dispatchEvent(new CustomEvent('smart-loader', { detail: { action: 'show' } })); try { const data = await fetch('/api/data/').then(r => r.json()); renderData(data); } finally { window.dispatchEvent(new CustomEvent('smart-loader', { detail: { action: 'hide' } })); } }
Reference
Element
<!-- One singleton anywhere in <body> — no attributes required --> <smart-loader></smart-loader>
Event detail (smart-loader)
| Key | Type | Description |
|---|---|---|
| action | string | Required. "show" — display the loader (200ms delay). "hide" — dismiss it with a fade-out transition. |
| scope | string | Optional element id. When provided, the overlay is scoped to that element instead of covering the full page. The element's position is temporarily set to relative if needed. |
The 200ms delay prevents a loading flash for operations that finish almost instantly (common on fast connections). If hide arrives before the delay expires, no overlay is shown at all.
SmartModal
A branded confirmation modal that intercepts smart-confirm events. When present in the DOM it calls event.preventDefault() so SmartTable skips its window.confirm() fallback. Closes on backdrop click and the Escape key.
Custom Labels & Callbacks
Pass title, message, confirmLabel, cancelLabel, onConfirm, and onCancel in the event detail to fully customise the modal for any use case — not just delete actions.
Each modal fires a success/info toast via its onConfirm / onCancel callbacks.
// Fire a confirmation modal from anywhere window.dispatchEvent(new CustomEvent('smart-confirm', { detail: { title: 'Delete Record?', message: 'This action cannot be undone.', confirmLabel: 'Delete', cancelLabel: 'Cancel', icon: '🗑️', onConfirm: () => { // Called when the user clicks the confirm button fetch('/api/records/42/', { method: 'DELETE' }); }, onCancel: () => { // Called when the user clicks Cancel, presses Esc, or clicks the backdrop console.log('Cancelled'); }, }, cancelable: true, // required for event.preventDefault() to work })); // When <smart-modal> is in the DOM: // → event.preventDefault() is called — SmartTable skips window.confirm() // → Branded modal renders // When <smart-modal> is absent: // → event is not cancelled — SmartTable's window.confirm() fallback runs
Reference
Element
<!-- One singleton anywhere in <body> — no attributes required --> <smart-modal></smart-modal>
Event detail (smart-confirm)
| Key | Type | Description | Default |
|---|---|---|---|
| title | string | Modal header text. | Confirm |
| message | string | Body / description text. | Are you sure? |
| confirmLabel | string | Label on the primary action button. | Delete |
| cancelLabel | string | Label on the dismiss button. | Cancel |
| onConfirm | function | Called when the user clicks the confirm button. | — |
| onCancel | function | Called when the modal is dismissed — Cancel button, Esc key, or backdrop click. | — |
The event must be dispatched with cancelable: true for event.preventDefault() to work correctly. SmartTable already does this internally.