ui-lit
Version:
UI Elements on LIT
388 lines (387 loc) • 13.2 kB
JavaScript
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}"
= "${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}"
= "${this._onSeatchClick}"
= "${this._onSearchValue}"
placeholder = "${this.multiple ? "" : this.searchPlaceholder}"
type = "text" /></div>`;
}
_containerTemplate(data) {
if (this.multiple) {
return html `
<div class = "wrapper"
tabindex = "0"
= "${this._handlekeyDown}"
= "${this._toggle}">
${data}
</div>`;
}
return html `<lit-button
= "${this._handlekeyDown}"
= "${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}"
= "${this._focusNext}"
= "${this._focusPrev}"
= "${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 };