module_builder-8.x-3.x-dev/js/component_form.autocomplete.js
js/component_form.autocomplete.js
/**
* @file
* Custom autocomplete for component forms.
*
* Autocomplete for textfields which supports:
* - multiple lines per item, with match text and a description.
* - links in the description which can be clicked without selecting the item.
*
* Data for the autocomplete is found int drupalSettings rather than retrieved
* from a route.
*/
(function ($, Drupal, drupalSettings) {
'use strict';
/**
* Holds all the list elements for the option sets.
*/
var listElements = {};
/**
* Holds all the option elements, nested by option set name.
*/
var optionElements = {};
/**
* Holds the strings to match, nested by option set name.
*/
var optionSetTexts = {};
Drupal.behaviors.moduleBuilderComponentAutocomplete = {
attach: function (context, settings) {
var $textField = $("input[data-option-set]");
// Build the dropdowns and the option texts.
jQuery.each(drupalSettings.moduleBuilder.options, function (optionSetName, optionSetValues) {
// Skip if we've already created a UL element for this option set.
if (optionSetName in listElements) {
return;
}
listElements[optionSetName] = $("<ul class='module-builder-dropdown-list'></ul>");
// Set the width to that of the textfields.
listElements[optionSetName].width(
$textField.width() +
parseFloat($textField.css('padding-left')) +
parseFloat($textField.css('padding-right'))
);
optionElements[optionSetName] = Array();
optionSetTexts[optionSetName] = Array();
jQuery.each(optionSetValues, function (optionKey, optionValue) {
var optionHtml = '<li><p class="text">' + optionValue[0] + '</p>';
if (optionValue[1]) {
optionHtml += '<p class="form-item__description">' + optionValue[1] + '</p>';
}
if (optionValue[2]) {
optionHtml += '<p class="form-item__description"><a target="_blank" href="' + optionValue[2] + '">[documentation]</a></p>';
}
optionHtml += '</li>';
var newOption = $(optionHtml);
listElements[optionSetName].append(newOption);
optionElements[optionSetName].push(newOption);
optionSetTexts[optionSetName].push(optionKey);
// Clicking on an option (but not a link inside it) updates the
// textfield.
newOption.on('click', function (e) {
if (e.target.nodeName == 'A') {
return;
}
// Set the value of the text field.
listElements[optionSetName].attached.value = optionKey;
// Hide the dropdown.
listElements[optionSetName].hide();
});
});
});
/**
* Filters an option list.
*
* @param optionSetName
* The option set name.
* @param filterText
* The text to filter by.
*/
var filterList = function (optionSetName, filterText) {
// Show all items if the textfield text is empty.
if (filterText == '') {
listElements[optionSetName].show();
optionElements[optionSetName].forEach(function (optionText) {
optionText.show();
});
return;
}
listElements[optionSetName].show();
// Hide or show each list item.
optionSetTexts[optionSetName].forEach(function (optionText, index) {
var textMatch = optionText.search(new RegExp(filterText, 'i')) !== -1;
optionElements[optionSetName][index].toggle(textMatch);
});
}
/**
* Attaches the option set to an autocomplete textfield that gets focus.
*/
$textField.on('focus', function () {
var optionSetName = this.getAttribute('data-option-set');
if (listElements[optionSetName].attached != this) { // ARGH no effect!
// Put the option set after the text field, and show it.
listElements[optionSetName].show();
$(this).after(listElements[optionSetName]);
// Tell the option set which text field it is currently attached to.
listElements[optionSetName].attached = this;
}
// Filter for the current text.
filterList(optionSetName, this.value);
});
/**
* Hides the option set when an autocomplete textfield loses focus.
*
* We exclude the dropdown list, so that clicks in the dropdown count as
* still being in this focus.
*/
$textField.on('focusout', function () {
var siblingHover = $(this).parent().find("ul:hover");
if (siblingHover.length) {
return;
}
var optionSetName = $(this).attr('data-option-set');
listElements[optionSetName].hide();
});
/**
* Searches in the option set when the user types in the textfield.
*/
$textField.on('keyup', function (e) {
var optionSetName = this.getAttribute('data-option-set');
var query = $(e.target).val().toLowerCase();
filterList(optionSetName, query);
});
},
detach: function (context, settings, trigger) {
// In all cases, detach all the ULs from where they are, so that
// they are not removed along with any DOM that gets replaced by AJAX.
jQuery.each(listElements, function (key, $listElement) {
$listElement.detach();
});
},
};
})(jQuery, Drupal, drupalSettings);
