Use the checkboxes dynamic component when you need to help users select one or more options from a list of options.
Do not use the checkboxes dynamic component when you need to help users:
In these cases, use (non-dynamic) checkboxes instead.
The checkboxes dynamic component is an adapted version of the checkboxes component that can handle various numbers of checkboxes, both short lists of checkboxes as well as average-length lists as long lists of checkboxes.
Depending on the number of checkboxes, the checkboxes dynamic component presents the checkboxes in a different way:
When there are more than 20 checkboxes, what would normally be a long list of checkboxes, is replaced by showing only the first 3 checkboxes with a “Show more” button that opens a modal where the list of checkboxes is placed instead.
The behavior is as follows:
When the user selects one or more of the first 3 checkboxes that are already visible outside the modal, the corresponding checkboxes inside the modal are also selected.
When one or more checkboxes are selected, inside or outside the modal, the user can see this both:
This display uses the accordion JS and @digipolis-gent/modal.
We refer you to the accordion and
modal components for a detailed account on how to use these libraries.
Create new accordions for each element with className .checkbox-accordion
.
new Accordion(element);
Create new modals for each element with className .modal:not(.has-custom-binding)
,
if you haven’t done so already.
new Modal(element);
On top of that, create a new CheckboxFilterDynamic instances
for each element with className checkbox-filter-dynamic
.
Don’t forget to add your translation for the hiddenTagText.
new CheckboxFilterDynamic(element, {hiddenTagText: 'Remove tag'})
The checkboxes shown as preview are copies!
You link them to their original checkbox by providing a data-original
attribute
with the ID of the original checkbox.
Do not give them a name if you don’t want them to show up in your FormData!
Option | Type | Default | Description |
---|---|---|---|
filterField |
String |
.checkbox-filter__filter |
QuerySelector for the filter input field. |
modalPreview |
String |
.modal-preview |
QuerySelector for the wrapper containing modal preview checkboxes. |
accordionPreview |
String |
.accordion-preview |
QuerySelector for the wrapper containing accordion preview checkboxes. |
accordionBtn |
String |
button.accordion--button |
QuerySelector for the button that toggles the accordion. |
previewCheckboxes |
String |
div.checkbox.preview |
QuerySelector for the preview checkboxes. |
checkboxes |
String |
div.checkbox:not(.preview) |
QuerySelector for the checkboxes. |
selectedContainer |
String |
.checkbox-filter__selected |
QuerySelector for the container holding the selected filter tags. |
checkboxContainers |
String |
.checkbox-filter__checkboxes |
QuerySelector for the container holding the checkboxes. |
openBtn |
String |
.checkbox-filter__open |
QuerySelector for the button used to open the modal. |
submitBtn |
String |
.checkbox-filter__submit |
QuerySelector for the button used to confirm the selection and close the modal. |
closeBtns |
String |
.checkbox-filter__close |
QuerySelector for a list of buttons used to close the modal. |
resultSpan |
String |
.checkbox-filter__result |
QuerySelector for the container to display the number of search results. |
makeTags |
Boolean |
true |
Prevent creation of tags, in case you have your own implementation. |
hiddenTagText |
String |
Remove tag |
Text used behind the remove-tag button, insert your translation here. |
onRemoveTag |
function |
function(checkbox, tag){} |
A hook to append your own logic after a tag has been removed. |
{% if options|length < 6 %}
{% include '@checkboxes' %}
{% else %}
<fieldset class="form-item {{ modifier }} checkbox-filter-dynamic">
<legend>
{{ label }}
{% if label_optional %}
<span class="label-optional">({{ label_optional }})</span>
{% endif %}
</legend>
<div class="form-item">
{% if field_description %}
{% include '@field-message' with {
field_message: field_description,
modifier: null
} %}
{% endif %}
<div class="form-columns">
<div class="form-item-column">
{% if options | length < 21 %}
<div class="accordion-preview">
{% for option in options|slice(0, 3) %}
{% include '@input' with {
id: "input-" ~ id ~ "-" ~ modifier ~ "-" ~ option.id,
type: 'checkbox',
name: option.name,
label: option.label,
modifier: modifier
} %}
{% endfor %}
</div>
<div class="checkbox-accordion">
<div class="accordion--content" aria-hidden="true"
hidden="hidden" id="{{ "hidden-options-" ~ id ~ "-" ~ modifier }}">
{% for option in options|slice(3, 21) %}
{% include '@input' with {
id: "input-" ~ id ~ "-" ~ modifier ~ "-" ~ option.id,
type: 'checkbox',
name: option.name,
label: option.label,
modifier: modifier
} %}
{% endfor %}
</div>
<button type="button"
class="accordion--button button button-secondary button-small icon-left"
aria-expanded="false"
aria-controls="{{ "hidden-options-" ~ id ~ "-" ~ modifier }}">
Show more
</button>
</div>
{% else %}
<div class="modal-preview">
{% for option in options|slice(0, 3) %}
{% include '@input' with {
id: "input-" ~ id ~ "-" ~ modifier ~ "-" ~ option.id ~ "-preview",
type: 'checkbox',
name: null,
attributes: 'data-original="' ~ "input-" ~ id ~ "-" ~ modifier ~ "-" ~ option.id ~ '"',
label: option.label,
modifier: modifier,
modifierWrapper: "preview",
value: option.value
} %}
{% endfor %}
</div>
{% set modalTitle = label %}
{% if label_optional %}
{% set modalTitle = modalTitle ~ '<span class="label-optional">(' ~ label_optional ~ ')</span>' %}
{% endif %}
{% set modalContent %}
<div class="form-item checkbox-filter__filter__search-wrapper">
<input type="search" id="checkboxes__filter_id_{{ modifier }}"
class="checkbox-filter__filter"
aria-label="Filter the list below">
<div aria-live="polite" aria-atomic="true"
class="checkbox-filter__result-wrapper">
<span class="checkbox-filter__result">#</span> filter(s) found
</div>
</div>
{% include '@tag-list' with { modifier: 'checkbox-filter__selected' } %}
{% include '@checkboxes' %}
{% endset %}
{% set modalActions %}
<button type="button"
class="button button-primary checkbox-filter__submit modal-close"
data-target="{{ 'modal' ~ modifier }}">Confirm selection
</button>
{% endset %}
{% include "@modal" with {
id: 'modal' ~ modifier,
title: modalTitle,
title_heading_level: 'h2',
classes: 'checkbox-filter__modal',
cancelClasses: 'checkbox-filter__close',
modifier: 'fixed-height',
content: modalContent,
actions: modalActions,
hasFocusables: true
} %}
<button type="button"
class="button button-secondary button-small icon-left icon-search checkbox-filter__open"
aria-controls="{{ 'modal' ~ modifier }}"
aria-expanded="false">
Show more
</button>
{% endif %}
</div>
<div class="form-item-column">
{% if modifier == 'error' %}
{% include '@field-message' with {
"modifier": "error"
} %}
{% endif %}
{% if modifier == 'success' %}
{% include '@field-message' with {
"modifier": "success"
} %}
{% endif %}
</div>
</div>
</div>
</fieldset>
{% endif %}
<fieldset class="form-item error checkbox-filter-dynamic">
<legend>
Checkboxes
</legend>
<div class="form-item">
<div class="form-columns">
<div class="form-item-column">
<div class="modal-preview">
<div class="checkbox preview">
<input type="checkbox" id="input-with-error-error-checkbox-20-default-20-plus-preview" data-original="input-with-error-error-checkbox-20-default-20-plus" value="20" class="checkbox error" />
<label for="input-with-error-error-checkbox-20-default-20-plus-preview">Checkbox option 20</label>
</div>
<div class="checkbox preview">
<input type="checkbox" id="input-with-error-error-checkbox-19-default-20-plus-preview" data-original="input-with-error-error-checkbox-19-default-20-plus" value="19" class="checkbox error" />
<label for="input-with-error-error-checkbox-19-default-20-plus-preview">Checkbox option 19</label>
</div>
<div class="checkbox preview">
<input type="checkbox" id="input-with-error-error-checkbox-18-default-20-plus-preview" data-original="input-with-error-error-checkbox-18-default-20-plus" value="18" class="checkbox error" />
<label for="input-with-error-error-checkbox-18-default-20-plus-preview">Checkbox option 18</label>
</div>
</div>
<div id="modalerror" class="modal modal--fixed-height checkbox-filter__modal" role="dialog" aria-modal="true" aria-labelledby="modalerror-title" tabindex="-1">
<div class="modal-inner">
<div class="modal-header">
<button type="button" class="button close icon-cross modal-close checkbox-filter__close" data-target="modalerror">
<span>Close</span>
</button>
</div>
<div class="modal-content">
<h2 id="modalerror-title">Checkboxes</h2>
<div class="form-item checkbox-filter__filter__search-wrapper">
<input type="search" id="checkboxes__filter_id_error" class="checkbox-filter__filter" aria-label="Filter the list below">
<div aria-live="polite" aria-atomic="true" class="checkbox-filter__result-wrapper">
<span class="checkbox-filter__result">#</span> filter(s) found
</div>
</div>
<div class="tag-list-wrapper">
<ul class="tag-list checkbox-filter__selected">
</ul>
</div>
<fieldset class="form-item error">
<legend>
Checkboxes
</legend>
<div class="form-item">
<div class="form-columns">
<div class="form-item-column">
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-20-default-20-plus" name="checkboxes-dynamic" value="20" class="checkbox error" />
<label for="input-with-error-error-checkbox-20-default-20-plus">Checkbox option 20</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-19-default-20-plus" name="checkboxes-dynamic" value="19" class="checkbox error" />
<label for="input-with-error-error-checkbox-19-default-20-plus">Checkbox option 19</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-18-default-20-plus" name="checkboxes-dynamic" value="18" class="checkbox error" />
<label for="input-with-error-error-checkbox-18-default-20-plus">Checkbox option 18</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-17-default-20-plus" name="checkboxes-dynamic" value="17" class="checkbox error" />
<label for="input-with-error-error-checkbox-17-default-20-plus">Checkbox option 17</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-16-default-20-plus" name="checkboxes-dynamic" value="16" class="checkbox error" />
<label for="input-with-error-error-checkbox-16-default-20-plus">Checkbox option 16</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-15-default-20-plus" name="checkboxes-dynamic" value="15" class="checkbox error" />
<label for="input-with-error-error-checkbox-15-default-20-plus">Checkbox option 15</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-14-default-20-plus" name="checkboxes-dynamic" value="14" class="checkbox error" />
<label for="input-with-error-error-checkbox-14-default-20-plus">Checkbox option 14</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-13-default-20-plus" name="checkboxes-dynamic" value="13" class="checkbox error" />
<label for="input-with-error-error-checkbox-13-default-20-plus">Checkbox option 13</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-12-default-20-plus" name="checkboxes-dynamic" value="12" class="checkbox error" />
<label for="input-with-error-error-checkbox-12-default-20-plus">Checkbox option 12</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-11-default-20-plus" name="checkboxes-dynamic" value="11" class="checkbox error" />
<label for="input-with-error-error-checkbox-11-default-20-plus">Checkbox option 11</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-10-default-20-plus" name="checkboxes-dynamic" value="10" class="checkbox error" />
<label for="input-with-error-error-checkbox-10-default-20-plus">Checkbox option 10</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-9-default-20-plus" name="checkboxes-dynamic" value="9" class="checkbox error" />
<label for="input-with-error-error-checkbox-9-default-20-plus">Checkbox option 9</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-8-default-20-plus" name="checkboxes-dynamic" value="8" class="checkbox error" />
<label for="input-with-error-error-checkbox-8-default-20-plus">Checkbox option 8</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-7-default-20-plus" name="checkboxes-dynamic" value="7" class="checkbox error" />
<label for="input-with-error-error-checkbox-7-default-20-plus">Checkbox option 7</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-6-default-20-plus" name="checkboxes-dynamic" value="6" class="checkbox error" />
<label for="input-with-error-error-checkbox-6-default-20-plus">Checkbox option 6</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-5-default-20-plus" name="checkboxes-dynamic" value="5" class="checkbox error" />
<label for="input-with-error-error-checkbox-5-default-20-plus">Checkbox option 5</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-4-default-20-plus" name="checkboxes-dynamic" value="4" class="checkbox error" />
<label for="input-with-error-error-checkbox-4-default-20-plus">Checkbox option 4</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-3-default-20-plus" name="checkboxes-dynamic" value="3" class="checkbox error" />
<label for="input-with-error-error-checkbox-3-default-20-plus">Checkbox option 3</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-2-default-20-plus" name="checkboxes-dynamic" value="2" class="checkbox error" />
<label for="input-with-error-error-checkbox-2-default-20-plus">Checkbox option 2</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-1-default-20-plus" name="checkboxes-dynamic" value="1" class="checkbox error" />
<label for="input-with-error-error-checkbox-1-default-20-plus">Checkbox option 1</label>
</div>
<div class="checkbox">
<input type="checkbox" id="input-with-error-error-checkbox-0-default-20-plus" name="checkboxes-dynamic" class="checkbox error" />
<label for="input-with-error-error-checkbox-0-default-20-plus">Checkbox option 0</label>
</div>
</div>
<div class="form-item-column">
<div class="field-message error" role="alert" id="with-error-validation">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec laoreet, urna sit amet convallis rhoncus, felis ex.
<div class="accolade "></div>
</div>
</div>
</div>
</div>
</fieldset>
</div>
<div class="modal-actions">
<button type="button" class="button button-primary checkbox-filter__submit modal-close" data-target="modalerror">Confirm selection
</button>
</div>
</div>
<div class="modal-overlay modal-close checkbox-filter__close" data-target="modalerror" tabindex="-1"></div>
</div>
<button type="button" class="button button-secondary button-small icon-left icon-search checkbox-filter__open" aria-controls="modalerror" aria-expanded="false">
Show more
</button>
</div>
<div class="form-item-column">
<div class="field-message error" role="alert" id="with-error">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec laoreet, urna sit amet convallis rhoncus, felis ex.
<div class="accolade "></div>
</div>
</div>
</div>
</div>
</fieldset>
{
"label": "Checkboxes",
"id": "with-error",
"field_description": null,
"field_message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec laoreet, urna sit amet convallis rhoncus, felis ex.",
"description": "Description checkboxes.",
"options": [
{
"label": "Checkbox option 20",
"id": "checkbox-20-default-20-plus",
"value": 20,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 19",
"id": "checkbox-19-default-20-plus",
"value": 19,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 18",
"id": "checkbox-18-default-20-plus",
"value": 18,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 17",
"id": "checkbox-17-default-20-plus",
"value": 17,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 16",
"id": "checkbox-16-default-20-plus",
"value": 16,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 15",
"id": "checkbox-15-default-20-plus",
"value": 15,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 14",
"id": "checkbox-14-default-20-plus",
"value": 14,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 13",
"id": "checkbox-13-default-20-plus",
"value": 13,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 12",
"id": "checkbox-12-default-20-plus",
"value": 12,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 11",
"id": "checkbox-11-default-20-plus",
"value": 11,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 10",
"id": "checkbox-10-default-20-plus",
"value": 10,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 9",
"id": "checkbox-9-default-20-plus",
"value": 9,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 8",
"id": "checkbox-8-default-20-plus",
"value": 8,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 7",
"id": "checkbox-7-default-20-plus",
"value": 7,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 6",
"id": "checkbox-6-default-20-plus",
"value": 6,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 5",
"id": "checkbox-5-default-20-plus",
"value": 5,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 4",
"id": "checkbox-4-default-20-plus",
"value": 4,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 3",
"id": "checkbox-3-default-20-plus",
"value": 3,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 2",
"id": "checkbox-2-default-20-plus",
"value": 2,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 1",
"id": "checkbox-1-default-20-plus",
"value": 1,
"name": "checkboxes-dynamic"
},
{
"label": "Checkbox option 0",
"id": "checkbox-0-default-20-plus",
"value": 0,
"name": "checkboxes-dynamic"
}
],
"modifier": "error"
}
.checkbox-filter-dynamic {
.modal-preview,
.accordion-preview,
.checkbox-accordion {
.checkbox:last-child {
margin-bottom: .5rem;
}
}
.checkbox-accordion {
.accordion--content {
// Adjust for checkbox focus outline.
margin: -4px -4px 0;
padding: 4px 4px 0;
transition: max-height .5s ease-in-out;
overflow: hidden;
}
.accordion--button {
&[aria-expanded=false] {
@include icon(chevron-down);
}
&[aria-expanded=true] {
@include icon(chevron-up);
}
}
}
}
.checkbox-filter__modal {
.tag-list-wrapper {
min-height: 2.2rem;
margin-bottom: 1.2rem;
}
.tag-list {
.tag {
margin: 0;
}
}
}
'use strict';
(function () {
if (Accordion) { // eslint-disable-line no-undef
const selected = document.querySelectorAll('.checkbox-accordion');
for (let i = selected.length; i--;) {
new Accordion(selected[i]); // eslint-disable-line no-undef
}
}
if (CheckboxFilterDynamic) { // eslint-disable-line no-undef
const selected = document.querySelectorAll('.checkbox-filter-dynamic');
for (let i = selected.length; i--;) {
new CheckboxFilterDynamic(selected[i], {hiddenTagText: 'Remove tag'}); // eslint-disable-line no-undef
}
}
})();
'use strict';
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
}
else {
if (typeof exports === 'object') {
module.exports = factory();
}
else {
root.CheckboxFilterDynamic = factory();
}
}
})(this || window, function () {
return function (elem, options) {
options = options || {};
options.filterField = options.filterField || '.checkbox-filter__filter';
options.modalPreview = options.modalPreview || '.modal-preview';
options.accordionPreview = options.accordionPreview || '.accordion-preview';
options.accordionBtn = options.accordionBtn || 'button.accordion--button';
options.previewCheckboxes = options.previewCheckboxes || 'div.checkbox.preview';
options.checkboxes = options.checkboxes || 'div.checkbox:not(.preview)';
options.selectedContainer = options.selectedContainer || '.checkbox-filter__selected';
options.checkboxContainers = options.checkboxContainers || '.checkbox-filter__checkboxes';
options.openBtn = options.openBtn || '.checkbox-filter__open';
options.submitBtn = options.submitBtn || '.checkbox-filter__submit';
options.closeBtns = options.closeBtns || '.checkbox-filter__close';
options.resultSpan = options.resultSpan || '.checkbox-filter__result';
options.makeTags = options.makeTags !== false;
options.hiddenTagText = options.hiddenTagText || 'remove tag';
options.onRemoveTag = options.onRemoveTag || function () {};
/**
* Filter input field.
* @type {HTMLInputElement}
*/
const filterField = elem.querySelector(options.filterField);
/**
* Container for the modal preview checkboxes.
* @type {HTMLElement}
*/
const modalPreview = elem.querySelector(options.modalPreview);
/**
* Container for the accordion preview checkboxes.
* @type {HTMLElement}
*/
const accordionPreview = elem.querySelector(options.accordionPreview);
/**
* Button to toggle the accordion.
* @type {HTMLElement}
*/
const accordionBtn = elem.querySelector(options.accordionBtn);
/**
* List of preview checkboxes, these exist outside of the modal.
* @type {NodeList|Array}
*/
const previewCheckboxes = elem.querySelectorAll(options.previewCheckboxes) || [];
/**
* List of checkboxwrappers, each containing a checkbox and a label.
* @type {NodeList|Array}
*/
const checkboxes = elem.querySelectorAll(options.checkboxes) || [];
/**
* Container to display the selected items.
* @type {HTMLElement}
*/
const selectedContainer = elem.querySelector(options.selectedContainer);
/**
* Container for the checkboxes.
* @type {HTMLElement}
*/
const checkboxContainers = elem.querySelectorAll(options.checkboxContainers);
/**
* Button to trigger opening the modal.
* @type {HTMLElement}
*/
const openBtn = elem.querySelector(options.openBtn);
/**
* Button to confirm the selection and close the modal.
* @type {Element}
*/
const submitBtn = elem.querySelector(options.submitBtn);
/**
* A list of elements to trigger closing the modal.
* At least one must have the button role.
* @type {NodeList}
*/
const closeBtns = elem.querySelectorAll(options.closeBtns);
/**
* Container to display the number of search results.
* @type {HTMLElement}
*/
const resultSpan = elem.querySelector(options.resultSpan);
/**
* Store the checked checkboxes prior to making changes.
* @type {Array}
*/
let selectedFilters = [];
/**
* Filter the displayed checkboxes.
* @param {boolean} clear Clear the filtervalue if true.
*/
const filter = clear => {
if (!filterField) {
return;
}
if (clear) {
filterField.value = '';
}
let count = 0;
[].slice.call(checkboxContainers).forEach(container => {
container.style.display = 'none';
});
checkboxLoop(({checkboxWrapper, checkbox, label}) => {
if (
!label ||
label.textContent
.toUpperCase()
.indexOf(filterField.value.toUpperCase()) === -1
) {
checkboxWrapper.setAttribute('hidden', 'true');
checkbox.setAttribute('hidden', 'true');
}
else {
checkboxWrapper.removeAttribute('hidden');
checkbox.removeAttribute('hidden');
count++;
}
});
[].slice.call(checkboxContainers).forEach(container => {
let displayedCount = container.querySelectorAll(`${options.checkboxes}:not([hidden])`).length;
if (displayedCount) {
container.style.display = '';
}
});
if (resultSpan) {
resultSpan.textContent = '' + count;
}
};
/**
* Make a tag.
* @param {HTMLInputElement} checkbox Input type checkbox.
* @param {Element} label Label for the input type checkbox.
* @return {Element} A gent styleguide tag component.
*/
const makeTag = (checkbox, label) => {
const li = document.createElement('li');
const tag = document.createElement('span');
tag.className = 'tag filter';
tag.textContent = label.textContent;
tag.setAttribute('data-value', checkbox.value);
const button = document.createElement('button');
button.type = 'button';
button.innerHTML = `<span class="visually-hidden">${options.hiddenTagText} ${label.textContent}</span>`;
button.addEventListener('click', () => checkbox.click());
tag.appendChild(button);
li.appendChild(tag);
return li;
};
/**
* Remove a tag from the selectedContainer.
* @param {HTMLInputElement} checkbox Input type checkbox.
*/
const removeTag = checkbox => {
if (options.makeTags && selectedContainer) {
const tag = selectedContainer.querySelector('.filter[data-value="' + checkbox.value + '"]');
if (tag) {
selectedContainer.removeChild(tag.parentElement);
options.onRemoveTag(checkbox, tag);
}
}
};
const addTag = (checkbox, label) => {
if (options.makeTags && selectedContainer) {
selectedContainer.appendChild(makeTag(checkbox, label));
}
};
/**
* Loop over all checkboxes and execute a callback for each iteration.
* @param {function} next The callback function.
*/
const checkboxLoop = next => {
for (let i = 0; i < checkboxes.length; i++) {
let checkboxWrapper = checkboxes[i];
let checkbox = checkboxWrapper.querySelector('input[type=checkbox]');
let label = checkboxWrapper.querySelector('label');
// Sometimes the input element isn't rendered at this point.
if (checkbox && label) {
next({checkboxWrapper, checkbox, label});
}
}
};
/**
* Reset the component to it's stored value.
*/
const reset = () => {
checkboxLoop(({checkbox}) => {
const isSelected = selectedFilters.indexOf(checkbox) !== -1;
if ((isSelected && !checkbox.checked) || (!isSelected && checkbox.checked)) {
checkbox.click();
}
});
};
/**
* Initialise the component.
*/
const init = () => {
checkboxLoop(({checkbox, label}) => {
if (checkbox.checked && !checkbox.indeterminate) {
addTag(checkbox, label);
}
});
filter(true);
};
/**
* Bind two checkboxes
* @param {HTMLInputElement} a A checkbox.
* @param {HTMLInputElement} b Another checkbox.
*/
const bindState = (a, b) => {
if (a.checked !== b.checked) {
a.click();
}
};
/**
* Bind the preview checkboxes to their original counterpart in the modal.
*/
const addPreviewCheckboxesEvent = () => {
if (!previewCheckboxes.length) {
return;
}
for (let i = previewCheckboxes.length; i--;) {
const checkboxWrapper = previewCheckboxes[i];
const checkbox = checkboxWrapper.querySelector('input[type=checkbox]');
const original = elem.querySelector('#' + checkbox.getAttribute('data-original'));
if (checkbox) {
checkbox.addEventListener('change', () => bindState(original, checkbox));
original.addEventListener('change', () => bindState(checkbox, original));
}
}
};
const addModalEvents = () => {
// Enable opening the modal.
if (openBtn) {
openBtn.addEventListener('click', () => {
selectedFilters = [];
checkboxLoop(({checkbox}) => {
if (checkbox.checked) {
selectedFilters.push(checkbox);
}
});
document.addEventListener('keydown', handleKeyboardInput);
});
}
// Add close events to all closeBtns.
if (closeBtns) {
for (let i = closeBtns.length; i--;) {
closeBtns[i].addEventListener('click', () => {
reset();
document.removeEventListener('keydown', handleKeyboardInput);
});
}
}
// Update selectedFilters and close.
if (submitBtn) {
submitBtn.addEventListener('click', () => {
document.removeEventListener('keydown', handleKeyboardInput);
});
}
// Make sure the filter method is not repeated while typing.
if (filterField) {
let filterTimeOut = null;
filterField.addEventListener('input', () => {
if (filterTimeOut) {
clearTimeout(filterTimeOut);
}
filterTimeOut = setTimeout(filter, 200);
});
}
};
const createPreviewClone = (checkboxWrapper, checkbox) => {
if (!modalPreview) {
return;
}
if (modalPreview.querySelector('#' + checkbox.id + '-preview')) {
return;
}
const wrapperClone = checkboxWrapper.cloneNode(true);
const checkboxClone = wrapperClone.querySelector('input');
const labelClone = wrapperClone.querySelector('label');
checkboxClone.name = null;
checkboxClone.id += '-preview';
labelClone.for += '-preview';
checkbox.addEventListener('change', () => bindState(checkboxClone, checkbox));
checkboxClone.addEventListener('change', () => bindState(checkbox, checkboxClone));
modalPreview.appendChild(wrapperClone);
};
const addAccordionEvent = () => {
if (!accordionBtn || !accordionPreview) {
return;
}
accordionBtn.addEventListener('click', () =>
checkboxLoop(({checkboxWrapper, checkbox}) => {
if (checkbox.checked && !accordionPreview.contains(checkboxWrapper)) {
accordionPreview.appendChild(checkboxWrapper);
}
})
);
};
/**
* Add all events.
*/
const addEvents = () => {
addModalEvents();
addPreviewCheckboxesEvent();
addAccordionEvent();
// Add events for all checkboxes.
checkboxLoop(({checkboxWrapper, checkbox, label}) => {
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
addTag(checkbox, label);
createPreviewClone(checkboxWrapper, checkbox);
}
else {
removeTag(checkbox);
}
});
});
};
/**
* Handle keyboard input
* @param {object} e event
*/
const handleKeyboardInput = e => {
let keyCode = e.keyCode || e.which;
if (keyCode === 27) {
e.preventDefault();
reset();
}
};
init();
addEvents();
return {};
};
});