| | |
| |
|
| | |
| | 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(); |
| | }); |