import type { PropertyValues } from 'lit' import { css, html } from 'lit' import { property, query, state } from 'lit/decorators.js' import type { DirectiveClass, DirectiveResult } from 'lit/directive.js' import { classMap } from 'lit/directives/class-map.js' import { repeat } from 'lit/directives/repeat.js' import { when } from 'lit/directives/when.js' import SimpleBar from 'simplebar' import simplebar_css from 'simplebar/dist/simplebar.css' import { customElementIfNotExist } from '../../utils/customElementIfNotExist' import { dispatchCustomEvent } from '../../utils/dispatchCustomEvent' import { UsBaseElement } from '../base/UsBaseElement' import type { ISelectItem } from '../form/select/model/ISelectItem' import { toggleImageDarkTheme } from '../../utils/toggleImageDarkTheme' import UsList_css from './list.pcss' import type { IListItemProps } from './model/IListItemProps' import type { IListProps } from './model/IListProps' // import '../icon/UsIcon' import '../form/checkbox/UsCheckbox' declare global { interface HTMLElementTagNameMap { 'us-list': UsList } } @customElementIfNotExist('us-list') export class UsList extends UsBaseElement implements IListProps { static styles = [ UsBaseElement.styles, css([UsList_css] as TemplateStringsArray | any), css([simplebar_css] as TemplateStringsArray | any), ] @property({ type: Boolean }) showList = false @property({ type: Boolean }) multiple = false @property({ type: Array }) list: IListItemProps[] | any[] = [] @property({ type: Object }) selectedValue: IListItemProps | ISelectItem | null = null @property({ type: Boolean }) required!: boolean @property({ type: String }) ariaLabel = 'simplebar-aria-label' @property({ type: Boolean }) top = false @property({ type: String }) horizontalAlign: IListProps['horizontalAlign'] = 'right' @property({ type: String }) maxHeight = '235' @property({ type: String, attribute: 'data-testid', reflect: true }) dataTestId = 'list' @property({ type: String }) mainCheckBoxName = '' @property({ type: Boolean }) isDarkTheme = false @state() currentValueIndex: number | null = null @state() activeIndex = -1 @query('[data-simplebar]') simpleBarEl: HTMLDivElement | undefined @query('ul') listElement!: HTMLUListElement simpleBar!: SimpleBar firstUpdated(_changedProperties: PropertyValues) { this.initSimpleBar() super.firstUpdated(_changedProperties) } willUpdate(changedProperties: PropertyValues) { if (changedProperties.has('list') && !this.multiple) this.activeIndex = -1 this.currentValueIndex = this.list.findIndex( item => item === this.selectedValue || this.selectedValue?.value === item?.value, ) } disconnectedCallback(): void { this.disposeSimpleBar() super.disconnectedCallback() } render() { return html`
` } initSimpleBar() { if (this.simpleBarEl) this.simpleBar = new SimpleBar(this.simpleBarEl, { ariaLabel: this.ariaLabel } as any) } disposeSimpleBar() { this.simpleBar && this.simpleBar.unMount() } amountOfCheckedItems() { return this.list.filter(item => item.checked && item.value !== 'mainCheckBox').length } toggleMainCheckbox() { dispatchCustomEvent('toggleMainCheckbox', null, this) } selectListItem(item: IListItemProps, index: number) { if (this.multiple) item.checked = !item.checked const isItemSelected = this.currentValueIndex === index const shouldSelectItem = isItemSelected ? !this.required : true this.currentValueIndex = shouldSelectItem ? index : null if (item?.value === 'mainCheckBox') this.toggleMainCheckbox() else dispatchCustomEvent('clickListItem', item, this, false) } eventHandle(e: Event, item: any, index: number) { e.stopPropagation() if (item.value === 'mainCheckBox') this.toggleMainCheckbox() else this.selectListItem(item, index) } handleKeyDown(event: KeyboardEvent) { switch (event.key) { case 'ArrowUp': event.preventDefault() this.activeIndex = this.activeIndex === 0 ? this.list.length - 1 : this.activeIndex - 1 break case 'ArrowDown': event.preventDefault() this.activeIndex = this.activeIndex === this.list.length - 1 ? 0 : this.activeIndex + 1 break case 'Enter': if (this.list.length === 1) { this.activeIndex = 0 if (this.list[0]?.disabled) return } this.selectListItem(this.list[this.activeIndex], this.activeIndex) break case 'Escape': case 'Tab': this.showList = false dispatchCustomEvent('blurElement', null, this) break } setTimeout(() => { const liElement = this.listElement.querySelector('li[tabindex="0"]') as HTMLElement liElement?.focus() }, 0) } getListItemClasses(listItem: IListItemProps, index: number): DirectiveResult { return classMap({ 'list-item_disabled': !!listItem?.disabled, 'list-item__main-checkbox': listItem.value === 'mainCheckBox', 'list-item_active': index === this.currentValueIndex && !!this.selectedValue, }) } getListClasses(): DirectiveResult { return classMap({ list_display_none: !this.showList, top: this.top, [this.horizontalAlign]: this.horizontalAlign, }) } getListLabelClasses(icon: string, image: string): DirectiveResult { return classMap({ 'list-item__label': true, 'list-item__label-with-img': image, 'reduce-left-padding-when-icon-exist': icon, }) } }