feat: add toast component
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import { toasts } from './toast.svelte';
|
||||
|
||||
const typeClasses: Record<string, string> = {
|
||||
success: 'border-l-green-500',
|
||||
error: 'border-l-red-500',
|
||||
info: 'border-l-active'
|
||||
};
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="fixed bottom-4 right-4 flex flex-col gap-2 z-[9999] pointer-events-none"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
aria-atomic="false"
|
||||
>
|
||||
{#each toasts.value as item (item.id)}
|
||||
<div
|
||||
class="
|
||||
bg-layer-2 text-text border border-outline rounded
|
||||
px-3.5 py-2 text-sm min-w-[180px] max-w-xs
|
||||
border-l-3 {typeClasses[item.type] ?? 'border-l-outline'}
|
||||
animate-[slide-in_0.18s_ease]
|
||||
"
|
||||
>
|
||||
{item.message}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@keyframes slide-in {
|
||||
from { opacity: 0; transform: translateX(12px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
</style>
|
||||
@@ -2,14 +2,20 @@ export { default as Input } from './Input.svelte';
|
||||
export { default as InputCheckbox } from './inputs/InputCheckbox.svelte';
|
||||
export { default as InputColor } from './inputs/InputColor.svelte';
|
||||
export { default as InputNumber } from './inputs/InputNumber.svelte';
|
||||
export { default as InputSearch } from './inputs/InputSearch.svelte';
|
||||
export { default as InputSelect } from './inputs/InputSelect.svelte';
|
||||
export { default as InputShape } from './inputs/InputShape.svelte';
|
||||
export { default as InputVec3 } from './inputs/InputVec3.svelte';
|
||||
export { default as SocketTable } from './inputs/SocketTable.svelte';
|
||||
|
||||
export { default as Button } from './Button.svelte';
|
||||
export { default as Details } from './Details.svelte';
|
||||
export { default as JsonViewer } from './JsonViewer.svelte';
|
||||
export { default as ShortCut } from './ShortCut.svelte';
|
||||
export { default as Spinner } from './Spinner.svelte';
|
||||
export { default as Toast } from './Toast.svelte';
|
||||
export { toast } from './toast.svelte';
|
||||
export { default as ConfirmDialog } from './ConfirmDialog.svelte';
|
||||
|
||||
import Input from './Input.svelte';
|
||||
export default Input;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
export type ToastType = 'info' | 'success' | 'error';
|
||||
|
||||
export type ToastItem = {
|
||||
id: number;
|
||||
message: string;
|
||||
type: ToastType;
|
||||
};
|
||||
|
||||
let _toasts = $state<ToastItem[]>([]);
|
||||
let _nextId = 0;
|
||||
|
||||
export const toasts = {
|
||||
get value() {
|
||||
return _toasts;
|
||||
}
|
||||
};
|
||||
|
||||
export function toast(message: string, type: ToastType = 'info', duration = 3000) {
|
||||
const id = _nextId++;
|
||||
_toasts.push({ id, message, type });
|
||||
setTimeout(() => {
|
||||
_toasts = _toasts.filter((t) => t.id !== id);
|
||||
}, duration);
|
||||
}
|
||||
Reference in New Issue
Block a user