138 lines
3.8 KiB
JavaScript
138 lines
3.8 KiB
JavaScript
/* Base JavaScript Utilities */
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize tooltips and popovers
|
|
initializeBootstrap();
|
|
|
|
// Setup flash message auto-close
|
|
setupFlashMessages();
|
|
|
|
// Setup theme toggle if available
|
|
setupThemeToggle();
|
|
});
|
|
|
|
/**
|
|
* Initialize Bootstrap tooltips and popovers
|
|
*/
|
|
function initializeBootstrap() {
|
|
// Initialize all tooltips
|
|
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
|
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
|
return new bootstrap.Tooltip(tooltipTriggerEl);
|
|
});
|
|
|
|
// Initialize all popovers
|
|
const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
|
|
popoverTriggerList.map(function (popoverTriggerEl) {
|
|
return new bootstrap.Popover(popoverTriggerEl);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Auto-close flash messages after 5 seconds
|
|
*/
|
|
function setupFlashMessages() {
|
|
const alerts = document.querySelectorAll('.alert');
|
|
alerts.forEach(function(alert) {
|
|
setTimeout(function() {
|
|
const bsAlert = new bootstrap.Alert(alert);
|
|
bsAlert.close();
|
|
}, 5000);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Setup theme toggle functionality
|
|
*/
|
|
function setupThemeToggle() {
|
|
const themeToggle = document.getElementById('theme-toggle');
|
|
if (!themeToggle) return;
|
|
|
|
const currentTheme = localStorage.getItem('theme') || 'light';
|
|
applyTheme(currentTheme);
|
|
|
|
themeToggle.addEventListener('click', function() {
|
|
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
|
applyTheme(newTheme);
|
|
localStorage.setItem('theme', newTheme);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Apply theme to document
|
|
*/
|
|
function applyTheme(theme) {
|
|
const html = document.documentElement;
|
|
if (theme === 'dark') {
|
|
html.setAttribute('data-bs-theme', 'dark');
|
|
} else {
|
|
html.removeAttribute('data-bs-theme');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display a toast notification
|
|
*/
|
|
function showToast(message, type = 'info') {
|
|
const toastHtml = `
|
|
<div class="toast align-items-center text-white bg-${type} border-0" role="alert" aria-live="assertive" aria-atomic="true">
|
|
<div class="d-flex">
|
|
<div class="toast-body">
|
|
${message}
|
|
</div>
|
|
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
const toastContainer = document.querySelector('.toast-container') || createToastContainer();
|
|
toastContainer.insertAdjacentHTML('beforeend', toastHtml);
|
|
|
|
const toastElement = toastContainer.lastElementChild;
|
|
const toast = new bootstrap.Toast(toastElement);
|
|
toast.show();
|
|
|
|
toastElement.addEventListener('hidden.bs.toast', function() {
|
|
toastElement.remove();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create toast container if it doesn't exist
|
|
*/
|
|
function createToastContainer() {
|
|
const container = document.createElement('div');
|
|
container.className = 'toast-container position-fixed bottom-0 end-0 p-3';
|
|
document.body.appendChild(container);
|
|
return container;
|
|
}
|
|
|
|
/**
|
|
* Debounce function for input handlers
|
|
*/
|
|
function debounce(func, wait) {
|
|
let timeout;
|
|
return function executedFunction(...args) {
|
|
const later = () => {
|
|
clearTimeout(timeout);
|
|
func(...args);
|
|
};
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(later, wait);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Throttle function for scroll/resize handlers
|
|
*/
|
|
function throttle(func, limit) {
|
|
let inThrottle;
|
|
return function(...args) {
|
|
if (!inThrottle) {
|
|
func.apply(this, args);
|
|
inThrottle = true;
|
|
setTimeout(() => inThrottle = false, limit);
|
|
}
|
|
};
|
|
}
|