332 lines
No EOL
9.6 KiB
Svelte
332 lines
No EOL
9.6 KiB
Svelte
<script lang="ts">
|
|
import { createEventDispatcher, onMount } from 'svelte';
|
|
import { fade } from 'svelte/transition';
|
|
|
|
// Define color presets
|
|
const colorPresets = {
|
|
primary: 'var(--accent-glow)',
|
|
secondary: 'var(--text-secondary)',
|
|
success: '#4CAF50',
|
|
warning: '#FFC107',
|
|
error: '#F44336',
|
|
info: '#2196F3',
|
|
default: 'var(--text-primary)'
|
|
};
|
|
|
|
type ButtonType = 'primary' | 'secondary' | 'text' | 'outlined';
|
|
type ColorPreset = keyof typeof colorPresets;
|
|
|
|
interface ButtonConfig {
|
|
text: string;
|
|
type: ButtonType;
|
|
href?: string;
|
|
color?: ColorPreset;
|
|
icon?: string;
|
|
action?: () => void;
|
|
}
|
|
|
|
// Props
|
|
export let open = false;
|
|
export let message: string = '';
|
|
export let icon: string = '';
|
|
export let iconColor: ColorPreset = 'primary';
|
|
export let buttons: ButtonConfig[] = [];
|
|
export let customImage: string = '';
|
|
export let autoClose: number = 1500; // 0 means no auto close
|
|
|
|
// Event dispatcher
|
|
const dispatch = createEventDispatcher();
|
|
|
|
// Close the notification
|
|
function close() {
|
|
open = false;
|
|
dispatch('close');
|
|
}
|
|
|
|
// Handle button click
|
|
function handleButtonClick(button: ButtonConfig) {
|
|
if (button.action) {
|
|
button.action();
|
|
}
|
|
if (!button.href) {
|
|
close();
|
|
}
|
|
}
|
|
|
|
// Auto close functionality
|
|
onMount(() => {
|
|
if (autoClose > 0 && open) {
|
|
const timer = setTimeout(() => {
|
|
close();
|
|
}, autoClose);
|
|
|
|
return () => {
|
|
clearTimeout(timer);
|
|
};
|
|
}
|
|
});
|
|
|
|
// Get color from preset or return the input if not a preset
|
|
function getColor(colorName: ColorPreset): string {
|
|
return colorPresets[colorName] || colorName;
|
|
}
|
|
|
|
// Get button class based on type
|
|
function getButtonClass(type: ButtonType): string {
|
|
switch(type) {
|
|
case 'primary':
|
|
return 'btn-primary';
|
|
case 'secondary':
|
|
return 'btn-secondary';
|
|
case 'text':
|
|
return 'btn-text';
|
|
case 'outlined':
|
|
return 'btn-outlined';
|
|
default:
|
|
return 'btn-primary';
|
|
}
|
|
}
|
|
</script>
|
|
|
|
{#if open}
|
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
<div class="notification-overlay" transition:fade={{ duration: 200 }} on:click={close}>
|
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
<div class="notification-container" on:click|stopPropagation>
|
|
<div class="notification-content">
|
|
{#if customImage}
|
|
<div class="custom-image">
|
|
{#if customImage.endsWith('.svg')}
|
|
{@html customImage}
|
|
{:else}
|
|
<!-- svelte-ignore a11y-img-redundant-alt -->
|
|
<img src={customImage} alt="Notification image" />
|
|
{/if}
|
|
</div>
|
|
{:else if icon}
|
|
<div class="notification-icon" style="color: {getColor(iconColor)}">
|
|
<span class="material-icons">{icon}</span>
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="notification-message">
|
|
{message}
|
|
</div>
|
|
|
|
{#if buttons.length > 0}
|
|
<div class="notification-actions">
|
|
{#each buttons as button}
|
|
{#if button.href}
|
|
<a
|
|
href={button.href}
|
|
class="notification-button {getButtonClass(button.type)}"
|
|
style={button.color ? `--button-color: ${getColor(button.color)};` : ''}
|
|
>
|
|
{#if button.icon}
|
|
<span class="material-icons" style={button.color ? `color: ${getColor(button.color)};` : ''}>
|
|
{button.icon}
|
|
</span>
|
|
{/if}
|
|
<span>{button.text}</span>
|
|
</a>
|
|
{:else}
|
|
<button
|
|
class="notification-button {getButtonClass(button.type)}"
|
|
style={button.color ? `--button-color: ${getColor(button.color)};` : ''}
|
|
on:click={() => handleButtonClick(button)}
|
|
>
|
|
{#if button.icon}
|
|
<span class="material-icons" style={button.color ? `color: ${getColor(button.color)};` : ''}>
|
|
{button.icon}
|
|
</span>
|
|
{/if}
|
|
<span>{button.text}</span>
|
|
</button>
|
|
{/if}
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
<style>
|
|
.notification-overlay {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 1000;
|
|
backdrop-filter: blur(3px);
|
|
}
|
|
|
|
.notification-container {
|
|
background-color: var(--bg-secondary);
|
|
border-radius: 8px;
|
|
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3);
|
|
width: 90%;
|
|
max-width: 500px;
|
|
border: 1px solid var(--border-color);
|
|
overflow: hidden;
|
|
animation: popup 0.3s ease forwards;
|
|
}
|
|
|
|
@keyframes popup {
|
|
0% { transform: scale(0.9); opacity: 0; }
|
|
100% { transform: scale(1); opacity: 1; }
|
|
}
|
|
|
|
.notification-content {
|
|
padding: 2rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.notification-icon {
|
|
font-size: 3rem;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.notification-icon .material-icons {
|
|
font-size: 3.5rem;
|
|
text-shadow: 0 0 10px var(--accent-glow);
|
|
}
|
|
|
|
.custom-image {
|
|
max-width: 120px;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.custom-image img {
|
|
width: 100%;
|
|
height: auto;
|
|
}
|
|
|
|
.notification-message {
|
|
text-align: center;
|
|
font-size: 1.25rem;
|
|
color: var(--text-primary);
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.notification-actions {
|
|
display: flex;
|
|
gap: 1rem;
|
|
justify-content: center;
|
|
margin-top: 1rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.notification-button {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0.75rem 1.5rem;
|
|
border-radius: 4px;
|
|
font-weight: 600;
|
|
font-size: 1rem;
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
text-decoration: none;
|
|
justify-content: center;
|
|
--button-color: var(--accent-color);
|
|
}
|
|
|
|
.notification-button .material-icons {
|
|
font-size: 1.25rem;
|
|
}
|
|
|
|
.btn-primary {
|
|
background-color: var(--button-color, var(--accent-color));
|
|
color: white;
|
|
border: none;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
background-color: var(--accent-hover);
|
|
box-shadow: 0 0 15px var(--accent-glow);
|
|
}
|
|
|
|
.btn-secondary {
|
|
background-color: transparent;
|
|
color: var(--button-color, var(--accent-color));
|
|
border: 1px solid var(--button-color, var(--accent-color));
|
|
}
|
|
|
|
.btn-secondary:hover {
|
|
background-color: rgba(var(--accent-color-rgb), 0.1);
|
|
}
|
|
|
|
.btn-text {
|
|
background-color: transparent;
|
|
color: var(--button-color, var(--accent-color));
|
|
border: none;
|
|
padding: 0.75rem 1rem;
|
|
}
|
|
|
|
.btn-text:hover {
|
|
background-color: rgba(var(--accent-color-rgb), 0.1);
|
|
}
|
|
|
|
.btn-outlined {
|
|
background-color: transparent;
|
|
color: var(--button-color, var(--accent-color));
|
|
border: 1px solid var(--button-color, var(--accent-color));
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.btn-outlined::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 3px;
|
|
background: linear-gradient(90deg, transparent, var(--button-color, var(--accent-color)), transparent);
|
|
transform: translateX(-100%);
|
|
transition: transform 0.3s ease;
|
|
opacity: 0;
|
|
}
|
|
|
|
.btn-outlined:hover {
|
|
text-shadow: 0 0 8px var(--accent-glow);
|
|
}
|
|
|
|
.btn-outlined:hover::after {
|
|
transform: translateX(0);
|
|
opacity: 1;
|
|
box-shadow: 0 0 15px var(--accent-glow);
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.notification-content {
|
|
padding: 1.5rem;
|
|
}
|
|
|
|
.notification-message {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.notification-actions {
|
|
flex-direction: column;
|
|
width: 100%;
|
|
}
|
|
|
|
.notification-button {
|
|
width: 100%;
|
|
}
|
|
}
|
|
</style> |