Multiple filters with tags
This extension allows you to easily add tags for the selected options of your filters. You can choose where to place the tags. They can be placed under each filter or in the same div. The data-tags-row
attribute, which you add to each input, specifies the ID of the div where you want to display the tag.
The template for your tag is taken from a <template>
tag. This allows you to easily modify the HTML structure of your tag without touching the JavaScript function.
Demo
Installation
Import the function into the JavaScript file of your project
import { MultipleFiltersWithTags } from "@overdog/fn"
Create and initialize an instance
new MultipleFiltersWithTags("#filter-wrapper-tags", {
divIds: ["listing"]
}).init()
This element uses the same options and arguments as the Multiple choices filters.
Twig template Code
{# --------------------- SET RELATEDTO ARRAY #}
{% set relatedTo = [] %}
{# --------------------- FETCH CATEGORIES GROUP TO POPULATE FILTERS #}
{# categories group - example #}
{% set colorsCatGroup = craft.categories().group('groupFoodColors').all() %}
{% set typesCatGroup = craft.categories().group('groupFoodTypes').all() %}
{# --------------------- CATEGORY GROUP 1 - COLORS #}
{% set colorsUrlQueryName = 'colors'|t %} {# variable also used in the form name #}
{% set relatedColorsCats = [] %}
{% set colorsUrlQueryValue = craft.app.request.getQueryParam(colorsUrlQueryName)|split(',')|filter %}
{% if colorsUrlQueryValue %}
{% set relatedColorsCats = colorsCatGroup|filter(cat => cat.slug in colorsUrlQueryValue) %}
{% set relatedTo = relatedTo|merge([{ targetElement: relatedColorsCats }]) %}
{% endif %}
{# --------------------- CATEGORY GROUP 2 - TYPES #}
{% set typesUrlQueryName = 'types'|t %} {# variable also used in the form name #}
{% set relatedTypesCats = [] %}
{% set typesUrlQueryValue = craft.app.request.getQueryParam(typesUrlQueryName)|split(',')|filter %}
{% if typesUrlQueryValue %}
{% set relatedTypesCats = typesCatGroup|filter(cat => cat.slug in typesUrlQueryValue) %}
{% set relatedTo = relatedTo|merge([{ targetElement: relatedTypesCats }]) %}
{% endif %}
{% if relatedTo|length > 1 %}
{% set relatedTo = ['and']|merge(relatedTo) %}
{% endif %}
{# --------------------- MAIN QUERY #}
{% set foodQuery = craft.entries()
.section('structureFood')
.orderBy('title')
.relatedTo(relatedTo ? relatedTo : null)
.with(['structureFoodColors', 'structureFoodTypes'])
.all()
%}
{# --------------------- FILTER LAYOUT #}
{# ID is used by Javascript - a wrapper around your form is necessary as elem for your instance #}
<div id="filter-wrapper-tags" class="grid grid-cols-1 md:grid-cols-3 gap-half mb-half">
{# MACRO TAGS SECTION #}
{% macro tag(id = "", content = "Category Name") %}
<div
class="inline-flex items-center bg-[#c4cad2] rounded-xl cursor-pointer text-0.6em px-3 py-1"
data-fn-tag-id="{{ id }}"
>
{# you can replace the SVG with one from your SVG Sprite #}
<svg class="h-2 w-2 mr-4px shrink-0" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
<div data-fn-tag-content>{{ content }}</div>
</div>
{% endmacro %}
{# IMPORTANT : HTML TEMPLATE FOR THE TAGS SECTION #}
{# template for tags - used by JS - not rendered by the browser #}
<template data-fn-tag-template>
{{ _self.tag() }}
</template>
{# COLORS FILTER #}
{% 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
id="{{ cat.slug }}"
class="inset-0 opacity-0 absolute -z-1 peer"
data-fn-tag-row="tag-row-colors"
type="checkbox"
name="{{ cat.slug }}"
value="{{ cat.slug }}"
{{(cat in relatedColorsCats) ? 'checked' }}
/>
<label
data-fn-tag-title="{{ cat.title }}" {# optionnal, value for the tag. If not present, label title will be used #}
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>
{# TAGS ROW with initial content to show tags on page load #}
<div id="tag-row-colors" class="flex flex-wrap mt-3 gap-2">
{% if colorsUrlQueryValue|length %}
{% for cat in relatedColorsCats %}
{{ _self.tag(cat.slug, cat.title) }}
{% endfor %}
{% endif %}
</div>
</form>
{% endif %}
{# TYPES FILTER #}
{% 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
id="{{ cat.slug }}"
class="inset-0 opacity-0 absolute -z-1 peer"
data-fn-tag-row="tag-row-types"
type="checkbox"
name="{{ cat.slug }}"
value="{{ cat.slug }}"
{{(cat in relatedTypesCats) ? '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>
{# TAGS ROW with initial content to show tags on page load #}
<div id="tag-row-types" class="flex flex-wrap mt-3 gap-2">
{% if typesUrlQueryValue|length %}
{% for cat in relatedTypesCats %}
{{ _self.tag(cat.slug, cat.title) }}
{% endfor %}
{% endif %}
</div>
</form>
{% endif %}
</div>
{# --------------------- LAYOUT WIH LOOP FOR CARDS #}
{# ID is used by Javascript - The div must be always visible (do not wrap an If condition around) #}
<div id="listing" class="grid grid-col-1 md:grid-cols-3 gap-third opacity-1 is-loading:opacity-40 transition-opacity">
{% 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>