Teste de filtros de projetos
Deploy / Deploy (push) Successful in 1m22s
Details
Deploy / Deploy (push) Successful in 1m22s
Details
This commit is contained in:
parent
dd837d6d6b
commit
984fdf2ca5
|
|
@ -20,63 +20,72 @@ permalink: /projetos/
|
|||
<div style="width: 60px; height: 4px; background-color: #3273dc; margin: 1rem auto; border-radius: 2px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="box has-background-white-ter mb-6" style="border: 1px solid #dbdbdb;">
|
||||
<div class="columns is-vcentered">
|
||||
<div class="column is-narrow">
|
||||
<span class="icon-text has-text-weight-bold has-text-grey">
|
||||
<span class="icon"><i class="fa-solid fa-robot"></i></span>
|
||||
<span>Filtro IA:</span>
|
||||
</span>
|
||||
<div class="box has-background-white-ter mb-6 p-0" style="border: 1px solid #dbdbdb; overflow: hidden;">
|
||||
|
||||
<div class="p-3 is-clickable" id="filter-toggle-btn" style="cursor: pointer; display: flex; align-items: center; justify-content: space-between; background-color: #f5f5f5;">
|
||||
<div class="icon-text has-text-weight-bold has-text-grey-dark">
|
||||
<span class="icon"><i class="fa-solid fa-sliders"></i></span>
|
||||
<span>Filtros, Gêneros & Ordenação</span>
|
||||
</div>
|
||||
|
||||
<div class="column">
|
||||
<div class="buttons" id="genre-filters">
|
||||
<button class="button is-small is-rounded is-active is-info" data-genre="all">Todos</button>
|
||||
<span class="icon transition-icon">
|
||||
<i class="fa-solid fa-chevron-down" id="filter-chevron"></i>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div id="filter-content" style="display: none; border-top: 1px solid #e8e8e8;">
|
||||
<div class="p-4">
|
||||
<div class="columns is-vcentered is-multiline">
|
||||
|
||||
<div class="column is-narrow">
|
||||
<span class="tag is-info is-light has-text-weight-bold">
|
||||
<i class="fa-solid fa-robot mr-1"></i> Filtro IA:
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-narrow">
|
||||
<div class="field has-addons">
|
||||
|
||||
<div class="control">
|
||||
<span class="button is-static is-small is-rounded">
|
||||
<i class="fa-solid fa-star"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="control">
|
||||
<div class="select is-small">
|
||||
<select id="star-filter">
|
||||
<option value="all">Todas Complexidades</option>
|
||||
<option value="5">⭐⭐⭐⭐⭐ (5) Expert</option>
|
||||
<option value="4">⭐⭐⭐⭐ (4+) Avançado</option>
|
||||
<option value="3">⭐⭐⭐ (3+) Intermediário</option>
|
||||
<option value="2">⭐⭐ (2+) Básico</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control"><span class="mx-1"></span></div>
|
||||
|
||||
<div class="control">
|
||||
<a class="button is-static is-small is-rounded">
|
||||
<i class="fa-solid fa-sort"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="control">
|
||||
<div class="select is-small is-rounded">
|
||||
<select id="sort-select">
|
||||
<option value="default">Ordem Padrão</option>
|
||||
<option value="stars_desc">⭐ Mais Complexos</option>
|
||||
<option value="stars_asc">⭐ Mais Simples</option>
|
||||
<option value="intensity_desc">🔥 Mais Intensos (dB)</option>
|
||||
<option value="intensity_asc">❄️ Mais Calmos (dB)</option>
|
||||
<option value="bpm_desc">⚡ BPM (Rápido)</option>
|
||||
<option value="bpm_asc">🐢 BPM (Lento)</option>
|
||||
</select>
|
||||
|
||||
<div class="column">
|
||||
<div class="buttons" id="genre-filters">
|
||||
<button class="button is-small is-rounded is-active is-info" data-genre="all">Todos</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
<div class="field has-addons" style="flex-wrap: wrap; gap: 5px;">
|
||||
|
||||
<div class="control has-icons-left">
|
||||
<div class="select is-small is-rounded">
|
||||
<select id="star-filter">
|
||||
<option value="all">Todas Dificuldades</option>
|
||||
<option value="5">⭐⭐⭐⭐⭐ Expert</option>
|
||||
<option value="4">⭐⭐⭐⭐ Avançado</option>
|
||||
<option value="3">⭐⭐⭐ Intermediário</option>
|
||||
<option value="2">⭐⭐ Básico</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="icon is-small is-left has-text-warning">
|
||||
<i class="fa-solid fa-star"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control has-icons-left">
|
||||
<div class="select is-small is-rounded">
|
||||
<select id="sort-select">
|
||||
<option value="default">Ordem Padrão</option>
|
||||
<option value="stars_desc">⭐ Mais Complexos</option>
|
||||
<option value="stars_asc">⭐ Mais Simples</option>
|
||||
<option value="intensity_desc">🔥 Mais Intensos</option>
|
||||
<option value="intensity_asc">❄️ Mais Calmos</option>
|
||||
<option value="bpm_desc">⚡ BPM (Rápido)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="icon is-small is-left">
|
||||
<i class="fa-solid fa-sort"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -283,8 +292,36 @@ permalink: /projetos/
|
|||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// ===============================================
|
||||
// 1. CONFIGURAÇÃO E MODAL
|
||||
// 1. CONFIGURAÇÃO VISUAL (MENU E MODAL)
|
||||
// ===============================================
|
||||
|
||||
// --- Lógica do Menu Expansível ---
|
||||
const filterBtn = document.getElementById('filter-toggle-btn');
|
||||
const filterContent = document.getElementById('filter-content');
|
||||
const filterChevron = document.getElementById('filter-chevron');
|
||||
let isFilterOpen = false;
|
||||
|
||||
// Abre o menu automaticamente se a tela for grande (Desktop), fecha em Mobile
|
||||
if(window.innerWidth > 1024) {
|
||||
isFilterOpen = true;
|
||||
filterContent.style.display = 'block';
|
||||
filterChevron.style.transform = 'rotate(180deg)';
|
||||
}
|
||||
|
||||
if(filterBtn && filterContent) {
|
||||
filterBtn.addEventListener('click', () => {
|
||||
isFilterOpen = !isFilterOpen;
|
||||
if(isFilterOpen) {
|
||||
filterContent.style.display = 'block';
|
||||
filterChevron.style.transform = 'rotate(180deg)';
|
||||
} else {
|
||||
filterContent.style.display = 'none';
|
||||
filterChevron.style.transform = 'rotate(0deg)';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- Lógica do Modal (Preview) ---
|
||||
const modal = document.getElementById('preview-modal');
|
||||
const iframe = document.getElementById('preview-iframe');
|
||||
const modalTitle = document.getElementById('modal-title');
|
||||
|
|
@ -293,10 +330,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
|
||||
function normalizarChaveJS(str) {
|
||||
if (!str) return "";
|
||||
return str.toString()
|
||||
.toLowerCase()
|
||||
.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
|
||||
.replace(/[^a-z0-9]/g, "");
|
||||
return str.toString().toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]/g, "");
|
||||
}
|
||||
|
||||
function openModal(url, title, btnText, btnLink) {
|
||||
|
|
@ -326,212 +360,176 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
closeButtons.forEach(el => el.addEventListener('click', closeModal));
|
||||
document.addEventListener('keydown', (e) => { if (e.key === "Escape") closeModal(); });
|
||||
|
||||
|
||||
// ===============================================
|
||||
// 2. LÓGICA DE DADOS (IA / FILTROS / ORDENAÇÃO)
|
||||
// 2. LÓGICA DE DADOS (IA / FILTROS)
|
||||
// ===============================================
|
||||
|
||||
const JSON_URL = '/mmpSearch/src_mmpSearch/saida_analises/db_final_completo.json';
|
||||
|
||||
|
||||
const cards = document.querySelectorAll('.project-card');
|
||||
const genreContainer = document.getElementById('genre-filters');
|
||||
const sortSelect = document.getElementById('sort-select');
|
||||
const starFilterSelect = document.getElementById('star-filter'); // NOVO SELETOR
|
||||
const starFilterSelect = document.getElementById('star-filter');
|
||||
const projectsContainer = document.getElementById('projects-container');
|
||||
|
||||
// Estado global dos filtros
|
||||
let currentGenre = 'all';
|
||||
let currentMinStars = 'all';
|
||||
|
||||
// Inicializa a carga de dados
|
||||
fetch(JSON_URL)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error("JSON não encontrado: " + response.statusText);
|
||||
return response.json();
|
||||
})
|
||||
.then(r => r.ok ? r.json() : Promise.reject("Erro JSON"))
|
||||
.then(data => {
|
||||
console.log("Dados IA carregados:", data.length, "músicas.");
|
||||
enrichCards(data);
|
||||
createGenreButtons(data);
|
||||
})
|
||||
.catch(err => {
|
||||
console.warn("Aviso: Dados da IA não puderam ser carregados.", err);
|
||||
const filterBox = document.querySelector('.box.has-background-white-ter');
|
||||
if(filterBox) filterBox.style.display = 'none';
|
||||
});
|
||||
.catch(console.warn);
|
||||
|
||||
// Função para desenhar as estrelas com COR FIXA (Gold)
|
||||
function gerarEstrelas(num) {
|
||||
// Garante que num seja inteiro
|
||||
const n = Math.round(num);
|
||||
let html = '<span class="has-text-warning" title="Nível de Complexidade: '+n+'/5">';
|
||||
const n = Math.round(num) || 0;
|
||||
// Cor #f1c40f é o amarelo padrão de estrelas
|
||||
let html = `<div style="color: #f1c40f; letter-spacing: 2px;" title="Complexidade: ${n}/5">`;
|
||||
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
if (i <= n) {
|
||||
html += '<i class="fa-solid fa-star"></i>';
|
||||
} else {
|
||||
html += '<i class="fa-regular fa-star" style="opacity:0.4"></i>';
|
||||
// Estrela vazia em cinza claro para contraste
|
||||
html += '<i class="fa-regular fa-star" style="color: #dbdbdb;"></i>';
|
||||
}
|
||||
}
|
||||
html += '</span>';
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
function enrichCards(data) {
|
||||
const mapDados = {};
|
||||
data.forEach(item => {
|
||||
const keyArquivo = normalizarChaveJS(item.arquivo.replace('.wav','').replace('.mp3',''));
|
||||
mapDados[keyArquivo] = item;
|
||||
|
||||
if(item.dados_projeto && item.dados_projeto.titulo) {
|
||||
const keyTitulo = normalizarChaveJS(item.dados_projeto.titulo);
|
||||
mapDados[keyTitulo] = item;
|
||||
}
|
||||
const k1 = normalizarChaveJS(item.arquivo.replace('.wav','').replace('.mp3',''));
|
||||
mapDados[k1] = item;
|
||||
if(item.dados_projeto?.titulo) mapDados[normalizarChaveJS(item.dados_projeto.titulo)] = item;
|
||||
});
|
||||
|
||||
cards.forEach(card => {
|
||||
const cardKey = normalizarChaveJS(card.dataset.title);
|
||||
const info = mapDados[cardKey];
|
||||
const info = mapDados[normalizarChaveJS(card.dataset.title)];
|
||||
|
||||
// Defaults
|
||||
let genero = "Unknown";
|
||||
let estrelas = 0;
|
||||
let intensidade = -100;
|
||||
let bpm = 0;
|
||||
let tomHtml = "";
|
||||
|
||||
if (info) {
|
||||
// --- 1. Extração de Dados ---
|
||||
const genero = (info.analise_ia?.genero_macro) || "Unknown";
|
||||
const intensidade = (info.analise_tecnica?.intensidade_db) ? parseFloat(info.analise_tecnica.intensidade_db) : -100;
|
||||
const bpm = (info.analise_tecnica?.bpm) ? parseFloat(info.analise_tecnica.bpm) : 0;
|
||||
|
||||
// Extração segura das estrelas
|
||||
let estrelas = 0;
|
||||
if (info.analise_tecnica?.complexidade?.estrelas) {
|
||||
estrelas = parseInt(info.analise_tecnica.complexidade.estrelas);
|
||||
}
|
||||
genero = info.analise_ia?.genero_macro || "Unknown";
|
||||
intensidade = parseFloat(info.analise_tecnica?.intensidade_db || -100);
|
||||
bpm = parseFloat(info.analise_tecnica?.bpm || 0);
|
||||
estrelas = parseInt(info.analise_tecnica?.complexidade?.estrelas || 0);
|
||||
|
||||
// --- 2. Atualiza Dataset do HTML (Para filtros funcionarem) ---
|
||||
card.dataset.genre = genero;
|
||||
card.dataset.intensity = intensidade;
|
||||
card.dataset.bpm_real = bpm;
|
||||
card.dataset.stars = estrelas; // Importante para ordenação
|
||||
// HTML do Tom
|
||||
if (info.analise_tecnica?.tom) {
|
||||
const t = info.analise_tecnica.tom;
|
||||
const e = info.analise_tecnica.escala === 'minor' ? 'm' : '';
|
||||
tomHtml = `<span class="tag is-white is-rounded border-tag">🎹 ${t}${e}</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
// --- 3. Renderização Visual ---
|
||||
const bpmContainer = card.querySelector('.bpm-container');
|
||||
|
||||
if (bpmContainer) {
|
||||
const iaTagsDiv = document.createElement('div');
|
||||
iaTagsDiv.className = "tags is-centered mt-1 mb-3";
|
||||
iaTagsDiv.style.flexDirection = "column";
|
||||
// Salva no dataset para filtros
|
||||
card.dataset.genre = genero;
|
||||
card.dataset.stars = estrelas;
|
||||
card.dataset.intensity = intensidade;
|
||||
card.dataset.bpm_real = bpm;
|
||||
|
||||
let htmlFinal = '';
|
||||
// Injeta HTML
|
||||
const bpmContainer = card.querySelector('.bpm-container');
|
||||
if (bpmContainer) {
|
||||
// Limpa conteúdo anterior injetado via JS se houver (opcional)
|
||||
// bpmContainer.innerHTML = ''; // Cuidado se tiver o BPM original do Liquid aqui.
|
||||
|
||||
const iaDiv = document.createElement('div');
|
||||
iaDiv.style.display = "flex";
|
||||
iaDiv.style.flexDirection = "column";
|
||||
iaDiv.style.alignItems = "center";
|
||||
iaDiv.style.gap = "4px";
|
||||
iaDiv.className = "mt-2";
|
||||
|
||||
// Renderiza Estrelas (mesmo que seja 0, mostra estrelas vazias)
|
||||
htmlFinal += `<div class="mb-1" style="font-size: 0.8rem;">${gerarEstrelas(estrelas)}</div>`;
|
||||
|
||||
htmlFinal += '<div class="tags is-centered">';
|
||||
// 1. Estrelas
|
||||
const starsDiv = document.createElement('div');
|
||||
starsDiv.innerHTML = gerarEstrelas(estrelas);
|
||||
iaDiv.appendChild(starsDiv);
|
||||
|
||||
// Tag Gênero
|
||||
if (genero !== "Unknown") {
|
||||
let colorClass = 'is-primary';
|
||||
if(genero === 'Electronic') colorClass = 'is-info';
|
||||
if(genero === 'Hip Hop') colorClass = 'is-warning';
|
||||
if(genero === 'Rock') colorClass = 'is-danger';
|
||||
// 2. Tags (Gênero + Tom)
|
||||
const tagsRow = document.createElement('div');
|
||||
tagsRow.className = "tags is-centered mb-0";
|
||||
|
||||
if(genero !== "Unknown") {
|
||||
let color = 'is-primary';
|
||||
if(genero === 'Electronic') color = 'is-info';
|
||||
if(genero === 'Hip Hop') color = 'is-warning';
|
||||
if(genero === 'Rock') color = 'is-danger';
|
||||
|
||||
tagsRow.innerHTML += `<span class="tag ${color} is-light is-rounded mr-1">🤖 ${genero}</span>`;
|
||||
}
|
||||
|
||||
tagsRow.innerHTML += tomHtml;
|
||||
iaDiv.appendChild(tagsRow);
|
||||
|
||||
htmlFinal += `<span class="tag ${colorClass} is-light is-rounded mr-1" style="font-size: 0.65rem;">
|
||||
🤖 ${genero}
|
||||
</span>`;
|
||||
}
|
||||
|
||||
// Tag Tom
|
||||
if (info.analise_tecnica?.tom) {
|
||||
const nota = info.analise_tecnica.tom;
|
||||
const escala = info.analise_tecnica.escala === 'minor' ? 'm' : '';
|
||||
htmlFinal += `<span class="tag is-white is-rounded" style="font-size: 0.65rem; border: 1px solid #ddd; color: #555;">
|
||||
🎹 ${nota}${escala}
|
||||
</span>`;
|
||||
}
|
||||
|
||||
htmlFinal += '</div>';
|
||||
iaTagsDiv.innerHTML = htmlFinal;
|
||||
bpmContainer.appendChild(iaTagsDiv);
|
||||
}
|
||||
} else {
|
||||
// Valores padrão se não achar dados
|
||||
card.dataset.genre = "Outros";
|
||||
card.dataset.intensity = -100;
|
||||
card.dataset.stars = 0;
|
||||
bpmContainer.appendChild(iaDiv);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createGenreButtons(data) {
|
||||
const genres = new Set();
|
||||
data.forEach(item => {
|
||||
if(item.analise_ia?.genero_macro && item.analise_ia.genero_macro !== "Unknown") {
|
||||
genres.add(item.analise_ia.genero_macro);
|
||||
}
|
||||
data.forEach(i => {
|
||||
if(i.analise_ia?.genero_macro && i.analise_ia.genero_macro !== "Unknown")
|
||||
genres.add(i.analise_ia.genero_macro);
|
||||
});
|
||||
|
||||
const genresArray = Array.from(genres).sort();
|
||||
genresArray.forEach(genre => {
|
||||
Array.from(genres).sort().forEach(g => {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'button is-small is-rounded is-white'; // Começa branco
|
||||
btn.textContent = genre;
|
||||
btn.dataset.genre = genre;
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
// UI Update
|
||||
const allBtns = genreContainer.querySelectorAll('.button');
|
||||
allBtns.forEach(b => {
|
||||
btn.className = 'button is-small is-rounded is-white';
|
||||
btn.textContent = g;
|
||||
btn.onclick = () => {
|
||||
document.querySelectorAll('#genre-filters .button').forEach(b => {
|
||||
b.classList.remove('is-active', 'is-info');
|
||||
b.classList.add('is-white');
|
||||
});
|
||||
btn.classList.remove('is-white');
|
||||
btn.classList.add('is-active', 'is-info');
|
||||
|
||||
// Logic Update
|
||||
currentGenre = genre;
|
||||
currentGenre = g;
|
||||
applyFilters();
|
||||
});
|
||||
|
||||
};
|
||||
genreContainer.appendChild(btn);
|
||||
});
|
||||
|
||||
// Botão Todos
|
||||
// Reset All Button
|
||||
const allBtn = genreContainer.querySelector('[data-genre="all"]');
|
||||
allBtn.addEventListener('click', () => {
|
||||
genreContainer.querySelectorAll('.button').forEach(b => {
|
||||
b.classList.remove('is-active', 'is-info');
|
||||
b.classList.add('is-white');
|
||||
allBtn.onclick = () => {
|
||||
document.querySelectorAll('#genre-filters .button').forEach(b => {
|
||||
b.classList.remove('is-active', 'is-info');
|
||||
b.classList.add('is-white');
|
||||
});
|
||||
allBtn.classList.remove('is-white');
|
||||
allBtn.classList.add('is-active', 'is-info');
|
||||
|
||||
currentGenre = 'all';
|
||||
applyFilters();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Lógica Unificada de Filtros
|
||||
function applyFilters() {
|
||||
cards.forEach(card => {
|
||||
const column = card.closest('.project-column');
|
||||
cards.forEach(c => {
|
||||
const col = c.closest('.project-column');
|
||||
const g = c.dataset.genre;
|
||||
const s = parseInt(c.dataset.stars || 0);
|
||||
|
||||
// 1. Checa Gênero
|
||||
const cardGenre = card.dataset.genre;
|
||||
const matchGenre = (currentGenre === 'all' || cardGenre === currentGenre);
|
||||
const matchG = (currentGenre === 'all' || g === currentGenre);
|
||||
const matchS = (currentMinStars === 'all' || s >= parseInt(currentMinStars));
|
||||
|
||||
// 2. Checa Estrelas (Mínimo)
|
||||
const cardStars = parseInt(card.dataset.stars) || 0;
|
||||
let matchStars = true;
|
||||
if (currentMinStars !== 'all') {
|
||||
const min = parseInt(currentMinStars);
|
||||
matchStars = (cardStars >= min);
|
||||
}
|
||||
|
||||
// Exibe apenas se passar nos DOIS filtros
|
||||
if (matchGenre && matchStars) {
|
||||
column.style.display = 'block';
|
||||
} else {
|
||||
column.style.display = 'none';
|
||||
}
|
||||
});
|
||||
col.style.display = (matchG && matchS) ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
// Evento do Dropdown de Estrelas
|
||||
if(starFilterSelect) {
|
||||
starFilterSelect.addEventListener('change', (e) => {
|
||||
currentMinStars = e.target.value;
|
||||
|
|
@ -539,37 +537,37 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
});
|
||||
}
|
||||
|
||||
// Ordenação
|
||||
sortSelect.addEventListener('change', (e) => {
|
||||
const criteria = e.target.value;
|
||||
const columns = Array.from(projectsContainer.children);
|
||||
|
||||
columns.sort((colA, colB) => {
|
||||
const cardA = colA.querySelector('.project-card');
|
||||
const cardB = colB.querySelector('.project-card');
|
||||
|
||||
const intensityA = parseFloat(cardA.dataset.intensity) || -100;
|
||||
const intensityB = parseFloat(cardB.dataset.intensity) || -100;
|
||||
const bpmA = parseFloat(cardA.dataset.bpm_real) || 0;
|
||||
const bpmB = parseFloat(cardB.dataset.bpm_real) || 0;
|
||||
const starsA = parseInt(cardA.dataset.stars) || 0;
|
||||
const starsB = parseInt(cardB.dataset.stars) || 0;
|
||||
|
||||
if (criteria === 'intensity_desc') return intensityB - intensityA;
|
||||
if (criteria === 'intensity_asc') return intensityA - intensityB;
|
||||
if (criteria === 'bpm_desc') return bpmB - bpmA;
|
||||
if (criteria === 'bpm_asc') return bpmA - bpmB;
|
||||
// Novas ordenações
|
||||
if (criteria === 'stars_desc') return starsB - starsA;
|
||||
if (criteria === 'stars_asc') return starsA - starsB;
|
||||
|
||||
const titleA = cardA.dataset.title.toLowerCase();
|
||||
const titleB = cardB.dataset.title.toLowerCase();
|
||||
return titleA.localeCompare(titleB);
|
||||
const crit = e.target.value;
|
||||
const cols = Array.from(projectsContainer.children);
|
||||
|
||||
cols.sort((a, b) => {
|
||||
const cA = a.querySelector('.project-card');
|
||||
const cB = b.querySelector('.project-card');
|
||||
|
||||
const getVal = (el, k) => parseFloat(el.dataset[k] || 0);
|
||||
|
||||
if(crit === 'stars_desc') return getVal(cB, 'stars') - getVal(cA, 'stars');
|
||||
if(crit === 'stars_asc') return getVal(cA, 'stars') - getVal(cB, 'stars');
|
||||
if(crit === 'bpm_desc') return getVal(cB, 'bpm_real') - getVal(cA, 'bpm_real');
|
||||
if(crit === 'intensity_desc') return getVal(cB, 'intensity') - getVal(cA, 'intensity');
|
||||
if(crit === 'intensity_asc') return getVal(cA, 'intensity') - getVal(cB, 'intensity');
|
||||
|
||||
return cA.dataset.title.localeCompare(cB.dataset.title);
|
||||
});
|
||||
|
||||
columns.forEach(col => projectsContainer.appendChild(col));
|
||||
|
||||
cols.forEach(c => projectsContainer.appendChild(c));
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.transition-icon i {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.border-tag {
|
||||
border: 1px solid #dbdbdb;
|
||||
color: #555;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue