/** * @summary aofl-multiselect-list * @version 3.0.0 * @since 3.0.0 * * @author Daniel Belisle<daniel.belisle@aofl.com> * @author Arian Khosravi<arian.khosravi@aofl.com> */ import {AoflElement} from '@aofl/element'; import styles from './multi-select-list'; /** * Similar to select multi * * @memberof module:@aofl/select * @extends {AoflElement} */ class AoflMultiselectList extends AoflElement { /** * @readonly * @type {String} */ static get is() { return 'aofl-multiselect-list'; } /** * @readonly * @type {Object} */ static get properties() { return { options: {type: Array, attribute: false}, selected: {type: Array, attribute: false} }; } /** * */ constructor() { super(); this.options = []; this.selected = []; this.focusIndex = 0; } /** * */ connectedCallback() { super.connectedCallback(); this.addEventListener('keydown', this.keydownCallback); this.addEventListener('focusout', this.focusoutCallback); this.addEventListener('mouseover', this.mouseoverCallback); } /** * @return {Object} */ render() { return super.render((ctx, html) => html`<slot></slot>`, [styles]); } /** * If the element losing focus is not a child of the list reset the focusIndex * * @param {Event} e */ /* istanbul ignore next */ focusoutCallback(e) { if (e.relatedTarget && e.relatedTarget.parentNode === this) { return; } this.focusIndex = 0; } /** * Handle the tab and arrow focus logic within the list * * @param {Event} e */ keydownCallback(e) { e.preventDefault(); if (e.keyCode === 38 || (e.shiftKey && e.keyCode === 9)) { // up arrow or shift tab if (this.focusIndex > 0) { this.options[--this.focusIndex].focus(); } } else if (e.keyCode === 40 || e.keyCode === 9) { // down arrow or tab if (this.focusIndex < this.options.length - 1) { this.options[++this.focusIndex].focus(); } } } /** * Focus list options that are hovered over * * @param {Event} e */ mouseoverCallback(e) { const index = this.options.indexOf(e.target); if (index > -1) { this.focusIndex = index; } } /** * Toggle selected on list element and dispatch custom change event * * @param {Boolean} dispatch * @fires AoflMultiselectList.change */ updateSelected(option, dispatch = true) { const selected = []; for (let i = 0; i < this.options.length; i++) { if (this.options[i] === option) { if (option.selected) { option.selected = false; } else { option.selected = true; if (selected.indexOf(option.value) === -1) { selected.push(option.value); } } } else { if (this.options[i].selected && selected.indexOf(this.options[i].value) === -1) { selected.push(this.options[i].value); } } } this.selected = selected; if (dispatch) { this.dispatchEvent(new CustomEvent('change', { composed: true, bubbles: true })); } } /** * Remove all items from selected array and remove selected attributes from all list options * */ clearSelected() { this.selected.length = 0; for (let i = 0; i < this.options.length; i++) { this.options[i].removeAttribute('selected'); } this.dispatchEvent(new CustomEvent('change')); } /** * Add an option to be selected * * @param {String} option */ addOption(option) { this.options.push(option); if (option.selected && this.selected.indexOf(option.value) === -1) { this.selected.push(option.value); } } /** * */ disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('keydown', this.keydownCallback); this.removeEventListener('focusout', this.focusoutCallback); this.removeEventListener('mouseover', this.mouseoverCallback); } } if (window.customElements.get(AoflMultiselectList.is) === void 0) { window.customElements.define(AoflMultiselectList.is, AoflMultiselectList); } export default AoflMultiselectList;