|
|
|
|
|
|
|
|
|
|
|
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'); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
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'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
modal.addEventListener('click', (e) => { |
|
|
if (e.target === modal) { |
|
|
modal.classList.remove('flex'); |
|
|
modal.classList.add('hidden'); |
|
|
document.body.classList.remove('overflow-hidden'); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
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'); |
|
|
}); |
|
|
|
|
|
|
|
|
document.addEventListener('click', (e) => { |
|
|
if (!button.contains(e.target) && !dropdown.contains(e.target)) { |
|
|
dropdown.classList.add('hidden'); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
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]'); |
|
|
|
|
|
|
|
|
tabContainer.querySelectorAll('[data-tab-content]').forEach(tab => { |
|
|
tab.classList.add('hidden'); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById(tabId).classList.remove('hidden'); |
|
|
|
|
|
|
|
|
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'); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
initTooltips(); |
|
|
initModals(); |
|
|
initMobileMenu(); |
|
|
initDropdowns(); |
|
|
initTabs(); |
|
|
|
|
|
|
|
|
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' |
|
|
}); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
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 { |
|
|
|
|
|
|
|
|
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(); |
|
|
|
|
|
|
|
|
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; |
|
|
|
|
|
|
|
|
const contentWidth = marquee.scrollWidth; |
|
|
const duration = contentWidth / 50; |
|
|
|
|
|
marquee.style.animationDuration = `${duration}s`; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
new PriceTicker(); |
|
|
}); |