Upgrade interface do MMPSearch
This commit is contained in:
1 parent
6690829971
commit
b2052bfafa
12 files changed
+2974
No files matched your search
@@ -0,0 +1,574 @@
|
||||
---
|
||||
layout: default
|
||||
title: Projetos por Pattern Rítmico
|
||||
permalink: /pattern/
|
||||
---
|
||||
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<main class="main-content">
|
||||
<div class="publication">
|
||||
<div class="container">
|
||||
<br />
|
||||
|
||||
<div class="tabs is-centered is-boxed is-medium mb-6">
|
||||
{% include sidebar.html %}
|
||||
</div>
|
||||
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-8">
|
||||
<div
|
||||
class="box has-background-white-ter has-text-centered shadow-sm"
|
||||
style="border: 1px solid #cfe8fc"
|
||||
>
|
||||
<h2 class="title is-5 has-text-grey-dark mb-3">
|
||||
<span class="icon has-text-info"
|
||||
><i class="fa-solid fa-drum"></i
|
||||
></span>
|
||||
Busca por Pattern Rítmico
|
||||
</h2>
|
||||
<p class="subtitle is-7 has-text-grey mb-4">
|
||||
Desenhe um ritmo (16 steps) abaixo ou clique nos patterns dos
|
||||
instrumentos:
|
||||
</p>
|
||||
|
||||
<div
|
||||
id="pattern-search-box"
|
||||
style="
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 1rem;
|
||||
"
|
||||
>
|
||||
{% for i in (0..15) %} {% assign mod = i | modulo: 4 %} {% if i >
|
||||
0 and mod == 0 %}
|
||||
<div style="width: 8px"></div>
|
||||
{% endif %}
|
||||
<div
|
||||
class="search-step"
|
||||
data-index="{{i}}"
|
||||
title="Step {{ i | plus: 1 }}"
|
||||
style="
|
||||
width: 25px;
|
||||
height: 45px;
|
||||
background: #e8e8e8;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.1s;
|
||||
"
|
||||
></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<button
|
||||
id="clearFilterButton"
|
||||
class="button is-small is-danger is-light is-rounded"
|
||||
>
|
||||
<span class="icon"><i class="fa-solid fa-trash"></i></span>
|
||||
<span>Limpar Desenho</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="project-list" class="columns is-multiline">
|
||||
{% for projeto in site.data.all %} {% assign project_patterns_flat = ""
|
||||
| split: "," %} {% for track in projeto.tracks %} {% if
|
||||
track.instruments %} {% for instrument in track.instruments %} {% if
|
||||
instrument.patterns %} {% for pattern in instrument.patterns %} {%
|
||||
assign pattern_steps = pattern.steps %} {% if pattern_steps and
|
||||
pattern_steps.size > 0 %} {% assign total_steps = pattern_steps.size %}
|
||||
{% assign chunk_size = 4 %} {% assign num_chunks = total_steps |
|
||||
divided_by: chunk_size %} {% assign remainder = total_steps | modulo:
|
||||
chunk_size %} {% if remainder > 0 %}{% assign num_chunks = num_chunks |
|
||||
plus: 1 %}{% endif %} {% for i in (0..num_chunks) %} {% assign
|
||||
start_index = i | times: chunk_size %} {% assign current_chunk_array =
|
||||
pattern_steps | slice: start_index, chunk_size %} {% if
|
||||
current_chunk_array.size > 0 %} {% assign chunk_string = "" %} {% for
|
||||
step in current_chunk_array %} {% if step == true or step == 'true' or
|
||||
step == 1 %}{% assign chunk_string = chunk_string | append: '1' %}{%
|
||||
else %}{% assign chunk_string = chunk_string | append: '0' %}{% endif %}
|
||||
{% endfor %} {% unless project_patterns_flat contains chunk_string %} {%
|
||||
assign project_patterns_flat = project_patterns_flat | push:
|
||||
chunk_string %} {% endunless %} {% endif %} {% endfor %} {% endif %} {%
|
||||
endfor %} {% endif %} {% endfor %} {% endif %} {% endfor %} {% if
|
||||
project_patterns_flat.size > 0 %} {% assign project_patterns_string =
|
||||
project_patterns_flat | join: ',' %}
|
||||
|
||||
<div
|
||||
class="column is-12-mobile is-6-tablet is-4-desktop is-3-widescreen project-item"
|
||||
data-patterns="{{ project_patterns_string }}"
|
||||
>
|
||||
<div
|
||||
class="card project-card"
|
||||
style="
|
||||
height: 100%;
|
||||
background-color: #f0f8ff;
|
||||
border: 1px solid #cfe8fc;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
"
|
||||
>
|
||||
{% assign file_url = projeto.file | downcase | replace: ' ', '-' |
|
||||
replace: 'ç', 'c' | replace: 'ã', 'a' | replace: 'á', 'a' | replace:
|
||||
'â', 'a' | replace: 'é', 'e' | replace: 'ê', 'e' | replace: 'í', 'i'
|
||||
| replace: 'ó', 'o' | replace: 'ô', 'o' | replace: 'õ', 'o' |
|
||||
replace: 'ú', 'u' %} {% assign page_url = '../mmp_pages/' | append:
|
||||
file_url | append: '.html' %}
|
||||
|
||||
<a
|
||||
href="{{ page_url }}"
|
||||
style="text-decoration: none; display: block"
|
||||
>
|
||||
<div class="card-content has-text-centered p-4 pb-0">
|
||||
<div
|
||||
style="
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 auto 0.5rem auto;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
|
||||
"
|
||||
>
|
||||
<span class="icon" style="color: #3273dc"
|
||||
><i class="fa-solid fa-music"></i
|
||||
></span>
|
||||
</div>
|
||||
<p
|
||||
class="title is-6 mb-3"
|
||||
style="color: #205081; word-break: break-word"
|
||||
>
|
||||
{{ projeto.file }}
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="card-content px-4 pb-4 pt-2" style="flex: 1">
|
||||
<div
|
||||
class="patterns-visualizer p-3"
|
||||
style="
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
border: 1px dashed #cfe8fc;
|
||||
text-align: left;
|
||||
"
|
||||
>
|
||||
<p
|
||||
class="is-size-7 has-text-grey mb-2 filter-status-label"
|
||||
style="font-weight: 600; text-align: center"
|
||||
>
|
||||
Patterns por Instrumento:
|
||||
</p>
|
||||
|
||||
<div style="display: flex; flex-direction: column; gap: 8px">
|
||||
{% for track in projeto.tracks %} {% if track.instruments %}
|
||||
{% for instrument in track.instruments %} {% if
|
||||
instrument.patterns %} {% assign inst_patterns = "" | split:
|
||||
"," %} {% for pattern in instrument.patterns %} {% assign
|
||||
pattern_steps = pattern.steps %} {% if pattern_steps and
|
||||
pattern_steps.size > 0 %} {% assign total_steps =
|
||||
pattern_steps.size %} {% assign chunk_size = 4 %} {% assign
|
||||
num_chunks = total_steps | divided_by: chunk_size %} {% assign
|
||||
remainder = total_steps | modulo: chunk_size %} {% if
|
||||
remainder > 0 %}{% assign num_chunks = num_chunks | plus: 1
|
||||
%}{% endif %} {% for i in (0..num_chunks) %} {% assign
|
||||
start_index = i | times: chunk_size %} {% assign
|
||||
current_chunk_array = pattern_steps | slice: start_index,
|
||||
chunk_size %} {% if current_chunk_array.size > 0 %} {% assign
|
||||
chunk_string = "" %} {% for step in current_chunk_array %} {%
|
||||
if step == true or step == 'true' or step == 1 %}{% assign
|
||||
chunk_string = chunk_string | append: '1' %}{% else %}{%
|
||||
assign chunk_string = chunk_string | append: '0' %}{% endif %}
|
||||
{% endfor %} {% if chunk_string != "0000" %} {% unless
|
||||
inst_patterns contains chunk_string %} {% assign inst_patterns
|
||||
= inst_patterns | push: chunk_string %} {% endunless %} {%
|
||||
endif %} {% endif %} {% endfor %} {% endif %} {% endfor %} {%
|
||||
if inst_patterns.size > 0 %}
|
||||
<div
|
||||
class="instrument-row"
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
padding-bottom: 4px;
|
||||
"
|
||||
>
|
||||
{% assign instrument_slug = instrument.instrument_name |
|
||||
replace: ' ', '+' %}
|
||||
<a
|
||||
href="{{ '/instruments/?instrument=' | append: instrument_slug | relative_url }}"
|
||||
class="tag is-white is-light instrument-link"
|
||||
style="
|
||||
font-size: 0.6rem;
|
||||
color: #555;
|
||||
max-width: 85px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
border: 1px solid transparent;
|
||||
"
|
||||
title="Ver projetos com {{ instrument.instrument_name }}"
|
||||
>
|
||||
{{ instrument.instrument_name }}
|
||||
</a>
|
||||
|
||||
<div style="display: flex; gap: 3px">
|
||||
{% for p_str in inst_patterns %}
|
||||
<div
|
||||
class="pattern-mini-grid js-pattern-trigger"
|
||||
data-pattern-val="{{ p_str }}"
|
||||
title="Filtrar por: {{ p_str }}"
|
||||
>
|
||||
{% assign bits = p_str | split: '' %} {% for bit in bits
|
||||
%}
|
||||
<div
|
||||
style="width: 4px; height: 8px; background-color: {% if bit == '1' %}#4caf50{% else %}#eee{% endif %};"
|
||||
></div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %} {% endif %} {% endfor %} {% endif %} {% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer
|
||||
class="card-footer"
|
||||
style="
|
||||
border-top: 1px solid #cfe8fc;
|
||||
background-color: #fff;
|
||||
border-radius: 0 0 12px 12px;
|
||||
overflow: hidden;
|
||||
"
|
||||
>
|
||||
<a
|
||||
href="#"
|
||||
class="card-footer-item js-open-modal"
|
||||
data-target-url="{{ page_url }}"
|
||||
data-modal-title="Detalhes: {{ projeto.file }}"
|
||||
data-full-btn-text="Ir para Página"
|
||||
data-full-btn-link="{{ page_url }}"
|
||||
style="
|
||||
color: #5b7da3;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
border-right: 1px solid #eee;
|
||||
"
|
||||
>Ver</a
|
||||
>
|
||||
{% assign creation_url = '/mmpSearch/creation.html?project=' |
|
||||
append: projeto.file %} {% assign embed_url = creation_url |
|
||||
append: '&embed=true' %}
|
||||
<a
|
||||
href="#"
|
||||
class="card-footer-item js-open-modal"
|
||||
data-target-url="{{ embed_url }}"
|
||||
data-modal-title="Editor: {{ projeto.file }}"
|
||||
data-full-btn-text="Abrir Editor"
|
||||
data-full-btn-link="{{ creation_url }}"
|
||||
style="color: #3273dc; font-size: 0.8rem; font-weight: 600"
|
||||
>Editar</a
|
||||
>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %} {% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.search-step.is-active {
|
||||
background-color: #4caf50 !important;
|
||||
border-color: #388e3c !important;
|
||||
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.pattern-mini-grid {
|
||||
display: flex;
|
||||
gap: 1px;
|
||||
padding: 2px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.pattern-mini-grid:hover {
|
||||
transform: scale(1.2);
|
||||
border-color: #3273dc;
|
||||
z-index: 10;
|
||||
}
|
||||
.pattern-mini-grid.match-highlight {
|
||||
border-color: #4caf50 !important;
|
||||
background-color: #e8f5e9;
|
||||
box-shadow: 0 0 0 1px #4caf50;
|
||||
}
|
||||
.instrument-link:hover {
|
||||
background-color: #eef6fc !important;
|
||||
color: #3273dc !important;
|
||||
border-color: #3273dc !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="preview-modal" class="modal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card" style="width: 90%; max-width: 800px">
|
||||
<header
|
||||
class="modal-card-head"
|
||||
style="background-color: #f0f8ff; border-bottom: 1px solid #cfe8fc"
|
||||
>
|
||||
<p
|
||||
class="modal-card-title"
|
||||
id="modal-title"
|
||||
style="color: #205081; font-weight: bold"
|
||||
>
|
||||
Preview
|
||||
</p>
|
||||
<button class="delete" aria-label="close"></button>
|
||||
</header>
|
||||
<section
|
||||
class="modal-card-body p-0"
|
||||
style="height: 500px; background-color: #fff"
|
||||
>
|
||||
<iframe
|
||||
id="preview-iframe"
|
||||
src=""
|
||||
style="width: 100%; height: 100%; border: none"
|
||||
></iframe>
|
||||
</section>
|
||||
<footer
|
||||
class="modal-card-foot"
|
||||
style="
|
||||
justify-content: flex-end;
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #cfe8fc;
|
||||
"
|
||||
>
|
||||
<button class="button" id="close-modal-btn">Fechar</button>
|
||||
<a href="#" id="full-edit-btn" target="_blank" class="button is-info"
|
||||
>Abrir</a
|
||||
>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const projects = document.querySelectorAll(".project-item");
|
||||
const clearFilterButton = document.querySelector("#clearFilterButton");
|
||||
const searchSteps = document.querySelectorAll(".search-step");
|
||||
let activeChunks = [];
|
||||
|
||||
function getChunksFromSearchBox() {
|
||||
let fullPattern = "";
|
||||
searchSteps.forEach(
|
||||
(step) =>
|
||||
(fullPattern += step.classList.contains("is-active") ? "1" : "0")
|
||||
);
|
||||
return fullPattern.match(/.{1,4}/g) || [];
|
||||
}
|
||||
|
||||
function setSearchBoxFromChunks(chunks) {
|
||||
let fullPattern = (chunks || []).join("").padEnd(16, "0");
|
||||
const bits = fullPattern.split("");
|
||||
searchSteps.forEach((step, index) => {
|
||||
if (bits[index] === "1") step.classList.add("is-active");
|
||||
else step.classList.remove("is-active");
|
||||
});
|
||||
}
|
||||
|
||||
// --- FUNÇÃO CRUCIAL: FILTRA PROJETOS E RESUME O CONTEÚDO INTERNO ---
|
||||
function filterByPattern(chunks) {
|
||||
const effectiveChunks = chunks.filter((c) => c !== "0000");
|
||||
|
||||
// RESET GLOBAL (Se não há filtro)
|
||||
if (effectiveChunks.length === 0) {
|
||||
projects.forEach((project) => {
|
||||
project.style.display = "block";
|
||||
// Mostra tudo dentro do card
|
||||
project
|
||||
.querySelectorAll(".instrument-row")
|
||||
.forEach((row) => (row.style.display = "flex"));
|
||||
project.querySelectorAll(".pattern-mini-grid").forEach((grid) => {
|
||||
grid.style.display = "flex";
|
||||
grid.classList.remove("match-highlight");
|
||||
});
|
||||
// Reseta label
|
||||
const label = project.querySelector(".filter-status-label");
|
||||
if (label) label.textContent = "Patterns por Instrumento:";
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// COM FILTRO ATIVO
|
||||
projects.forEach((project) => {
|
||||
const projectPatternsStr = project.getAttribute("data-patterns");
|
||||
const projectPatterns = projectPatternsStr.split(",");
|
||||
|
||||
// 1. O projeto tem TODOS os patterns buscados?
|
||||
const isMatch = effectiveChunks.every((chunk) =>
|
||||
projectPatterns.includes(chunk)
|
||||
);
|
||||
|
||||
if (!isMatch) {
|
||||
project.style.display = "none";
|
||||
} else {
|
||||
project.style.display = "block";
|
||||
|
||||
// 2. Filtragem Interna: Mostrar apenas o que bateu
|
||||
const rows = project.querySelectorAll(".instrument-row");
|
||||
let matchCount = 0;
|
||||
|
||||
rows.forEach((row) => {
|
||||
const grids = row.querySelectorAll(".pattern-mini-grid");
|
||||
let rowHasVisible = false;
|
||||
|
||||
grids.forEach((grid) => {
|
||||
const val = grid.dataset.patternVal;
|
||||
// Se o pattern faz parte da busca, mostra. Se não, esconde.
|
||||
if (effectiveChunks.includes(val)) {
|
||||
grid.style.display = "flex";
|
||||
grid.classList.add("match-highlight");
|
||||
rowHasVisible = true;
|
||||
} else {
|
||||
grid.style.display = "none";
|
||||
grid.classList.remove("match-highlight");
|
||||
}
|
||||
});
|
||||
|
||||
// Se a linha do instrumento não tem nenhum pattern visível, esconde a linha toda
|
||||
row.style.display = rowHasVisible ? "flex" : "none";
|
||||
if (rowHasVisible) matchCount++;
|
||||
});
|
||||
|
||||
// Atualiza label para dar feedback
|
||||
const label = project.querySelector(".filter-status-label");
|
||||
if (label) label.textContent = "Patterns encontrados:";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateUrl(chunks) {
|
||||
const newUrl = new URL(window.location.href);
|
||||
const effectiveChunks = chunks.filter((c) => c !== "0000");
|
||||
if (effectiveChunks.length > 0)
|
||||
newUrl.searchParams.set("p", effectiveChunks.join(","));
|
||||
else newUrl.searchParams.delete("p");
|
||||
window.history.replaceState({}, "", newUrl);
|
||||
}
|
||||
|
||||
function runFilter() {
|
||||
const chunksFromBox = getChunksFromSearchBox();
|
||||
activeChunks = chunksFromBox.filter((c) => c !== "0000");
|
||||
filterByPattern(activeChunks);
|
||||
updateUrl(activeChunks);
|
||||
}
|
||||
|
||||
// --- TRIGGERS ---
|
||||
document.querySelectorAll(".js-pattern-trigger").forEach((trigger) => {
|
||||
trigger.addEventListener("click", function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const pattern = this.dataset.patternVal;
|
||||
let currentChunks = getChunksFromSearchBox().filter(
|
||||
(c) => c !== "0000"
|
||||
);
|
||||
|
||||
if (currentChunks.includes(pattern))
|
||||
currentChunks = currentChunks.filter((c) => c !== pattern);
|
||||
else {
|
||||
if (currentChunks.length < 4) currentChunks.push(pattern);
|
||||
}
|
||||
|
||||
activeChunks = currentChunks;
|
||||
setSearchBoxFromChunks(activeChunks);
|
||||
filterByPattern(activeChunks);
|
||||
updateUrl(activeChunks);
|
||||
});
|
||||
});
|
||||
|
||||
searchSteps.forEach((step) => {
|
||||
step.addEventListener("click", function () {
|
||||
step.classList.toggle("is-active");
|
||||
runFilter();
|
||||
});
|
||||
});
|
||||
|
||||
clearFilterButton.addEventListener("click", function () {
|
||||
activeChunks = [];
|
||||
setSearchBoxFromChunks([]);
|
||||
filterByPattern([]);
|
||||
updateUrl([]);
|
||||
});
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
let patternFromUrl = urlParams.get("p");
|
||||
if (patternFromUrl) {
|
||||
activeChunks = patternFromUrl.split(",");
|
||||
setSearchBoxFromChunks(activeChunks);
|
||||
filterByPattern(activeChunks);
|
||||
}
|
||||
|
||||
// Modal logic (igual)
|
||||
const modal = document.getElementById("preview-modal");
|
||||
const iframe = document.getElementById("preview-iframe");
|
||||
const modalTitle = document.getElementById("modal-title");
|
||||
const fullEditBtn = document.getElementById("full-edit-btn");
|
||||
const closeButtons = document.querySelectorAll(
|
||||
".modal-background, .modal-card-head .delete, #close-modal-btn"
|
||||
);
|
||||
|
||||
function openModal(url, title, btnText, btnLink) {
|
||||
if (!modal) return;
|
||||
modalTitle.textContent = title;
|
||||
iframe.src = url;
|
||||
fullEditBtn.textContent = btnText;
|
||||
fullEditBtn.href = btnLink;
|
||||
modal.classList.add("is-active");
|
||||
document.documentElement.classList.add("is-clipped");
|
||||
}
|
||||
function closeModal() {
|
||||
modal.classList.remove("is-active");
|
||||
document.documentElement.classList.remove("is-clipped");
|
||||
iframe.src = "";
|
||||
}
|
||||
document.querySelectorAll(".js-open-modal").forEach((btn) => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
openModal(
|
||||
btn.dataset.targetUrl,
|
||||
btn.dataset.modalTitle,
|
||||
btn.dataset.fullBtnText,
|
||||
btn.dataset.fullBtnLink
|
||||
);
|
||||
});
|
||||
});
|
||||
iframe.addEventListener("load", () => {
|
||||
try {
|
||||
const iframeDoc =
|
||||
iframe.contentDocument || iframe.contentWindow.document;
|
||||
const style = iframeDoc.createElement("style");
|
||||
style.textContent = `.tabs, .navbar, .sidebar-wrapper, .main-header { display: none !important; } .publication { padding-top: 0 !important; } body { background-color: #fff !important; }`;
|
||||
iframeDoc.head.appendChild(style);
|
||||
} catch (e) {}
|
||||
});
|
||||
closeButtons.forEach((el) => el.addEventListener("click", closeModal));
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") closeModal();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Reference in new issue
Block a user