Skip to main content

Multiple choices filters

Demo

Colors
Types
Radio Button Field

Ananas

Brun
Jaune
Fruits

Avocat

Vert
Fruits

Banane

Jaune
Fruits

Brocoli

Vert
Légumes

Celeri

Vert
Légumes

Citron

Jaune
Fruits

Filet mignon

Gris
Viande

Lime

Vert
Fruits

Patate

Brun
Jaune
Légumes

Pomme

Rouge
Fruits

Salade

Vert
Légumes

Steak

Rouge
Viande

Installation

Importez la fonction dans votre projet

import { MultipleFilters } from "@overdog/fn"

Instance et initialisation

new MultipleFilters("#filter-wrapper-multiple", {
   divIds: ["listing"],
   clearAllButtonId: "clear-all"
}).init()

Twig template Code

{# --------------------- SET RELATEDTO ARRAY #}

{% set relatedTo = [] %}

{# --------------------- CATEGORY GROUP 1 - COLORS #}

{% set colorsUrlQueryName = 'colors'|t %} {# variable also used in the form name #}
{% set colorsUrlQueryValue = craft.app.request.getQueryParam(colorsUrlQueryName)|split(',')|filter %}
{% if colorsUrlQueryValue %}
  {% set relatedTo = relatedTo|merge([{
    targetElement: craft.categories({ group: 'groupFoodColors', slug: colorsUrlQueryValue }).ids()
  }]) %}
{% endif %}

{# --------------------- CATEGORY GROUP 2 - TYPES #}

{% set typesUrlQueryName = 'types'|t %} {# variable also used in the form name #}
{% set typesUrlQueryValue = craft.app.request.getQueryParam(typesUrlQueryName)|split(',')|filter %}
{% if typesUrlQueryValue %}
  {% set relatedTo = relatedTo|merge([{
    targetElement: craft.categories({ group: 'groupFoodTypes', slug: typesUrlQueryValue }).ids()
  }]) %}
{% endif %}

{# --------------------- RADIO BUTTON FIELD 3 - AVAILABILITY
   no need to split param - radio button always give one value at the time (same thing with a select input)
#}
{% set availabilityUrlQueryName = 'availability'|t %} {# variable also used in the form name #}
{% set availabilityUrlQueryValue = craft.app.request.getQueryParam(availabilityUrlQueryName) %}

{# --------------------- SEARCH INPUT 4 #}

{% set searchUrlQueryName = 'keyword'|t %}
{% set searchUrlQueryValue = craft.app.request.getQueryParam(searchUrlQueryName) %}


{# --------------------- MERGE THE CATEGORIES FOR THE MAIN QUERY RELATEDTO #}

{% if relatedTo|length > 1 %}
  {% set relatedTo = ['and']|merge(relatedTo) %}
{% endif %}

{# --------------------- MAIN QUERY #}

{% set foodQuery = craft.entries()
  .section('structureFood')
  .orderBy('title')
  .relatedTo(relatedTo ? relatedTo : null)
  .search(searchUrlQueryValue)
  .with(['structureFoodColors', 'structureFoodTypes'])
  .structureFoodAvailability(availabilityUrlQueryValue ?? null)
  .all()
%}

{# --------------------- FETCH CATEGORIES GROUP AND RADIO BUTTON FIELD TO POPULATE FILTERS #}

{# categories group - example #}
{% set colorsCatGroup = craft.categories().group('groupFoodColors').select(['title', 'slug']).asArray().all() %}
{% set typesCatGroup = craft.categories().group('groupFoodTypes').select(['title', 'slug']).asArray().all() %}
{# radio field - example #}
{% set radioField = craft.app.fields.getFieldByHandle('structureFoodAvailability') %}

{# --------------------- FILTER LAYOUT #}

{# ID is used by Javascript - a wrapper around your form is necessary > see docs #}
<div id="filter-wrapper-multiple" class="border border-gray-300 p-8 grid grid-cols-1 md:grid-cols-3 gap-half mb-half">
  {# COLORS FORM -> checkbox - category #}
  {% if colorsCatGroup|length %}
    <form data-fn-filter-group="{{ colorsUrlQueryName }}">
      <fieldset>
        <legend class="font-bold mb-quarter">{{ 'Colors'|t }}</legend>
        {% for cat in colorsCatGroup %}
            <div class="flex items-center relative py-1">
               <input 
                  class="inset-0 opacity-0 absolute -z-1 peer" 
                  id="{{ cat.slug }}" 
                  type="checkbox" 
                  name="{{ cat.slug }}" 
                  value="{{ cat.slug }}" {{(cat.slug in colorsUrlQueryValue) ? 'checked' }}
               />
               <label 
                  class="cursor-pointer flex items-center w-full
                  peer-checked:before:bg-black
                  before:bg-[#c4cad2] before:cursor-pointer before:inline-block before:h-4 before:w-4 before:mr-4 before:shrink-0" 
                  for="{{ cat.slug }}"
               >
                  {{ cat.title }}
               </label>
            </div>
        {% endfor %}
      </fieldset>
    </form>
  {% endif %}

  {# TYPES FORM -> checkbox - category #}
  {% if typesCatGroup|length %}
    <form data-fn-filter-group="{{ typesUrlQueryName }}">
      <fieldset>
        <legend class="font-bold mb-quarter">{{ 'Types'|t }}</legend>
        {% for cat in typesCatGroup %}
            <div class="flex items-center relative py-1">
                  <input 
                     class="inset-0 opacity-0 absolute -z-1 peer" 
                     id="{{ cat.slug }}" 
                     type="checkbox" 
                     name="{{ cat.slug }}" 
                     value="{{ cat.slug }}" {{(cat.slug in typesUrlQueryValue) ? 'checked' }}
                  />
                  <label
                     class="cursor-pointer flex items-center w-full
                     peer-checked:before:bg-black
                     before:bg-[#c4cad2] before:cursor-pointer before:inline-block before:h-4 before:w-4 before:mr-4 before:shrink-0" 
                     for="{{ cat.slug }}"
                  >
                  {{ cat.title }}
                  </label>
            </div>
        {% endfor %}
      </fieldset>
    </form>
  {% endif %}

  {# AVAILABILITY FORM -> radio button - field #}
  <form data-fn-filter-group="{{ availabilityUrlQueryName }}">
    <fieldset>
      <legend class="font-bold mb-quarter">Radio Button Field</legend>
      <div class="flex items-center relative py-1">
         {# HINT : name on all input must be the SAME for radio button #}
         <input 
            id="all"
            class="inset-0 opacity-0 absolute -z-1 peer" 
            type="radio" 
            name="availability" 
            value=""
         />
         <label
            class="cursor-pointer flex items-center w-full
            peer-checked:before:bg-black
            before:bg-[#c4cad2] before:rounded-full before:cursor-pointer before:inline-block before:h-4 before:w-4 before:mr-4 before:shrink-0" 
            for="all"
         >
            {{ 'All availabilities'|t }}
         </label>
      </div>
      {% for option in radioField.options %}
         <div class="flex items-center relative py-1">
            {# HINT : name on all input must be the SAME for radio button #}
            <input 
               id="{{ option.value }}"
               class="inset-0 opacity-0 absolute -z-1 peer" 
               type="radio" 
               name="availability" 
               value="{{ option.value }}" {{(option.value in availabilityUrlQueryValue) ? 'checked' }}
            />
            <label
               class="cursor-pointer flex items-center w-full
               peer-checked:before:bg-black
               before:bg-[#c4cad2] before:rounded-full before:cursor-pointer before:inline-block before:h-4 before:w-4 before:mr-4 before:shrink-0" 
               for="{{ option.value }}"
            >
               {{ option.label }}
            </label>
         </div>
      {% endfor %}
    </fieldset>
  </form>

  {# SEARCH FORM > The role search on the form is needed by JS #}
  <form role="search" class="flex md:col-span-2 max-w-[600px]" data-fn-filter-group="{{ searchUrlQueryName }}">
      <div class="h-[44px] relative w-full">
         <input 
            class="border-b border-gray-300 h-full px-4 w-full" 
            type="text"
            name="{{ searchUrlQueryName }}" 
            autocomplete="off" 
            placeholder="Search.."
         >
         {# searchbox reset #}
         <input class="text-[#999] cursor-pointer text-[13px] h-full absolute right-0 px-4" type="reset" value="delete">
      </div>
      <button 
         class="rounded-full shrink-0 text-0.9em h-[44px] ml-4 w-[44px] bg-slate-700 text-white" 
         type="submit" 
         value="submit"
      >
         Go
      </button>
  </form>

  <div class="filter-bar__clear-wrapper">
    <button id="clear-all" class="filter-bar__clear-button" type="reset" value="clear">clear all</button>
  </div>
</div>


{# --------------------- LAYOUT WIH LOOP FOR CARDS #}

{# ID is used by Javascript #}
<div id="listing" class="grid grid-col-1 md:grid-cols-3 gap-third">
  {% if foodQuery|length %}
      {{ include('_lib-fn/_card', { queryName: foodQuery }, ignore_missing = true )}}
  {% else %}
    <div>{{ ('There is no content matching your criteria.')|t }}</div>
  {% endif %}
</div>

Options