Show subcategories in Filter - Checkbox Element

Hello everyone,

When placed on a category archive, I would like the “Filter - Checkbox” element to only display subcategories of the archive category. Does anyone know how to achieve this?

Cheers.

I think this works like this.

What I am missing is collapsible subcategories list inside a hierarchical checkbox filter element.

I set up the checkbox filter element to show my categories and subcategories. Is is a total mess, is there any workaround to make the subcategories collapsible. Just a small section of my filter, it describes the problem very well:

@timmse Can you recommend anything to make the filter usable for the customers? Thank you!

Hi @Andris ,

It’s not available in Bricks query filters currently.
Someone sent us via email something similar to this

We already recorded it into our improvement task.

Hello @itchycode

I implemented a JS fix for this problem as a temporary solution. I hope you can upgrade Bricks to work more seamlessly with WooCommerce :slight_smile:

My fix is this with an extra Code element – needs some improvement but not bad. (Or course it would be far better if the DOM elements would be initially in a hierarchy and there would be no need for JS trickery, but for now this works.)

JS:

(function () {
  const INIT_ATTR = 'data-tree-init';
  const openStates = new Set(); // stored by value attribute

  function saveState() {
    const list = document.querySelector('.brxe-filter-checkbox');
    if (!list) return;
    openStates.clear();
    list.querySelectorAll('li.is-open').forEach(li => {
      const input = li.querySelector('input[type="checkbox"]');
      if (input) openStates.add(input.value);
    });
  }

  function restoreState() {
    const list = document.querySelector('.brxe-filter-checkbox');
    if (!list) return;
    list.querySelectorAll('li').forEach(li => {
      const input = li.querySelector('input[type="checkbox"]');
      if (input && openStates.has(input.value)) {
        li.classList.add('is-open');
      }
    });
  }

  function init() {
    const list = document.querySelector('.brxe-filter-checkbox');
    if (!list) return;

    const items = [...list.querySelectorAll(`li:not([${INIT_ATTR}])`)];
    if (items.length === 0) return;

    for (let i = items.length - 1; i >= 0; i--) {
      const li = items[i];
      const depth = parseInt(li.dataset.depth);

      const children = [];
      let next = li.nextElementSibling;
      while (next && parseInt(next.dataset.depth) > depth) {
        if (parseInt(next.dataset.depth) === depth + 1) children.push(next);
        next = next.nextElementSibling;
      }

      li.setAttribute(INIT_ATTR, '1');

      if (children.length === 0) continue;

      const wrapper = document.createElement('div');
      wrapper.className = 'children-wrapper';
      const ul = document.createElement('ul');
      wrapper.appendChild(ul);

      next = li.nextElementSibling;
      while (next && parseInt(next.dataset.depth) > depth) {
        const toMove = next;
        next = next.nextElementSibling;
        ul.appendChild(toMove);
      }

      li.appendChild(wrapper);

      const icon = document.createElement('span');
      icon.className = 'toggle-icon';
      icon.setAttribute('aria-hidden', 'true');
      icon.innerHTML = `<!-- @license lucide-static v0.546.0 - ISC -->
<svg
  class="lucide lucide-chevron-down"
  xmlns="http://www.w3.org/2000/svg"
  width="24"
  height="24"
  viewBox="0 0 24 24"
  fill="none"
  stroke="currentColor"
  stroke-width="2"
  stroke-linecap="round"
  stroke-linejoin="round"
>
  <path d="m6 9 6 6 6-6" />
</svg>`;
      li.querySelector('label').appendChild(icon);

      li.querySelector('label').addEventListener('click', (e) => {
        if (e.target.tagName === 'INPUT') return;
        e.preventDefault();
        li.classList.toggle('is-open');
        // Azonnal mentjük a kattintás után
        saveState();
      });
    }

    // Basic state: if there is only one top level element and there is no saved state
    const topLevel = list.querySelectorAll(':scope > li[data-depth="0"]');
    if (topLevel.length === 1 && openStates.size === 0) {
      topLevel[0].classList.add('is-open');
      saveState();
    }

    // Restore saved state
    restoreState();
  }

  init();

  const observer = new MutationObserver((mutations) => {
    const relevant = mutations.some(m =>
      [...m.addedNodes].some(n => n.nodeType === 1 && (
        n.matches?.('li') || n.querySelector?.('li')
      ))
    );
    if (relevant) init();
  });

  const target = document.querySelector('.brxe-filter-checkbox') ?? document.body;
  observer.observe(target, { childList: true, subtree: true });

})();

CSS:

/* Wrapper minden li köré kell egy animálható konténer */
.brxe-filter-checkbox {
  gap: 8px;
	display: flex;
	flex-direction: column;
}
.brxe-filter-checkbox li .children-wrapper {
  display: grid;
  grid-template-rows: 0fr;
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  overflow: hidden;
}

.brxe-filter-checkbox li.is-open > .children-wrapper {
  grid-template-rows: 1fr;
  margin-top: 8px;
}

.brxe-filter-checkbox li .children-wrapper > ul {
  min-height: 0;
  list-style: none;
  padding: 0;
  gap: 8px;
	display: flex;
	flex-direction: column;
}
.brx-option-disabled {
  opacity: 1;
	pointer-events: auto;
}
.brx-option-text {
  text-wrap: nowrap
}
.toggle-icon {
  display: inline-flex;
  align-items: center;
  margin-left: auto;
  vertical-align: middle;
}

.toggle-icon svg {
  transition: all 0.3s ease;
  transform: rotate(0deg);
  width: 18px;
  height: 18px;
  stroke: var(--gray-300);
}
.toggle-icon:hover svg {
  stroke: var(--gray-500);
}

.is-open > label .toggle-icon svg {
  transform: rotateX(180deg);
  stroke: var(--gray-500);
}