devusman's picture
Project detail:
df8b402 verified
// Global scripts that will be used across all pages
// Initialize tooltips
function initTooltips() {
const tooltipTriggers = document.querySelectorAll('[data-tooltip]');
tooltipTriggers.forEach(trigger => {
const tooltip = document.createElement('div');
tooltip.className = 'tooltip-text bg-gray-800 text-white text-xs rounded py-1 px-2 absolute z-50 invisible';
tooltip.textContent = trigger.dataset.tooltip;
trigger.appendChild(tooltip);
trigger.addEventListener('mouseenter', () => {
tooltip.classList.remove('invisible');
tooltip.classList.add('visible');
});
trigger.addEventListener('mouseleave', () => {
tooltip.classList.remove('visible');
tooltip.classList.add('invisible');
});
});
}
// Initialize modals
function initModals() {
const modalButtons = document.querySelectorAll('[data-modal-toggle]');
modalButtons.forEach(button => {
const modalId = button.dataset.modalToggle;
const modal = document.getElementById(modalId);
const closeButtons = modal.querySelectorAll('[data-modal-hide]');
button.addEventListener('click', () => {
modal.classList.remove('hidden');
modal.classList.add('flex');
document.body.classList.add('overflow-hidden');
});
closeButtons.forEach(closeButton => {
closeButton.addEventListener('click', () => {
modal.classList.remove('flex');
modal.classList.add('hidden');
document.body.classList.remove('overflow-hidden');
});
});
// Close when clicking outside
modal.addEventListener('click', (e) => {
if (e.target === modal) {
modal.classList.remove('flex');
modal.classList.add('hidden');
document.body.classList.remove('overflow-hidden');
}
});
});
}
// Mobile menu toggle
function initMobileMenu() {
const mobileMenuButton = document.querySelector('[data-collapse-toggle="mobile-menu"]');
if (!mobileMenuButton) return;
const mobileMenu = document.getElementById('mobile-menu');
mobileMenuButton.addEventListener('click', () => {
mobileMenu.classList.toggle('hidden');
const expanded = mobileMenuButton.getAttribute('aria-expanded') === 'true';
mobileMenuButton.setAttribute('aria-expanded', !expanded);
});
}
// Dropdown toggle
function initDropdowns() {
const dropdownButtons = document.querySelectorAll('[data-dropdown-toggle]');
dropdownButtons.forEach(button => {
const dropdownId = button.dataset.dropdownToggle;
const dropdown = document.getElementById(dropdownId);
button.addEventListener('click', () => {
dropdown.classList.toggle('hidden');
});
// Close when clicking outside
document.addEventListener('click', (e) => {
if (!button.contains(e.target) && !dropdown.contains(e.target)) {
dropdown.classList.add('hidden');
}
});
});
}
// Tab functionality
function initTabs() {
const tabButtons = document.querySelectorAll('[data-tab-toggle]');
tabButtons.forEach(button => {
button.addEventListener('click', () => {
const tabId = button.dataset.tabToggle;
const tabContainer = button.closest('[data-tab-container]');
// Hide all tabs in the container
tabContainer.querySelectorAll('[data-tab-content]').forEach(tab => {
tab.classList.add('hidden');
});
// Show the selected tab
document.getElementById(tabId).classList.remove('hidden');
// Update active state of buttons
tabContainer.querySelectorAll('[data-tab-toggle]').forEach(btn => {
btn.classList.remove('active');
btn.classList.add('text-gray-400');
btn.classList.remove('text-white', 'border-purple-500');
});
button.classList.add('active', 'text-white', 'border-purple-500');
button.classList.remove('text-gray-400');
});
});
}
// Initialize everything when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
initTooltips();
initModals();
initMobileMenu();
initDropdowns();
initTabs();
// Smooth scroll for anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId === '#') return;
const targetElement = document.querySelector(targetId);
if (targetElement) {
targetElement.scrollIntoView({
behavior: 'smooth'
});
}
});
});
});
// Price ticker functionality
class PriceTicker {
constructor() {
this.ticker = document.querySelector('custom-price-ticker');
if (!this.ticker) return;
this.coins = [
{ symbol: 'BTC', name: 'Bitcoin', price: 0, change: 0 },
{ symbol: 'ETH', name: 'Ethereum', price: 0, change: 0 },
{ symbol: 'XRP', name: 'Ripple', price: 0, change: 0 },
{ symbol: 'LTC', name: 'Litecoin', price: 0, change: 0 },
{ symbol: 'ADA', name: 'Cardano', price: 0, change: 0 },
{ symbol: 'DOGE', name: 'Dogecoin', price: 0, change: 0 },
];
this.fetchPrices();
}
async fetchPrices() {
try {
// In a real app, you would fetch from a real API
// This is a mock implementation
this.coins = this.coins.map(coin => {
const basePrice = Math.random() * 10000 + 1000;
const price = parseFloat(basePrice.toFixed(2));
const change = parseFloat((Math.random() * 10 - 5).toFixed(2));
return {
...coin,
price,
change
};
});
this.render();
// Update prices every 10 seconds
setTimeout(() => this.fetchPrices(), 10000);
} catch (error) {
console.error('Error fetching prices:', error);
}
}
render() {
if (!this.ticker) return;
this.ticker.innerHTML = `
<div class="overflow-hidden">
<div class="inline-block whitespace-nowrap animate-marquee">
${this.coins.map(coin => `
<div class="inline-flex items-center mx-8">
<span class="font-bold mr-2">${coin.symbol}</span>
<span class="mr-2">$${coin.price.toLocaleString()}</span>
<span class="${coin.change >= 0 ? 'text-green-400' : 'text-red-400'}">
${coin.change >= 0 ? '+' : ''}${coin.change}%
</span>
</div>
`).join('')}
</div>
</div>
`;
}
startAnimation() {
if (!this.ticker) return;
const marquee = this.ticker.querySelector('.animate-marquee');
if (!marquee) return;
// Calculate animation duration based on content width
const contentWidth = marquee.scrollWidth;
const duration = contentWidth / 50; // Adjust speed as needed
marquee.style.animationDuration = `${duration}s`;
}
}
// Initialize price ticker when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
new PriceTicker();
});