Files
mmpSearch/pages/pattern.md
T
2025-12-02 19:54:04 -03:00

21 KiB

layout, title, permalink
layout title permalink
default Projetos por Pattern Rítmico /pattern/

  <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; height: fit-content !important; min-height: unset;"
      >
        <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>
<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>

Preview

Fechar Abrir
<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>