digitalmarketplace-frontend
Version:
Digital Marketplace Frontend contains assets and components used in Digital Marketplace projects
187 lines (185 loc) • 9.01 kB
JavaScript
class ListInput {
static moduleName = 'dm-list-input';
static itemContainerClass = 'dm-list-input__item-container';
static hiddenItemClass = 'dm-list-input__item--hidden';
static visibleItemClass = 'dm-list-input__item--visible';
static itemInputClass = 'dm-list-input__item-input';
static itemErrorClass = 'dm-list-input__item--error';
static itemErrorMsgClass = 'dm-list-input-error-message';
static itemCounterClass = 'dm-list-input__counter';
static inputErrorClass = 'govuk-input--error';
static removeButtonClass = 'dm-list-input__item-remove';
static hiddenRemoveButtonClass = 'dm-list-input__item-remove--hidden';
static formGroupErrorClass = 'govuk-form-group--error';
static hiddenAddButtonClass = 'dm-list-input__item-add--hidden';
static addButtonRemainingClass = 'dm-list-input__js-remaining-counter';
$module;
$allVisibleItems;
$addAnotherButton;
$allItems;
constructor($module) {
this.$module = $module;
this.$allVisibleItems = this.findVisibleItems();
this.$addAnotherButton = this.$module.find('.dm-list-input__item-add');
this.$allItems = this.$module.find('.dm-list-input__item');
}
init() {
this.hideEmptyItems();
this.updateAllCounters();
this.addRemoveClickEvent();
this.toggleAddAnotherButton();
this.addAddClickEvent();
}
findVisibleItems() {
return this.$module.find('.dm-list-input__item--visible');
}
// Hide all items that do not have a value (except for the first one, or the first two if no items have a value)
hideEmptyItems() {
let numberOfVisibleEmptyItems = 0;
let numberOfFilledInItems = 0;
this.$allItems.each((_index, item) => {
const $item = $(item);
const $input = $item.find('.' + ListInput.itemInputClass);
const $removeButton = $item.find('.' + ListInput.removeButtonClass);
if (!$input.attr('value')) {
if (numberOfVisibleEmptyItems === 0) {
$removeButton.removeClass(ListInput.hiddenRemoveButtonClass);
}
else if (numberOfVisibleEmptyItems === 1 && numberOfFilledInItems === 0) {
$removeButton.removeClass(ListInput.hiddenRemoveButtonClass);
}
else {
$item.addClass(ListInput.hiddenItemClass);
$item.removeClass(ListInput.visibleItemClass);
$removeButton.addClass(ListInput.hiddenRemoveButtonClass);
$input.attr('disabled', 'true');
}
numberOfVisibleEmptyItems += 1;
}
else {
$item.removeClass(ListInput.hiddenItemClass);
$item.addClass(ListInput.visibleItemClass);
$removeButton.removeClass(ListInput.hiddenRemoveButtonClass);
numberOfFilledInItems += 1;
}
});
this.$allVisibleItems = this.findVisibleItems();
}
// Adds an event listener to module to listen for any
// click events fired by the items "Remove" button
addRemoveClickEvent() {
this.$allItems.each((_index, item) => {
const $item = $(item);
const $input = $item.find('.' + ListInput.itemInputClass);
const $removeButton = $item.find('.' + ListInput.removeButtonClass);
$removeButton.on('click', () => {
$input.val('');
$input.removeAttr('value');
$input.attr('disabled', 'true');
$removeButton.addClass(ListInput.hiddenRemoveButtonClass);
$item.addClass(ListInput.hiddenItemClass);
$item.removeClass(ListInput.visibleItemClass);
this.$allVisibleItems = this.findVisibleItems();
if ($item.hasClass(ListInput.itemErrorClass)) {
$item.removeClass(ListInput.itemErrorClass);
const $errorFormGroup = $item.find('.' + ListInput.formGroupErrorClass);
$errorFormGroup.removeClass(ListInput.formGroupErrorClass);
$errorFormGroup.remove('.' + ListInput.itemErrorMsgClass);
const $errorInput = $errorFormGroup.find('.' + ListInput.inputErrorClass);
const inputId = $errorInput.attr('id');
const inputDescribedBy = $errorInput.attr('aria-describedby');
$errorInput.attr('aria-describedby', inputDescribedBy.replace(inputId + '-error', ''));
$errorInput.removeClass(ListInput.inputErrorClass);
}
// Hide Remove buttons if there is only one item left
if (this.$allVisibleItems.length === 1) {
const $removeButtons = this.$module.find('.' + ListInput.removeButtonClass);
$removeButtons.each((_index, button) => {
$(button).addClass(ListInput.hiddenRemoveButtonClass);
});
$(this.$allVisibleItems.get(0)).find('input').trigger('focus');
}
else {
// Set focus to the next input
const $nextVisibleItem = ListInput.getSibling('next', $item, '.' + ListInput.visibleItemClass);
if ($nextVisibleItem) {
$nextVisibleItem.find('input').trigger('focus');
}
else {
const $previousVisibleItem = ListInput.getSibling('previous', $item, '.' + ListInput.visibleItemClass);
$previousVisibleItem.find('input').trigger('focus');
}
}
this.updateAllCounters();
this.toggleAddAnotherButton();
});
});
}
// Used to update each item's label and remove button hidden text
updateCounters() {
this.$allVisibleItems.each((index, item) => {
$(item).find('.' + ListInput.itemCounterClass).each((_index, counter) => {
$(counter).text(index + 1);
});
});
}
// Used to update "Add another" buttons "Remaining" counter
// and sets focus to the new additional input
updateRemainingCounter() {
const $visibleItems = this.$allVisibleItems;
const totalItems = this.$allItems.length;
this.$module.find('.' + ListInput.addButtonRemainingClass).text(totalItems - $visibleItems.length);
}
addAddClickEvent() {
this.$addAnotherButton.on('click', () => {
const $firstHiddenItem = this.$module.find('.' + ListInput.hiddenItemClass).first();
const $firstHiddenInput = $firstHiddenItem.find('.' + ListInput.itemInputClass);
if ($firstHiddenItem.length) {
$firstHiddenInput.removeAttr('disabled');
this.$module.find('.' + ListInput.itemContainerClass).append($firstHiddenItem);
$firstHiddenItem.removeClass(ListInput.hiddenItemClass);
$firstHiddenItem.addClass(ListInput.visibleItemClass);
this.$allVisibleItems = this.findVisibleItems();
this.updateAllCounters();
$firstHiddenItem.find('.' + ListInput.removeButtonClass).removeClass(ListInput.hiddenRemoveButtonClass);
$firstHiddenInput.trigger('focus');
this.toggleAddAnotherButton();
// Show Remove buttons if there is more one item
if (this.$allVisibleItems.length > 1) {
this.$module.find('.' + ListInput.removeButtonClass).each((_index, button) => {
$(button).removeClass(ListInput.hiddenRemoveButtonClass);
});
}
}
});
}
toggleAddAnotherButton() {
const $firstHiddenItem = this.$module.find('.' + ListInput.hiddenItemClass);
if ($firstHiddenItem.length) {
this.$addAnotherButton.removeClass(ListInput.hiddenAddButtonClass);
}
else {
this.$addAnotherButton.addClass(ListInput.hiddenAddButtonClass);
}
}
updateAllCounters() {
this.updateCounters();
this.updateRemainingCounter();
}
static getSibling(direction, elem, selector) {
// Get the next sibling element
let sibling = (direction === 'next') ? elem.next() : elem.prev();
// If there's no selector, return the first sibling
if (!selector)
return sibling;
// If the sibling matches our selector, use it
// If not, jump to the next sibling and continue the loop
while (sibling.length) {
if (sibling.is(selector))
return sibling;
sibling = (direction === 'next') ? sibling.next() : sibling.prev();
}
}
}
export { ListInput };
//# sourceMappingURL=list-input.mjs.map