UNPKG

ui-lit

Version:

UI Elements on LIT

388 lines (387 loc) 13.2 kB
import { __decorate } from "tslib"; import { search } from './../svgIcons/index'; import { dropdown, cancel } from '../svgIcons'; import { html, LitElement, nothing } from 'lit'; import { formAssociated } from '../mixins/form-associated/index'; import { customElement, property } from 'lit/decorators.js'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; import '../button'; import './listbox'; import './group'; import './option'; import { focusable } from '../mixins/focusable/index'; import { labled } from '../mixins/labled/index'; import { notificatable } from '../mixins/notificatable/index'; import { selectStyles } from './styles'; import { getEventDataset } from 'kailib'; export * from './option'; let LitSelect = class LitSelect extends focusable(labled(notificatable(formAssociated(LitElement)))) { constructor() { super(...arguments); this.tabindex = 0; this.multiple = false; this.searchable = false; this.searchPlaceholder = 'search'; this.listboxPosition = 'auto'; this._optionMap = new Set(); this._connectedTime = 0; this.isMenu = false; this._open = false; this._searchValue = ''; this._onOptionChange = (e) => { const option = e.detail; if (!this.multiple && option.selected) { return; } this.selectOption(option); if (!this.multiple) { this.hide(); } }; this._onOptionSlotChanged = () => { this.requestUpdate(); }; this._onOptionConnect = (e) => { const option = e.detail; this._optionMap.add(option); option.setSelectHost(this); this.requestUpdate(); }; this.optionDisconnect = (option) => { this._optionMap.delete(option); this.requestUpdate(); }; this._getSlotElements = (slot = this.shadowRoot.querySelector(`.slot`)) => { const elements = slot === null || slot === void 0 ? void 0 : slot.assignedElements(); if (elements[0] instanceof HTMLSlotElement) { return this._getSlotElements(elements[0]); } return elements.reduce((acc, v) => { if ([`lit-menu-item`, `lit-option`].includes(v.tagName.toLowerCase())) { acc.push(v); } else { v.querySelectorAll('lit-menu-item, lit-option') .forEach(n => acc.push(n)); } return acc; }, []); }; this._handlekeyDown = (e) => { var _a; if (e.key === "Enter" || e.key === " ") { this._toggle(); } if (e.key === "ArrowDown") { if (this.isMenu) { const d = this._getSlotElements(); (_a = d[this.selectedIndex + 1]) === null || _a === void 0 ? void 0 : _a.focus(); } else { this.selectedIndex = this.selectedIndex + 1; } e.preventDefault(); } if (e.key === "ArrowUp") { this.selectedIndex = this.selectedIndex - 1; e.preventDefault(); } if (e.key === "Escape") { this.hide(); } if (e.key === "Tab") { if (this.open) { this.hide(); e.preventDefault(); } } }; } static get styles() { return [...super.elementStyles, selectStyles]; } ; static get properties() { return { open: { type: Boolean } }; } set open(value) { if (value === this._open) { return; } const oldValue = this._open; this._open = value; if (value) { this.searchValue = ''; } this.requestUpdate('open', oldValue); } get open() { return this._open; } get value() { var _a; return (_a = this.selectedOptions[0]) === null || _a === void 0 ? void 0 : _a.value; } set value(value) { const option = this._optionByValue(value); if (!option) { //Make possible to set value to lit-select on init if ((Date.now() - this._connectedTime < 20) || !this._connectedTime) { this._connectedTime = Date.now(); setTimeout(() => { this.value = value; }, 20); } else { console.warn("No option with value:", value); } return; } this.selectOption(option); } get searchValue() { return this._searchValue; } set searchValue(value) { this._searchValue = value; this.options.forEach(it => { it.visability = it.innerText.toLocaleLowerCase().includes(value.toLocaleLowerCase()); }); this.requestUpdate(); } get length() { return this.options.length; } get options() { return [...this._optionMap.values()]; } get sortedOptions() { return [...this.querySelectorAll('lit-option')]; } set selectedIndex(value) { if (value > this.length - 1 || value < 0) return; const option = this.sortedOptions[value]; if (option) { this.selectOption(option); } } get selectedIndex() { const options = this.sortedOptions; for (let i = 0; i < this.length; i++) { if (options[i].selected) return i; } return -1; } get selectedOptions() { return this.options.filter(item => item.selected); } get selectedValues() { return this.selectedOptions.map(item => item.value); } get selectedContent() { var _a; return ((_a = this.selectedOptions[0]) === null || _a === void 0 ? void 0 : _a.innerHTML) || '-'; } _optionByValue(value) { return this.options.find(item => item.value === value); } _selectValue(value) { const option = this._optionByValue(value); if (!option) { console.warn("No option with value:", value); return; } this.selectOption(option); } _unSelectValue(value) { const option = this._optionByValue(value); if (!option) { console.warn("No option with value:", value); return; } this.unSelectOption(option); } selectOption(option) { if (this.multiple) { option.selected = !option.selected; } else if (option.selected) { return; } else { this.selectedOptions.forEach(option => option.selected = false); option.selected = true; } this.requestUpdate(); this.notify(); setTimeout(() => this.focus(), 20); } unSelectOption(option) { option.selected = false; this.requestUpdate(); } willUpdate(_changedProperties) { if (!this._optionMap.size) { this.setAttribute("empty", ""); } else { this.removeAttribute("empty"); } } connectedCallback() { super.connectedCallback(); this.addEventListener('optionConnected', this._onOptionConnect); this.addEventListener('optionSlotChanged', this._onOptionSlotChanged); this.addEventListener('optionChange', this._onOptionChange); } disconnectedCallback() { super.disconnectedCallback(); this.removeEventListener('optionConnected', this._onOptionConnect); this.removeEventListener('optionSlotChanged', this._onOptionSlotChanged); this.removeEventListener('optionChange', this._onOptionChange); } _toggle() { var _a, _b; if (this.disabled) return; if (this.open) { this.hide(); } else { this.show(); if (this.multiple) { (_b = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector("input")) === null || _b === void 0 ? void 0 : _b.focus(); } } } hide() { if (!this.open) return; this.open = false; } show() { if (this.open) return; this.open = true; } _clickCancel(e) { const v = getEventDataset(e, '.cancel', 'value'); this._unSelectValue(v); this.notify(); e.stopPropagation(); } _onSearchValue(e) { const value = e.target.value; this.searchValue = value; this.style.setProperty("--input-width", this.searchValue.length * 8 + "px"); } _onSeatchClick(e) { e.stopPropagation(); } _contentTemplate() { var _a; if (this.multiple) { return this.selectedOptions.map(option => { if (!option) return ""; return html `<button class = "button" ?disabled = "${option.disabled}"> ${unsafeHTML(option.innerHTML)} <span class = "cancel" data-value = "${option.value}" @click = "${this._clickCancel}" > ${cancel()} </span> </button>`; }); } return html `<slot name = "selected">${unsafeHTML(((_a = this.selectedOptions[0]) === null || _a === void 0 ? void 0 : _a.innerHTML) || '-')}</slot>`; } _searchTamplate() { if (!this.searchable) return nothing; return html `<div class = "search-wrapper"><input .value = "${this.searchValue}" @click = "${this._onSeatchClick}" @input = "${this._onSearchValue}" placeholder = "${this.multiple ? "" : this.searchPlaceholder}" type = "text" /></div>`; } _containerTemplate(data) { if (this.multiple) { return html ` <div class = "wrapper" tabindex = "0" @keydown = "${this._handlekeyDown}" @click = "${this._toggle}"> ${data} </div>`; } return html `<lit-button @keydown = "${this._handlekeyDown}" @click = "${this._toggle}"> <div class = "wrapper">${data}</div> </lit-button>`; } _wrapperTemplate() { return this._containerTemplate(html ` ${this._contentTemplate()} ${this.multiple ? this._searchTamplate() : nothing} <div class = "icon-wrapper"> ${this.multiple && this.searchable && this.open ? search() : dropdown(this.open)} </div>`); } render() { return html ` ${super.render()} ${this._wrapperTemplate()} <lit-listbox .position = "${this.listboxPosition}" @focusNext = "${this._focusNext}" @focusPrev = "${this._focusPrev}" @listboxClose = "${this.hide}" .open = "${this.open}"> ${this.multiple ? nothing : this._searchTamplate()} <slot></slot> </lit-listbox>`; } _focusNext(e) { var _a, _b; (_b = (_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.nextElementSibling) === null || _b === void 0 ? void 0 : _b.focus(); } _focusPrev(e) { var _a, _b; (_b = (_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.previousElementSibling) === null || _b === void 0 ? void 0 : _b.focus(); } notify() { this.dispatchEvent(new CustomEvent('changed', { detail: this.value, bubbles: true, composed: true })); } }; __decorate([ property({ type: Number }) ], LitSelect.prototype, "tabindex", void 0); __decorate([ property({ type: Boolean, reflect: true }) ], LitSelect.prototype, "multiple", void 0); __decorate([ property({ type: Boolean, reflect: true }) ], LitSelect.prototype, "searchable", void 0); __decorate([ property({ type: String, }) ], LitSelect.prototype, "searchPlaceholder", void 0); __decorate([ property({ type: String }) ], LitSelect.prototype, "listboxPosition", void 0); LitSelect = __decorate([ customElement("lit-select") ], LitSelect); export { LitSelect };