@financial-times/o-multi-select
Version:
Multi select component
146 lines (135 loc) • 5.03 kB
JavaScript
/**
* adds or removes the selection of a multi-select option in selected list.
*
* @param {HTMLElement} optionEl - The option element that was selected.
* @param {{ label: string, value: string }} option - The text content and value of the option.
* @param {number} index - The index of the option that was selected.
* @returns {void}
*/
export function handleOptionSelect(optionEl, option, index) {
const alreadySelected = optionEl.classList.contains(
'o-multi-select-option__selected'
);
if (alreadySelected) {
removeOption.call(this, optionEl, option, index);
} else {
addSelectedOption.call(this, optionEl, option, index);
}
handleCustomEvent.call(this, optionEl, option, !alreadySelected, index);
this._activeIndex = index;
this._updateCurrentElement();
const coreOption = this._coreOptions[index];
if (coreOption.selected) {
coreOption.removeAttribute('selected');
} else {
coreOption.setAttribute('selected', '');
}
}
/**
* Dispatches custom event with important details.
*
* @param {HTMLElement} optionEl - The option element that was selected.
* @param {{ label: string, value: string }} option - The text content and value of the option.
* @param {boolean} selected - Determines if the element is selected or not.
* @param {number} index - The index of the option that was selected.
* @returns {void}
*/
function handleCustomEvent(optionEl, option, selected, index) {
const optionClickEvent = new CustomEvent('oMultiSelect.OptionChange', {
bubbles: true,
detail: {
optionElement: optionEl,
value: option.value,
selected: selected,
index,
instance: this,
},
});
this.multiSelectEl.dispatchEvent(optionClickEvent);
}
/**
* Removes selected item from a multi-select selected list.
*
* @private
* @param {HTMLElement} optionEl - The option element to remove.
* @param {{ label: string, value: string }} option - The text content and value of the option.
* @param {number} index - The index of the option to remove.
* @returns {void}
*/
function removeOption(optionEl, option, index) {
optionEl.classList.remove('o-multi-select-option__selected');
optionEl.setAttribute('aria-selected', 'false');
this._numberOfSelectedOptions--;
const button = this._selectedOptions.querySelector(
`#${option.value}-${index}`
);
button.parentElement.remove();
this._updateState();
}
/**
* adds a item in a multi-select selected list.
*
* @private
* @param {HTMLElement} optionEl - The option element to add.
* @param {{ label: string, value: string }} option - The text content and value of the option.
* @param {number} index - The index of the option to add.
* @returns {void}
*/
export function addSelectedOption(optionEl, option, index) {
this._numberOfSelectedOptions++;
optionEl.classList.add('o-multi-select-option__selected');
optionEl.setAttribute('aria-selected', 'true');
const {li, button} = createOptionButton(option, index);
this._selectedOptions.appendChild(li);
this._updateState();
button.addEventListener('click', () => {
li.remove();
optionEl.classList.remove('o-multi-select-option__selected');
this._numberOfSelectedOptions--;
this._updateState();
handleCustomEvent.call(this, optionEl, option, false, index);
});
}
/**
* Creates a button for a multi-select option.
*
* @private
* @param {{ label: string, value: string }} option - The text content and value of the option.
* @param {number} index - The index of the option.
* @returns {{ li: HTMLElement, button: HTMLElement }} An object containing the newly created <li> and <button> elements.
*/
function createOptionButton(option, index) {
const li = document.createElement('li');
const button = document.createElement('button');
button.id = `${option.value}-${index}`;
button.setAttribute('aria-label', ` remove ${option.label} `);
button.className = 'o-multi-select__selected-options-button';
button.type = 'button';
button.textContent = option.label.trim();
const span = document.createElement('span');
span.classList = 'o-icons-icon o-icons-icon--cross';
button.appendChild(span);
li.appendChild(button);
return {li, button};
}
/**
* Creates an option element for a multi-select.
*
* @param {string} idBase - The base ID to use for the option element.
* @param {{ label: string, value: string }} option - The text content and value of the option.
* @param {number} index - The index of the option.
* @param {boolean} [selected=false] - Whether the option should be selected.
* @returns {HTMLElement} The newly created option element.
*/
export function createOption(idBase, option, index, selected) {
const optionEl = document.createElement('div');
optionEl.setAttribute('role', 'option');
optionEl.id = `${idBase}-${index}`;
optionEl.className = 'o-multi-select-option';
optionEl.setAttribute('aria-selected', selected);
optionEl.textContent = option.label;
const tickSpan = document.createElement('span');
tickSpan.className = 'o-multi-select-option-tick';
optionEl.appendChild(tickSpan);
return optionEl;
}