import type { PropertyValues } from 'lit' import { LitElement, css, html } from 'lit' import { property, query } from 'lit/decorators.js' import { classMap } from 'lit/directives/class-map.js' import type { Options } from 'simplebar' import SimpleBar from 'simplebar' import simpleBar_css from 'simplebar/dist/simplebar.css' import type { DirectiveClass, DirectiveResult } from 'lit/directive' import { customElementIfNotExist } from '../../utils/customElementIfNotExist' import { dispatchCustomEvent } from '../../utils/dispatchCustomEvent' import { debounce } from '../../utils/debounce' import styles_css from './styles.pcss' import vars_css from './vars.pcss' declare global { interface HTMLElementTagNameMap { 'us-scroll-layout': ScrollLayout } } @customElementIfNotExist('us-scroll-layout') export class ScrollLayout extends LitElement { static styles = [ css([vars_css] as TemplateStringsArray | any), css([styles_css] as TemplateStringsArray | any), css([simpleBar_css] as TemplateStringsArray | any), ] @property() ariaLabel = '' @property({ type: Object }) options = {} as Options @property({ type: Boolean }) innactive = false @property({ type: Boolean }) observeEvent = false @property({ type: Boolean }) fullHeight = false @query('[data-simplebar]') simpleBarEl!: HTMLDivElement simpleBar!: SimpleBar handleSimpleBarScrollEventBinded = this.handleSimpleBarScrollEvent.bind(this) handleScrollEventDebounced = debounce(() => dispatchCustomEvent('handleScrollEvent', null, this, false, false), 100, false) firstUpdated() { this.initSimpleBar() } async updated(changedProperties: PropertyValues) { if (changedProperties.has('innactive')) { if (this.innactive) this.disposeSimpleBar() else await this.reInitSimpleBar() } } disconnectedCallback() { this.disposeSimpleBar() super.disconnectedCallback() } render() { if (this.innactive) return html`` return html`
` } getLayoutClasses(): DirectiveResult { return classMap({ 'scroll-layout': true, 'scroll-layout_full-height': this.fullHeight, }) } initSimpleBar() { const ariaLabel = this.ariaLabel || 'simplebar-el' const options = { ...this.options, ariaLabel } if (this.simpleBarEl) { this.simpleBar = new SimpleBar(this.simpleBarEl, options as Options) this.observeEvent && this.subscribeSimpleBarEvent() } } async reInitSimpleBar() { await this.updateComplete this.disposeSimpleBar() this.initSimpleBar() } disposeSimpleBar() { this.observeEvent && this.unsubscribeSimpleBarEvent() this.simpleBar && this.simpleBar.unMount() } async handleSimpleBarScrollEvent(e: Event) { const { scrollHeight, scrollTop, clientHeight } = e.target as HTMLDivElement const remainingScroll = Number((scrollHeight - (scrollTop + clientHeight)).toFixed(2)) if (remainingScroll <= 1) this.handleScrollEventDebounced() } subscribeSimpleBarEvent() { const scrollEl = this.simpleBar && this.simpleBar.getScrollElement() scrollEl && scrollEl.addEventListener('scroll', this.handleSimpleBarScrollEventBinded) } unsubscribeSimpleBarEvent() { const scrollEl = this.simpleBar && this.simpleBar.getScrollElement() scrollEl && scrollEl.removeEventListener('scroll', this.handleSimpleBarScrollEventBinded) } }