dawarich/app/javascript/maps_maplibre/components/toast.js
Evgenii Burmakin 8934c29fce
0.36.2 (#2007)
* fix: move foreman to global gems to fix startup crash (#1971)

* Update exporting code to stream points data to file in batches to red… (#1980)

* Update exporting code to stream points data to file in batches to reduce memory usage

* Update changelog

* Update changelog

* Feature/maplibre frontend (#1953)

* Add a plan to use MapLibre GL JS for the frontend map rendering, replacing Leaflet

* Implement phase 1

* Phases 1-3 + part of 4

* Fix e2e tests

* Phase 6

* Implement fog of war

* Phase 7

* Next step: fix specs, phase 7 done

* Use our own map tiles

* Extract v2 map logic to separate manager classes

* Update settings panel on v2 map

* Update v2 e2e tests structure

* Reimplement location search in maps v2

* Update speed routes

* Implement visits and places creation in v2

* Fix last failing test

* Implement visits merging

* Fix a routes e2e test and simplify the routes layer styling.

* Extract js to modules from maps_v2_controller.js

* Implement area creation

* Fix spec problem

* Fix some e2e tests

* Implement live mode in v2 map

* Update icons and panel

* Extract some styles

* Remove unused file

* Start adding dark theme to popups on MapLibre maps

* Make popups respect dark theme

* Move v2 maps to maplibre namespace

* Update v2 references to maplibre

* Put place, area and visit info into side panel

* Update API to use safe settings config method

* Fix specs

* Fix method name to config in SafeSettings and update usages accordingly

* Add missing public files

* Add handling for real time points

* Fix remembering enabled/disabled layers of the v2 map

* Fix lots of e2e tests

* Add settings to select map version

* Use maps/v2 as main path for MapLibre maps

* Update routing

* Update live mode

* Update maplibre controller

* Update changelog

* Remove some console.log statements

---------

Co-authored-by: Robin Tuszik <mail@robin.gg>
2025-12-06 20:54:49 +01:00

183 lines
3.9 KiB
JavaScript

/**
* Toast notification system
* Displays temporary notifications in the top-right corner
*/
export class Toast {
static container = null
/**
* Initialize toast container
*/
static init() {
if (this.container) return
this.container = document.createElement('div')
this.container.className = 'toast-container'
this.container.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 12px;
pointer-events: none;
`
document.body.appendChild(this.container)
// Add CSS animations
this.addStyles()
}
/**
* Add CSS animations for toasts
*/
static addStyles() {
if (document.getElementById('toast-styles')) return
const style = document.createElement('style')
style.id = 'toast-styles'
style.textContent = `
@keyframes toast-slide-in {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes toast-slide-out {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(400px);
opacity: 0;
}
}
.toast {
pointer-events: auto;
animation: toast-slide-in 0.3s ease-out;
}
.toast.removing {
animation: toast-slide-out 0.3s ease-out;
}
`
document.head.appendChild(style)
}
/**
* Show toast notification
* @param {string} message - Message to display
* @param {string} type - Toast type: 'success', 'error', 'info', 'warning'
* @param {number} duration - Duration in milliseconds (default 3000)
*/
static show(message, type = 'info', duration = 3000) {
this.init()
const toast = document.createElement('div')
toast.className = `toast toast-${type}`
toast.textContent = message
toast.style.cssText = `
padding: 12px 20px;
background: ${this.getBackgroundColor(type)};
color: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
font-size: 14px;
font-weight: 500;
max-width: 300px;
line-height: 1.4;
`
this.container.appendChild(toast)
// Auto dismiss after duration
if (duration > 0) {
setTimeout(() => {
this.dismiss(toast)
}, duration)
}
return toast
}
/**
* Dismiss a toast
* @param {HTMLElement} toast - Toast element to dismiss
*/
static dismiss(toast) {
toast.classList.add('removing')
setTimeout(() => {
toast.remove()
}, 300)
}
/**
* Get background color for toast type
* @param {string} type - Toast type
* @returns {string} CSS color
*/
static getBackgroundColor(type) {
const colors = {
success: '#22c55e',
error: '#ef4444',
warning: '#f59e0b',
info: '#3b82f6'
}
return colors[type] || colors.info
}
/**
* Show success toast
* @param {string} message
* @param {number} duration
*/
static success(message, duration = 3000) {
return this.show(message, 'success', duration)
}
/**
* Show error toast
* @param {string} message
* @param {number} duration
*/
static error(message, duration = 4000) {
return this.show(message, 'error', duration)
}
/**
* Show warning toast
* @param {string} message
* @param {number} duration
*/
static warning(message, duration = 3500) {
return this.show(message, 'warning', duration)
}
/**
* Show info toast
* @param {string} message
* @param {number} duration
*/
static info(message, duration = 3000) {
return this.show(message, 'info', duration)
}
/**
* Clear all toasts
*/
static clearAll() {
if (!this.container) return
const toasts = this.container.querySelectorAll('.toast')
toasts.forEach(toast => this.dismiss(toast))
}
}