import './bootstrap.js'; /* * Welcome to your app's main JavaScript file! * * We recommend including the built version of this JavaScript file * (and its CSS file) in your base layout (base.html.twig). */ // any CSS you import will output into a single css file (app.css in this case) import './styles/tailwind.css'; import './styles/app.css'; // Notification System class NotificationSystem { constructor() { this.container = null; this.init(); } init() { // Create notification container if it doesn't exist this.container = document.getElementById('notification-container'); if (!this.container) { this.container = document.createElement('div'); this.container.id = 'notification-container'; this.container.className = 'notification-container'; document.body.appendChild(this.container); } } show(message, type = 'info', title = '', duration = 5000) { const notification = document.createElement('div'); notification.className = `notification ${type}`; const icon = this.getIcon(type); const closeIcon = this.getCloseIcon(); notification.innerHTML = `
${icon}
${title ? `
${title}
` : ''}
${message}
`; // Add progress bar animation const style = document.createElement('style'); style.textContent = ` @keyframes progress { from { width: 100%; } to { width: 0%; } } `; document.head.appendChild(style); this.container.appendChild(notification); // Trigger animation setTimeout(() => { notification.classList.add('show'); }, 10); // Auto remove if (duration > 0) { setTimeout(() => { this.remove(notification); }, duration); } return notification; } getIcon(type) { const icons = { success: ` `, error: ` `, warning: ` `, info: ` ` }; return icons[type] || icons.info; } getCloseIcon() { return ` `; } remove(notification) { if (notification && notification.parentElement) { notification.classList.remove('show'); setTimeout(() => { if (notification.parentElement) { notification.remove(); } }, 300); } } success(message, title = 'موفقیت') { return this.show(message, 'success', title); } error(message, title = 'خطا') { return this.show(message, 'error', title); } warning(message, title = 'هشدار') { return this.show(message, 'warning', title); } info(message, title = 'اطلاعات') { return this.show(message, 'info', title); } } // Initialize notification system const notification = new NotificationSystem(); // Make notification available globally window.notification = notification; // Test notification system setTimeout(() => { if (window.notification) { console.log('Notification system initialized successfully'); } }, 1000); // Blog functionality document.addEventListener('DOMContentLoaded', function() { // Blog search enhancement const searchInput = document.querySelector('input[name="search"]'); if (searchInput) { searchInput.addEventListener('focus', function() { this.classList.add('blog-search-input'); }); searchInput.addEventListener('blur', function() { this.classList.remove('blog-search-input'); }); } // Blog card hover effects const blogCards = document.querySelectorAll('.group.bg-white.rounded-2xl'); blogCards.forEach(card => { card.classList.add('blog-card-hover'); }); // Pagination hover effects const paginationItems = document.querySelectorAll('nav a, nav span'); paginationItems.forEach(item => { item.classList.add('pagination-item'); }); // Smooth scroll for anchor links const anchorLinks = document.querySelectorAll('a[href^="#"]'); anchorLinks.forEach(link => { link.addEventListener('click', function(e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // Lazy loading for images const images = document.querySelectorAll('img[data-src]'); const imageObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.classList.remove('blog-loading'); img.classList.add('opacity-100'); observer.unobserve(img); } }); }); images.forEach(img => { img.classList.add('blog-loading', 'opacity-0'); imageObserver.observe(img); }); // Reading progress bar for blog posts const blogPost = document.querySelector('main.min-h-screen.bg-gray-50'); if (blogPost) { const progressBar = document.createElement('div'); progressBar.className = 'fixed top-0 left-0 w-0 h-1 bg-gradient-to-r from-blue-500 to-purple-500 z-50 transition-all duration-300'; document.body.appendChild(progressBar); window.addEventListener('scroll', function() { const scrollTop = window.pageYOffset; const docHeight = document.body.scrollHeight - window.innerHeight; const scrollPercent = (scrollTop / docHeight) * 100; progressBar.style.width = scrollPercent + '%'; }); } // Copy code blocks functionality const codeBlocks = document.querySelectorAll('pre code'); codeBlocks.forEach(block => { const copyButton = document.createElement('button'); copyButton.className = 'absolute top-2 left-2 px-2 py-1 text-xs bg-gray-800 text-white rounded opacity-0 group-hover:opacity-100 transition-opacity duration-200'; copyButton.textContent = 'کپی'; const wrapper = document.createElement('div'); wrapper.className = 'relative group'; wrapper.style.position = 'relative'; block.parentNode.insertBefore(wrapper, block); wrapper.appendChild(block); wrapper.appendChild(copyButton); copyButton.addEventListener('click', function() { navigator.clipboard.writeText(block.textContent).then(() => { copyButton.textContent = 'کپی شد!'; setTimeout(() => { copyButton.textContent = 'کپی'; }, 2000); }); }); }); // Q&A functionality // Add hover effects to Q&A cards const qaCards = document.querySelectorAll('.bg-white.rounded-2xl.shadow-soft'); qaCards.forEach(card => { card.classList.add('qa-card-hover'); }); // Add hover effects to Q&A tags const qaTags = document.querySelectorAll('.inline-flex.items-center.px-3.py-1'); qaTags.forEach(tag => { tag.classList.add('qa-tag-hover'); }); // Add focus effects to Q&A form inputs const qaInputs = document.querySelectorAll('input, textarea, select'); qaInputs.forEach(input => { input.classList.add('qa-form-focus'); }); // Add hover effects to Q&A pagination const qaPaginationItems = document.querySelectorAll('nav a, nav span'); qaPaginationItems.forEach(item => { item.classList.add('qa-pagination-item'); }); // Add stagger animation to Q&A cards qaCards.forEach((card, index) => { card.classList.add('qa-fade-in-up'); if (index < 4) { card.classList.add(`qa-stagger-${index + 1}`); } }); // Q&A search enhancement const qaSearchInput = document.querySelector('input[name="search"]'); if (qaSearchInput) { qaSearchInput.addEventListener('focus', function() { this.parentElement.classList.add('ring-2', 'ring-blue-500'); }); qaSearchInput.addEventListener('blur', function() { this.parentElement.classList.remove('ring-2', 'ring-blue-500'); }); } // Q&A filter enhancement const qaFilterSelect = document.querySelector('select[name="filter"]'); if (qaFilterSelect) { qaFilterSelect.addEventListener('change', function() { this.classList.add('bg-blue-50', 'border-blue-300'); setTimeout(() => { this.classList.remove('bg-blue-50', 'border-blue-300'); }, 300); }); } // Q&A vote button enhancement const qaVoteButtons = document.querySelectorAll('.vote-btn'); qaVoteButtons.forEach(button => { button.classList.add('qa-vote-btn'); }); // Q&A tag selection enhancement const qaTagSuggestions = document.querySelectorAll('.tag-suggestion'); qaTagSuggestions.forEach(tag => { tag.addEventListener('click', function() { this.classList.toggle('qa-tag-selected'); this.classList.toggle('qa-tag-unselected'); }); }); // Q&A answer acceptance enhancement const qaAcceptButtons = document.querySelectorAll('.accept-answer-btn'); qaAcceptButtons.forEach(button => { button.addEventListener('click', function() { this.classList.add('bg-green-200', 'text-green-800'); setTimeout(() => { this.classList.remove('bg-green-200', 'text-green-800'); }, 1000); }); }); // Q&A loading states const qaForms = document.querySelectorAll('form'); qaForms.forEach(form => { form.addEventListener('submit', function() { const submitButton = this.querySelector('button[type="submit"]'); if (submitButton) { submitButton.classList.add('qa-loading-shimmer'); submitButton.disabled = true; } }); }); });