// pages/ResourcesPage.js - Resources page functionality for SPA import { homeBackgroundImage, areasData } from '../data/areas.js'; import { pressMentions } from '../data/press.js'; import { sampleResources } from '../data/resources.js'; import { createResourceCard, initializeResourceCards } from '../cards/ResourceCard.js'; export function renderResourcesPage() { const content = `

Technical Resources

Our team develops technical artifacts in the course of their research and policy work. These resources include datasets, tools, benchmarks, and interactive applications that support transparency, reproducibility, and accessibility in AI research and governance.

Press Mentions

${pressMentions.map((article, index) => { // Get the primary area for this article const primaryArea = areasData[article.areaTags[0]]; const primaryColor = primaryArea?.primaryColor || 'gray'; // Determine alignment (alternating) const isLeftAligned = index % 2 === 0; const alignment = isLeftAligned ? 'justify-start' : 'justify-end'; const borderSide = isLeftAligned ? 'border-l-2' : 'border-r-2'; const borderColor = `border-${primaryColor}-200`; // Format date const formattedDate = new Date(article.date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); // Generate area tags const areaTagsHtml = article.areaTags.map(areaId => { const area = areasData[areaId]; if (!area) return ''; return `${area.name}`; }).join(''); // Generate sub-area tags const subAreaTagsHtml = article.subAreaTags.map(subAreaKey => { // Find the sub-area name and color by looking through all areas let subAreaName = subAreaKey; // fallback to key if not found let textColor = 'text-gray-700'; // fallback color for (const areaKey in areasData) { const area = areasData[areaKey]; if (area.subAreas && area.subAreas[subAreaKey]) { const subArea = area.subAreas[subAreaKey]; subAreaName = typeof subArea === 'string' ? subArea : subArea.name; // Extract just the text color from the sub-area color if (typeof subArea === 'object' && subArea.color) { const colorMatch = subArea.color.match(/text-(\w+)-(\d+)/); if (colorMatch) { textColor = `text-${colorMatch[1]}-700`; // Use consistent 700 shade } } break; } } return `${subAreaName}`; }).join(''); return `

"${article.title}"

${article.source} ${formattedDate}
${areaTagsHtml} ${subAreaTagsHtml}
`; }).join('')}
`; return { content, init: () => { // Initialize background attribution initializeResourcesBackgroundAttribution(); // Initialize resource cards initializeResourceCards(); // Initialize resource carousel initializeResourceCarousel(); } }; } function initializeResourcesBackgroundAttribution() { const backgroundContainer = document.getElementById('resources-background-container'); const attribution = document.getElementById('resources-bg-attribution'); if (!backgroundContainer || !attribution) { return; } // Show attribution on hover over the background container backgroundContainer.addEventListener('mouseenter', () => { attribution.style.opacity = '1'; }); backgroundContainer.addEventListener('mouseleave', () => { attribution.style.opacity = '0'; }); } function initializeResourceCarousel() { const carousel = document.querySelector('.resource-carousel'); const prevButton = document.querySelector('.resource-carousel-prev'); const nextButton = document.querySelector('.resource-carousel-next'); if (!carousel || !prevButton || !nextButton) { return; } let currentIndex = 0; const cardWidth = 624; // 600px card + 24px gap const visibleCards = 1.5; // Show 1.5 cards at a time (larger cards) const maxIndex = sampleResources.length - 1; // Allow going to the last item function updateCarousel() { const translateX = -currentIndex * cardWidth; carousel.style.transform = `translateX(${translateX}px)`; // Update button states - no disabled states for looping prevButton.classList.remove('opacity-50', 'cursor-not-allowed'); nextButton.classList.remove('opacity-50', 'cursor-not-allowed'); } prevButton.addEventListener('click', () => { currentIndex = currentIndex > 0 ? currentIndex - 1 : maxIndex; updateCarousel(); }); nextButton.addEventListener('click', () => { currentIndex = currentIndex < maxIndex ? currentIndex + 1 : 0; updateCarousel(); }); // Initialize carousel state updateCarousel(); }