document.addEventListener('DOMContentLoaded', async () => {
const grid = document.querySelector('[data-material-grid]');
const searchInput = document.querySelector('[data-search]');
const categoriesContainer = document.querySelector('[data-categories]');
let materialLibrary = [];
try {
const response = await fetch('materials.json');
const data = await response.json();
materialLibrary = data.materials;
const categories = [...new Set(materialLibrary.flatMap(m => m.categories))].sort();
categoriesContainer.innerHTML = categories.map(category => `
`).join('');
const categoryButtons = categoriesContainer.querySelectorAll('.category-button');
categoryButtons.forEach(button => {
button.addEventListener('click', () => {
button.classList.toggle('active');
filterMaterials();
});
});
} catch (error) {
console.error('Error loading materials:', error);
grid.innerHTML = '
Error loading materials library.
';
return;
}
function renderMaterials(materials) {
grid.innerHTML = materials.map(material => `
${material.name}
${material.author}
${material.description}
${material.categories.map(cat => `${cat}`).join(' ')}
`).join('');
const tags = document.querySelectorAll('.tag');
tags.forEach(tag => {
tag.addEventListener('click', (e) => {
e.stopPropagation();
const category = tag.dataset.tag;
const categoryButton = [...document.querySelectorAll('.category-button')]
.find(button => button.dataset.category === category);
if (categoryButton) {
categoryButton.classList.add('active');
filterMaterials();
}
});
});
reSortMasonry();
}
async function loadSnippet(snippetPath) {
try {
const response = await fetch(snippetPath);
return await response.text();
} catch (error) {
console.error('Error loading snippet:', error);
return '';
}
}
async function filterMaterials() {
const searchTerm = searchInput.value.toLowerCase();
const selectedCategories = [...document.querySelectorAll('.category-button.active')]
.map(button => button.dataset.category);
const filtered = materialLibrary.filter(material => {
const matchesSearch = material.name.toLowerCase().includes(searchTerm) ||
material.description.toLowerCase().includes(searchTerm) ||
material.categories.some(cat => cat.toLowerCase().includes(searchTerm));
const matchesCategory = selectedCategories.every(cat => material.categories.includes(cat));
return matchesSearch && matchesCategory;
});
const allCards = document.querySelectorAll('.material-card');
allCards.forEach(card => {
card.classList.add('hide');
});
setTimeout(() => {
renderMaterials(filtered);
}, 300);
}
function reSortMasonry() {
const masonryItems = document.querySelectorAll('.material-card');
let transitionCount = masonryItems.length;
masonryItems.forEach(item => {
item.style.display = 'inline-block';
item.addEventListener('transitionend', () => {
transitionCount--;
if (transitionCount === 0) {
grid.style.columnCount = 0;
grid.style.columnCount = 4;
}
}, { once: true });
});
}
grid.addEventListener('click', async (e) => {
const card = e.target.closest('.material-card');
if (card && !e.target.classList.contains('tag')) {
const materialName = card.dataset.material;
const material = materialLibrary.find(m =>
m.name.toLowerCase().replace(/\s+/g, '-') === materialName
);
const snippet = await loadSnippet(material.snippet);
navigator.clipboard.writeText(snippet);
const notification = document.createElement('div');
notification.className = 'copy-notification';
notification.textContent = 'Copied!';
notification.style.left = `${e.clientX}px`;
notification.style.top = `${e.clientY - 30}px`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 1000);
}
});
grid.addEventListener('dragstart', (e) => {
e.preventDefault();
});
searchInput.addEventListener('input', filterMaterials);
renderMaterials(materialLibrary);
});