Initial implementation of custom profile editor + formatting

This commit is contained in:
ThePetrovich 2025-07-09 20:14:47 +08:00
parent 82b36f96d0
commit ffb27c2e0a
21 changed files with 3045 additions and 2034 deletions

View file

@ -1,71 +1,70 @@
<script context="module">
import { writable } from 'svelte/store';
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
*/
/**
* @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([]);
/** @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'
};
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;
}
/**
* 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);
});
}
/**
* 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.
*/
/**
* 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
*/
import { Toast, ToastBody, ToastHeader, Icon } from "@sveltestrap/sveltestrap";
/**
* Removes a toast from the list by its ID.
* @param {string} id
*/
</script>
<!--
@ -83,27 +82,30 @@
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}
{#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>
.toast-container {
z-index: 1090; /* High z-index to appear above other elements */
}
</style>