<div class="acl-searchbox" style="width: 420px">
<div class="acl-searchbox__header">
<div class="acl-searchbox__search-icon"><i class="aforza-icons">acl_search</i></div>
<div class="autocomplete acl-searchbox__input">
<input autocomplete="off" id="routeBuilderSearch" type="text" name="routeBuilderSearch" placeholder="Type here">
</div>
<div class="acl-searchbox__filter-button acl-searchbox__filter-button--grey">
Filter <span class="acl-searchbox__filter-selected-count"></span>
</div>
</div>
<div class="acl-searchbox__filters-banner acl-searchbox__filters-banner--hidden">
<div class="acl-searchbox__filters-banner-preview"></div>
</div>
<div class="acl-chip-set acl-chip-set--filter acl-searchbox__filters acl-searchbox__filters--hidden">
<div class="acl-searchbox__filters-header">
<div class="acl-searchbox__filters-description">Select the filters for your search results</div>
<div class="acl-searchbox__filters-close"><i class="material-icons">close</i></div>
</div>
<div class="acl-searchbox__filters-chips"></div>
</div>
</div>
<script>
var filters = [{
id: 'filter01',
name: "Big"
},
{
id: 'filter02',
name: "Medium"
},
{
id: 'filter03',
name: "Small"
},
{
id: 'filter04',
name: "Xsmall"
},
{
id: 'filter05',
name: "Supermarket"
},
{
id: 'filter06',
name: "Petrol Station"
},
];
const searchboxEl = document.querySelector(".acl-searchbox");
const searchbox = new acl.ACLSearchboxSuggestions(searchboxEl, filters);
searchbox.autocomplete();
searchboxEl.addEventListener('selectedfilterchips', function(e) {
console.log('selectedfilterchips');
console.log(e.detail.chips);
});
searchboxEl.addEventListener('resultselected', function(e) {
console.log('resultselected');
console.log(e.detail.resultId);
});
searchboxEl.addEventListener('searchquery', function(e) {
console.log('searchquery');
console.log(e.detail.searchValue);
});
</script>
<div class="acl-searchbox" style="width: 420px">
<div class="acl-searchbox__header">
<div class="acl-searchbox__search-icon"><i class="aforza-icons">acl_search</i></div>
<div class="autocomplete acl-searchbox__input">
<input autocomplete="off" id="routeBuilderSearch" type="text" name="routeBuilderSearch"
placeholder="Type here">
</div>
<div class="acl-searchbox__filter-button acl-searchbox__filter-button--grey">
Filter <span class="acl-searchbox__filter-selected-count"></span>
</div>
</div>
<div class="acl-searchbox__filters-banner acl-searchbox__filters-banner--hidden">
<div class="acl-searchbox__filters-banner-preview"></div>
</div>
<div class="acl-chip-set acl-chip-set--filter acl-searchbox__filters acl-searchbox__filters--hidden">
<div class="acl-searchbox__filters-header">
<div class="acl-searchbox__filters-description">Select the filters for your search results</div>
<div class="acl-searchbox__filters-close"><i class="material-icons">close</i></div>
</div>
<div class="acl-searchbox__filters-chips"></div>
</div>
</div>
<script>
var filters = [
{ id: 'filter01', name: "Big" },
{ id: 'filter02', name: "Medium" },
{ id: 'filter03', name: "Small" },
{ id: 'filter04', name: "Xsmall" },
{ id: 'filter05', name: "Supermarket" },
{ id: 'filter06', name: "Petrol Station" },
];
const searchboxEl = document.querySelector(".acl-searchbox");
const searchbox = new acl.ACLSearchboxSuggestions(searchboxEl, filters);
searchbox.autocomplete();
searchboxEl.addEventListener('selectedfilterchips', function (e) {
console.log('selectedfilterchips');
console.log(e.detail.chips);
});
searchboxEl.addEventListener('resultselected', function (e) {
console.log('resultselected');
console.log(e.detail.resultId);
});
searchboxEl.addEventListener('searchquery', function (e) {
console.log('searchquery');
console.log(e.detail.searchValue);
});
</script>
/* No context defined. */
class ACLSearchboxSuggestions {
constructor(searchboxEl, filters) {
this.searchboxEl = searchboxEl;
this.filters = filters;
this.filterChips;
this.resultCount = 0;
this.searchValue;
this.results;
this.currentFocus;
this.resultsEl;
}
autocomplete(results) {
let self = this;
this.results = results;
let chipSet;
let allChips;
let selectedChips;
let filterCount;
this.filterButtonEl = this.searchboxEl.querySelector('.acl-searchbox__filter-button');
this.resultsEl = this.searchboxEl.querySelector('.acl-searchbox__results');
const searchInputEl = this.searchboxEl.querySelector('input');
const closeFiltersButtonEl = this.searchboxEl.querySelector('.acl-searchbox__filters-close');
const filterBannerEl = this.searchboxEl.querySelector('.acl-searchbox__filters-banner');
const filterBannerResultsEl = this.searchboxEl.querySelector('.acl-searchbox__filters-banner-preview');
const selectedFilterCountLabel = this.searchboxEl.querySelector('.acl-searchbox__filter-selected-count');
const filterPanel = this.searchboxEl.querySelector('.acl-searchbox__filters-chips');
const searchboxHeader = this.searchboxEl.querySelector('.acl-searchbox__header');
const searchboxFilters = this.searchboxEl.querySelector('.acl-searchbox__filters');
if (this.filters) {
addFilters(this.filters);
}
if (closeFiltersButtonEl) {
closeFiltersButtonEl.addEventListener('click', function() {
toggleFilterPanel();
if (filterCount > 0) {
filterBannerEl.classList.remove('acl-searchbox__filters-banner--hidden');
}
postFilterEventChanges();
});
}
if (this.filterButtonEl) {
this.filterButtonEl.addEventListener('click', function() {
toggleFilterPanel();
filterBannerEl.classList.add('acl-searchbox__filters-banner--hidden');
hideDropdownList(self.searchboxEl);
});
}
if (this.resultsEl) {
this.resultsEl.addEventListener('click', function(e) {
let autocompleteText = e.target.dataset.autocompleteItemName;
let autocompleteId = e.target.dataset.autocompleteItemId;
if (autocompleteText) {
searchInputEl.value = e.target.dataset.autocompleteItemName;
}
postResultSelectedEvent(autocompleteId);
toggleDropdownListVisibility(searchboxEl);
});
}
/*execute a function when someone writes in the text field:*/
searchInputEl.addEventListener('input', function(e) {
self.searchValue = this.value;
self.closeAllLists();
if (!self.searchValue) {
if (filterCount == 0) {
self.filterButtonEl.classList.add('acl-searchbox__filter-button--hidden', 'animated', 'fadeOut');
}
postSearchQueryEvent();
} else {
if (self.searchValue.length > 2 && self.results) {
self.generateResults(self.searchValue, self);
}
if (self.searchValue.length > 2 && self.filterButtonEl) {
self.filterButtonEl.classList.remove('acl-searchbox__filter-button--hidden', 'animated', 'fadeOut');
}
postSearchQueryEvent(self.searchValue);
}
});
/*execute a function presses a key on the keyboard:*/
searchInputEl.addEventListener('keydown', function(e) {
var x = document.getElementById(this.id + 'acl-autocomplete-list');
if (x) x = x.querySelectorAll('.acl-autocomplete__item');
if (e.keyCode == 40) {
/*If the arrow DOWN key is pressed, increase the currentFocus variable:*/
this.currentFocus++;
/*and and make the current item more visible:*/
addActive(x);
} else if (e.keyCode == 38) {
//up
/*If the arrow UP key is pressed, decrease the currentFocus variable:*/
this.currentFocus--;
/*and and make the current item more visible:*/
addActive(x);
} else if (e.keyCode == 13) {
/*If the ENTER key is pressed, prevent the form from being submitted,*/
e.preventDefault();
if (this.currentFocus > -1) {
/*and simulate a click on the "active" item:*/
if (x) {
x[this.currentFocus].click();
}
}
}
});
function addActive(x) {
/*a function to classify an item as "active":*/
if (!x) return false;
/*start by removing the "active" class on all items:*/
removeActive(x);
if (this.currentFocus >= x.length) this.currentFocus = 0;
if (this.currentFocus < 0) this.currentFocus = x.length - 1;
/*add class "autocomplete-active":*/
x[this.currentFocus].classList.add('autocomplete-active');
}
function removeActive(x) {
/*a function to remove the "active" class from all autocomplete items:*/
for (var i = 0; i < x.length; i++) {
x[i].classList.remove('autocomplete-active');
}
}
function toggleDropdownListVisibility(elmnt) {
const dropdown = elmnt.querySelector('.acl-autocomplete__items');
dropdown.classList.toggle('acl-autocomplete__items--hidden');
}
function hideDropdownList(elmnt) {
const dropdown = elmnt.querySelector('.acl-autocomplete__items');
if (dropdown) {
dropdown.classList.add('acl-autocomplete__items--hidden');
}
}
function postFilterEventChanges() {
let filterIds = 0;
if (selectedChips.length > 0) {
filterIds = selectedChips.map(selectedChip => {
return selectedChip.id;
});
}
self.searchboxEl.dispatchEvent(
new CustomEvent('selectedfilterchips', {
detail: {
chips: filterIds,
},
})
);
}
function postResultSelectedEvent(autocompleteId) {
self.searchboxEl.dispatchEvent(
new CustomEvent('resultselected', {
detail: {
resultId: autocompleteId,
},
})
);
}
function postSearchQueryEvent(searchValue) {
self.searchboxEl.dispatchEvent(
new CustomEvent('searchquery', {
detail: {
searchValue: searchValue,
},
})
);
}
function addFilters(filters) {
if (filterPanel) {
for (var i = 0; i < filters.length; i++) {
filterPanel.innerHTML += `<div class="acl-chip acl-ripple-upgraded" role="row" data-filter-text="${filters[i].name}" id="${filters[i].id}"
style="--acl-ripple-fg-size:57px; --acl-ripple-fg-scale:1.94633; --acl-ripple-fg-translate-start:16.4414px,
- 21.9688px; --acl-ripple-fg-translate-end: 19.3672px, -12.5px;">
<span role="button" tabindex="0" class="acl-chip__text">${filters[i].name}</span>
<div class="acl-chip__checkmark">
<svg class="acl-chip__checkmark-svg" viewBox="-3 -3 30 30">
<path class="" fill="white" stroke="none" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" /><path d="M0 0h24v24H0z" fill="none" />
</svg>
</div>
</div>`;
}
if (self.searchboxEl) {
getAllChips();
}
}
}
function toggleFilterPanel() {
searchboxFilters.classList.toggle('acl-searchbox__filters--hidden');
searchboxHeader.classList.toggle('acl-searchbox__header--hidden');
setSelectedFilterCount();
setFilterBannerPreview();
}
function getAllChips() {
const chipSetEls = Array.from(self.searchboxEl.querySelectorAll('.acl-chip-set'));
chipSetEls.forEach(el => {
chipSet = new acl.MDCChipSet(el);
});
return chipSet;
}
function getSelectedChips() {
allChips = getAllChips();
selectedChips = allChips.chips.filter(element => element.selected == true);
return selectedChips;
}
function setSelectedFilterCount() {
const selectedFilters = getSelectedChips();
filterCount = selectedFilters.length;
selectedFilterCountLabel.innerHTML = filterCount;
if (selectedFilters.length > 0) {
selectedFilterCountLabel.classList.add('acl-searchbox__filter-selected-count--visible');
self.filterButtonEl.classList.add('acl-searchbox__filter-button--inc-count');
} else {
selectedFilterCountLabel.classList.remove('acl-searchbox__filter-selected-count--visible');
filterBannerEl.classList.add('acl-searchbox__filters-banner--hidden');
self.filterButtonEl.classList.remove('acl-searchbox__filter-button--inc-count');
}
}
function setFilterBannerPreview() {
const selectedFilters = getSelectedChips();
filterBannerResultsEl.innerHTML = '';
if (selectedFilters.length > 0) {
selectedFilters.slice(0, 2).forEach(function(element) {
filterBannerResultsEl.innerHTML += `<div class="acl-searchbox__filters-banner-item" data-filter-id="${element.id}" id="${element.id}-preview" >
${element.root_.dataset.filterText}
<svg class="acl-searchbox__filters-banner-item-close" viewBox="-3 -3 30 30">
<path class="" fill="#1FACEC" stroke="none" d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" /><path d="M0 0h24v24H0z" fill="none" />
</svg>
</div> `;
});
}
if (selectedFilters.length > 2) {
filterBannerResultsEl.innerHTML += `<div class="acl-searchbox__filters-more"> + ${selectedFilters.length -
2} more</div>`;
const searchboxFilterPreviewMore = searchboxEl.querySelector('.acl-searchbox__filters-more');
searchboxFilterPreviewMore.addEventListener('click', function() {
toggleFilterPanel();
toggleDropdownListVisibility(searchboxEl);
filterBannerEl.classList.add('acl-searchbox__filters-banner--hidden');
});
}
let filterPreviewEls = searchboxEl.querySelectorAll('.acl-searchbox__filters-banner-item');
for (const filterEl of filterPreviewEls) {
filterEl.addEventListener('click', function(event) {
selectedChips.filter(obj => {
if (obj.selected == true && obj.id == event.target.dataset.filterId) {
obj.selected = false;
event.target.innerHTML = '';
}
});
setSelectedFilterCount();
setFilterBannerPreview();
postFilterEventChanges(searchboxEl);
});
}
}
}
closeAllLists() {
/*close all autocomplete lists in the document,
except the one passed as an argument:*/
var x = this.searchboxEl.getElementsByClassName('acl-autocomplete__items');
for (var i = 0; i < x.length; i++) {
x[i].parentNode.removeChild(x[i]);
}
}
showDropdownList() {
const dropdown = this.searchboxEl.querySelector('.acl-autocomplete__items');
dropdown.classList.remove('acl-autocomplete__items--hidden');
}
setResults(updatedResults) {
this.results = updatedResults;
this.generateResults(this.searchValue);
}
generateResults(inputValue) {
if (inputValue && this.resultsEl) {
this.closeAllLists();
this.resultCount = 0;
var a,
b,
i,
val = inputValue;
this.currentFocus = -1;
a = document.createElement('DIV');
a.setAttribute('id', this.id + 'acl-autocomplete-list');
a.setAttribute('class', 'acl-autocomplete__items');
this.resultsEl.appendChild(a);
for (i = 0; i < this.results.length; i++) {
const searchTerm = `${this.results[i].primary.toUpperCase()} ${this.results[
i
].secondary.searchValue.toUpperCase()}`;
if (searchTerm.includes(val.toUpperCase())) {
b = document.createElement('DIV');
b.classList.add('acl-autocomplete__item');
b.setAttribute(
'data-autocomplete-item-name',
`${this.results[i].primary} ${this.results[i].secondary.searchValue}`
);
b.setAttribute('data-autocomplete-item-id', `${this.results[i].id}`);
let ctaBtn = `<span class="acl-autocomplete__item-cta">View</span>`;
if (!this.results[i].cta) {
ctaBtn = '';
}
b.innerHTML += `<div class="acl-autocomplete__item-details">
<i class="aforza-icons acl-autocomplete__icon--${this.results[i].iconSize}" style="color: ${
this.results[i].iconColour
}">${this.results[i].icon}</i>
<strong> ${this.results[i].primary} </strong>
${this.results[i].seperator ? ' - ' : ' '} ${this.results[i].secondary.displayValue}
</div>
${ctaBtn}
`;
a.appendChild(b);
this.resultCount++;
} else {
var dropdownItems = this.searchboxEl.getElementsByClassName('acl-autocomplete__item');
if (dropdownItems) {
this.resultCount = dropdownItems.length;
} else {
this.resultCount = 0;
}
}
}
if (this.resultCount == 0) {
b = document.createElement('DIV');
b.classList.add('acl-autocomplete__item');
b.innerHTML = `<div class="acl-autocomplete__no-results">There are no results matching your search</div>`;
a.appendChild(b);
} else {
this.showDropdownList();
this.filterButtonEl.classList.remove('acl-searchbox__filter-button--hidden', 'animated', 'fadeIn');
}
}
}
}
export { ACLSearchboxSuggestions };
Buttons allow users to take actions, and make choices, with a single tap.
npm install ..//button<button class="acl-button">
<div class="acl-button__ripple"></div>
<span class="acl-button__label">Button</span>
</button>NOTE: Examples here use the generic
<button>, but users can also apply theacl-buttonclass to<a>elements.
@import "../button/acl-button";The button will work without JavaScript, but you can enhance it to have a ripple effect by instantiating MDCRipple on the root element. See MDC Ripple for details.
import {MDCRipple} from '..//ripple';
const buttonRipple = new MDCRipple(document.querySelector('.acl-button'));See Importing the JS component for more information on how to import JavaScript.
To style a contained button, add the acl-button--raised class to the <button> element for a contained button with elevation, or the acl-button--unelevated class for a contained button flush with the surface.
To style an outlined button, add the class acl-button--outlined to the <button> element.
We recommend using Material Icons from Google Fonts:
<head>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
</head>However, you can also use SVG, Font Awesome, or any other icon library you wish.
To add an icon, add an element with the acl-button__icon class inside the button element and set the attribute aria-hidden="true". The icon is set to 18px to meet legibility requirements.
<button class="acl-button">
<div class="acl-button__ripple"></div>
<i class="material-icons acl-button__icon" aria-hidden="true">favorite</i>
<span class="acl-button__label">Button</span>
</button>It’s also possible to use an SVG icon:
<button class="acl-button">
<div class="acl-button__ripple"></div>
<svg class="acl-button__icon" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="...">
...
</svg>
<span class="acl-button__label">Button</span>
</button>Certain icons make more sense to appear after the button’s text label rather than before. This can be accomplished by
putting the icon markup after the acl-button__label element.
<button class="acl-button">
<div class="acl-button__ripple"></div>
<span class="acl-button__label">Button</span>
<i class="material-icons acl-button__icon" aria-hidden="true">favorite</i>
</button>NOTE: The
acl-button__labelelement is required in order for the trailing icon to be styled appropriately.
To disable a button, add the disabled attribute directly to the <button>, or set the disabled attribute on the <fieldset> containing the button.
Disabled buttons cannot be interacted with and have no visual interaction effect.
<button class="acl-button" disabled>
<div class="acl-button__ripple"></div>
<span class="acl-button__label">Button</span>
</button>Material Design spec advises that touch targets should be at least 48 x 48 px. To meet this requirement, add the following to your button:
<div class="acl-touch-target-wrapper">
<button class="acl-button acl-button--touch">
<div class="acl-button__ripple"></div>
<span class="acl-button__label">My Accessible Button</span>
<div class="acl-button__touch"></div>
</button>
</div>Note that the outer acl-touch-target-wrapper element is only necessary if you want to avoid potentially overlapping touch targets on adjacent elements (due to collapsing margins).
| CSS Class | Description |
|---|---|
acl-button |
Mandatory. Defaults to a text button that is flush with the surface. |
acl-button__ripple |
Mandatory. Indicates the element which shows the ripple styling. |
acl-button--raised |
Optional. Styles a contained button that is elevated above the surface. |
acl-button--unelevated |
Optional. Styles a contained button that is flush with the surface. |
acl-button--outlined |
Optional. Styles an outlined button that is flush with the surface. |
acl-button__label |
Recommended.* Indicates the element containing the button’s text label. |
acl-button__icon |
Optional. Indicates the element containing the button’s icon. |
*NOTE: The
acl-button__labelelement is required for buttons with a trailing icon, but it is currently optional for buttons with no icon or a leading icon. In the latter cases, it is acceptable for the text label to simply exist directly within theacl-buttonelement. However, theacl-button__labelclass may become mandatory for all cases in the future, so it is recommended to always include it to be future-proof.
To customize a button’s color and properties, you can use the following mixins.
MDC Button uses MDC Theme‘s primary color by default. Use the following mixins to customize it.
| Mixin | Description |
|---|---|
acl-button-filled-accessible($container-fill-color) |
Sets the container fill color for a contained (raised or unelevated) button, and updates the button’s ink, icon, and ripple colors to meet accessibility standards |
These mixins will override the color of the container, ink, outline or ripple. It is up to you to ensure your button meets accessibility standards.
| Mixin | Description |
|---|---|
acl-button-container-fill-color($color) |
Sets the container fill color to the given color. |
acl-button-icon-color($color) |
Sets the icon color to the given color. |
acl-button-ink-color($color) |
Sets the ink color to the given color, and sets the icon color to the given color unless acl-button-icon-color is also used. |
acl-button-density($density-scale) |
Sets density scale for button. Supported density scale values (-3, -2, -1, 0). |
acl-button-height($height) |
Sets custom height of button. |
acl-button-shape-radius($radius, $density-scale, $rtl-reflexive) |
Sets rounded shape to button with given radius size. $density-scale is only required when $radius value is in percentage unit, defaults to $acl-button-density-default-scale. Set $rtl-reflexive to true to flip radius values in RTL context, defaults to false. |
acl-button-horizontal-padding($padding) |
Sets horizontal padding to the given number. |
acl-button-outline-color($color) |
Sets the outline color to the given color. |
acl-button-outline-width($width, $padding) |
Sets the outline width to the given number (defaults to 2px) and adjusts padding accordingly. $padding is only required in cases where acl-button-horizontal-padding is also included with a custom value. |
In browsers that fully support CSS custom properties, the above mixins will work if you pass in a MDC Theme property (e.g. primary) as an argument. However, Edge does not fully support CSS custom properties. If you are using the acl-button-container-fill-color mixin, you must pass in an actual color value for support in Edge.