Demo
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
-
NameTypeDefaultDescription
-
divIdsArray - Liste de ID["listing"]Array de <div> dont le contenu sera remplacé. Idéalement, utilisez des ids.
-
fetchOnlyOnSubmitBooleanfalseMettez à true dans les options de votre instance si vous désirez utiliser un submit button au lieu de déclancher sur changement.
-
clearAllButtonIdIDnullID du bouton pour tout désélectionner
-
regexToRemoveRegex/\/p[0-9]+/Permet de supprimer un morceau de URL lorsqu'un event du filtre a lieu. Par défaut, cela va supprimer la pagination de Craft CMS par défaut (/p2).