import { LightningElement, api } from "lwc"; import { LightningSlotElement, Tab } from "typings/custom"; import { toJson } from "dxUtils/normalizers"; import { uniqueId } from "./uniqueId"; export default class TabPanelList extends LightningElement { @api get initialIndex() { return +this._initialIndex; } set initialIndex(value) { this._initialIndex = +value; } @api get tabs() { return this._tabs.map((tab: Tab, index) => ({ ...tab, index, groupId: this.groupId, active: this.activeIndex === index })); } set tabs(value: Tab[]) { this._tabs = toJson(value); if (typeof this._activeIndex === "number") { this._activeIndex = undefined; this.updateTabs(); } } private _initialIndex: number = 0; private _tabs: Tab[] = []; private _activeIndex?: number; private groupId: string = uniqueId(); private tabSlotPanels?: LightningSlotElement; private showPopover: boolean = true; private dropdownItems: Tab[] = []; private hasRendered: boolean = false; private get tabElements() { return this.template.querySelectorAll("dx-tab-panel-item"); } private get activeIndex() { return this._activeIndex == null ? this.initialIndex : this._activeIndex; } private set activeIndex(value: number) { this._activeIndex = value; this.updateTabs(); } connectedCallback() { this.addListeners(); } renderedCallback(): void { if (!this.hasRendered) { this.onResize(); this.hasRendered = true; } } disconnectedCallback() { this.removeListeners(); } private onTabClick(e: { detail: number }) { if (e.detail !== this.activeIndex) { this.dispatchEvent(new CustomEvent("change", { detail: e.detail })); } this.activeIndex = e.detail; } private onDropdownItemClick(e: any) { this.onTabClick({ detail: +e.currentTarget.dataset.value }); } private onSlotChange(e: Event) { this.tabSlotPanels = e.target; this.updateTabs(); } private updateTabs() { if (this.tabSlotPanels) { this.tabSlotPanels .assignedElements() .forEach((tabPanel: any, index) => { tabPanel.index = index; tabPanel.groupId = this.groupId; tabPanel.active = this.activeIndex === index; }); } this.tabElements.forEach((tab: any, index) => { tab.active = this.activeIndex === index; }); this.dispatchEvent( new CustomEvent("tabchanged", { detail: this.id || this.groupId, bubbles: true, composed: true }) ); } private focusActiveTab() { this.tabElements[this.activeIndex].focus(); } private onKeydown = (e: KeyboardEvent) => { switch (e.key) { case "ArrowLeft": { e.preventDefault(); const prev = this.activeIndex - 1; this.activeIndex = prev < 0 ? this.tabs.length - 1 : prev; this.focusActiveTab(); break; } case "ArrowRight": { e.preventDefault(); const next = this.activeIndex + 1; this.activeIndex = next === this.tabs.length ? 0 : next; this.focusActiveTab(); break; } default: } }; private onResize = () => { const allItems = this.template.querySelectorAll("dx-tab-panel-item"); const hiddenItems: number[] = []; const primaryItems = this.template.querySelectorAll( "dx-tab-panel-item:not(.more-btn-container)" ); const moreBtn = this.template.querySelector(".more-btn-container")!; const containerWidth = this.template.querySelector(".tablist")!.offsetWidth; let stopWidth = this.template.querySelector( "dx-tab-panel-item.more-btn-container" )!.offsetWidth; this.dropdownItems = []; allItems.forEach((item) => { item.classList.remove("hidden"); }); primaryItems.forEach((item, i) => { if (containerWidth >= stopWidth + item.offsetWidth) { stopWidth += item.offsetWidth; } else { item.classList.add("hidden"); hiddenItems.push(i); } }); if (!hiddenItems.length) { moreBtn.classList.add("hidden"); } else { hiddenItems.forEach((i) => { this.dropdownItems.push({ ...this._tabs[i], index: i }); }); } }; private addListeners() { this.template.addEventListener("keydown", this.onKeydown as any); window.addEventListener("resize", this.onResize.bind(this)); } private removeListeners() { this.template.removeEventListener("keydown", this.onKeydown as any); window.removeEventListener("resize", this.onResize.bind(this)); } }