leaflet_svelte/src/lib/components/ui/Toast.svelte
ThePetrovich 8e3dfa54f9 Refactoring of various stuff
big mess, I don't remember what I was trying to accomplish there
2025-12-14 18:06:17 +08:00

111 lines
3.2 KiB
Svelte

<script context="module">
import { writable } from "svelte/store";
/**
* @typedef {'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark'} ToastColor
* @typedef {object} ToastMessage
* @property {string} id - Unique identifier
* @property {string} header - Toast title
* @property {string} body - Toast message content
* @property {ToastColor} [color='info'] - The color of the toast header icon
* @property {boolean} [persistent=false] - If true, toast will not auto-close
* @property {function} [onRemoveCallback=null] - Callback function to be called when the toast is removed
*/
/** @type {import('svelte/store').Writable<ToastMessage[]>} */
export const toasts = writable([]);
const TOAST_ICONS = {
primary: "info-circle-fill",
secondary: "info-circle-fill",
success: "check-circle-fill",
danger: "exclamation-triangle-fill",
warning: "exclamation-circle-fill",
info: "info-circle-fill",
light: "lightbulb",
dark: "question",
};
/**
* Adds a new toast to the list.
* @param {Omit<ToastMessage, 'id'>} toast
* @returns {string} The ID of the new toast.
*/
export function addToast(toast) {
const id = crypto.randomUUID();
toasts.update((all) => [...all, { id, ...toast }]);
return id;
}
/**
* Removes a toast by its ID.
* @param {string} id
*/
export function removeToast(id) {
// call the onRemoveCallback if it exists
toasts.update((all) => {
const toast = all.find((t) => t.id === id);
if (toast && toast.onRemoveCallback) {
toast.onRemoveCallback(id);
}
return all.filter((t) => t.id !== id);
});
}
/**
* Callback function to be called when a toast is removed.
* @param {string} id - The ID of the removed toast.
*/
</script>
<script>
import { Toast, ToastBody, ToastHeader, Icon } from "@sveltestrap/sveltestrap";
/**
* Removes a toast from the list by its ID.
* @param {string} id
*/
</script>
<!--
This container holds all the toasts.
To use this component:
1. Import it into your layout or page: `import ToastContainer from './Toast.svelte';`
2. Place `<ToastContainer />` in your markup.
3. To show a toast from any other component:
import { addToast } from './Toast.svelte';
// For an auto-closing error message
addToast({ header: 'Error', body: 'Something went wrong.', color: 'danger' });
// For a persistent "map mode" indication
addToast({ header: 'Map Mode', body: 'You are in satellite view.', color: 'info', persistent: true });
-->
<div class="toast-container position-fixed bottom-0 end-0 p-3">
{#each $toasts as toast (toast.id)}
<Toast
isOpen={true}
autohide={!toast.persistent}
delay={5000}
color={toast.color || "info"}
on:close={() => removeToast(toast.id)}>
<ToastHeader toggle={() => removeToast(toast.id)} class={`text-${toast.color || "text-info"}`}>
<Icon
slot="icon"
name={TOAST_ICONS[toast.color ? toast.color : "info"]}
class="me-2"
color={toast.color || "info"} />
{toast.header}
</ToastHeader>
<ToastBody>
{toast.body}
</ToastBody>
</Toast>
{/each}
</div>
<style>
.toast-container {
z-index: 1090; /* High z-index to appear above other elements */
}
</style>