UNPKG

@scania/tegel

Version:
379 lines (378 loc) 14.7 kB
import { Host, h, } from "@stencil/core"; /** * @slot <default> - <b>Unnamed slot.</b> For the tab elements. */ export class TdsNavigationTabs { constructor() { this.navWrapperElement = null; // reference to container with nav buttons this.componentWidth = 0; // visible width of this component this.buttonsWidth = 0; // total width of all nav items combined this.scrollWidth = 0; // total amount that is possible to scroll in the nav wrapper this.clickHandlers = new WeakMap(); this.addResizeObserver = () => { const resizeObserver = new ResizeObserver((entries) => { entries.forEach((entry) => { const componentWidth = entry.contentRect.width; let buttonsWidth = 0; const navButtons = Array.from(this.host.children); navButtons.forEach((navButton) => { const style = window.getComputedStyle(navButton); buttonsWidth += navButton.clientWidth + parseFloat(style.marginLeft) + parseFloat(style.marginRight); }); this.componentWidth = componentWidth; this.buttonsWidth = buttonsWidth; this.scrollWidth = buttonsWidth - componentWidth; this.updateScrollButtons(); }); }); resizeObserver.observe(this.navWrapperElement); }; this.addEventListenerToTabs = () => { this.children = Array.from(this.host.children); this.children.map((item, index) => { const clickHandler = () => { if (!item.disabled) { const tdsChangeEvent = this.tdsChange.emit({ selectedTabIndex: this.children.indexOf(item), }); if (!tdsChangeEvent.defaultPrevented) { this.children.forEach((element) => element.setSelected(false)); item.setSelected(true); this.selectedIndex = index; } } }; item.addEventListener('click', clickHandler); this.clickHandlers.set(item, clickHandler); // Store the handler in WeakMap return item; }); }; this.removeEventListenerFromTabs = () => { this.children.forEach((item) => { const clickHandler = this.clickHandlers.get(item); if (clickHandler) { item.removeEventListener('click', clickHandler); this.clickHandlers.delete(item); } }); }; this.modeVariant = 'primary'; this.defaultSelectedIndex = 0; this.selectedIndex = undefined; this.leftPadding = 32; this.tdsScrollLeftAriaLabel = 'Scroll left'; this.tdsScrollRightAriaLabel = 'Scroll right'; this.showLeftScroll = false; this.showRightScroll = false; } /** Sets the passed tabindex as the selected Tab. */ async selectTab(tabIndex) { if (!this.children[tabIndex].disabled) { this.children.forEach((element) => element.setSelected(false)); this.children = this.children.map((element, index) => { if (index === tabIndex) { element.setSelected(true); this.selectedIndex = tabIndex; } return element; }); } return { selectedTabIndex: this.selectedIndex, }; } /** Reinitializes the component. */ async reinitialize() { this.handleSlotChange(); } handleSelectedIndexUpdate() { this.children = Array.from(this.host.children).map((tabElement) => { tabElement.setSelected(false); return tabElement; }); this.children[this.selectedIndex].setSelected(true); } scrollRight() { const scroll = this.navWrapperElement.scrollLeft; this.navWrapperElement.scrollLeft = scroll + this.buttonsWidth; this.evaluateScrollButtons(); } scrollLeft() { const scroll = this.navWrapperElement.scrollLeft; this.navWrapperElement.scrollLeft = scroll - this.buttonsWidth; this.evaluateScrollButtons(); } evaluateScrollButtons() { const scroll = this.navWrapperElement.scrollLeft; this.showRightScroll = scroll <= this.scrollWidth; this.showLeftScroll = scroll > 0; } initializeTabs() { this.children = Array.from(this.host.children); // remove first and last class from other tabs in case of initialization this.children.forEach((child) => { child.classList.remove('last'); child.classList.remove('first'); }); if (this.children.length > 0) { this.children[0].classList.add('first'); this.children[this.children.length - 1].classList.add('last'); } } initializeSelectedTab() { if (this.selectedIndex === undefined) { this.addEventListenerToTabs(); this.children[this.defaultSelectedIndex].setSelected(true); this.selectedIndex = this.defaultSelectedIndex; } else { this.children[this.selectedIndex].setSelected(true); } this.tdsChange.emit({ selectedTabIndex: this.selectedIndex, }); } updateScrollButtons() { if (this.buttonsWidth > this.componentWidth) { this.evaluateScrollButtons(); } else { this.showLeftScroll = false; this.showRightScroll = false; } } applyCustomLeftPadding() { if (this.navWrapperElement) { this.navWrapperElement.style.paddingLeft = `${this.leftPadding}px`; } } handleSlotChange() { this.initializeTabs(); this.addEventListenerToTabs(); this.initializeSelectedTab(); this.updateScrollButtons(); this.addResizeObserver(); this.applyCustomLeftPadding(); // Apply custom left padding to the wrapper } connectedCallback() { this.initializeTabs(); } componentDidLoad() { this.initializeSelectedTab(); } componentDidRender() { this.updateScrollButtons(); this.addResizeObserver(); } disconnectedCallback() { this.removeEventListenerFromTabs(); } render() { return (h(Host, { key: '2fc278cacc04614ca43b70f52b12f79b31227fbb', role: "tablist", class: { [`tds-mode-variant-${this.modeVariant}`]: this.modeVariant !== null } }, h("div", { key: '50357810903ea3bff83fde4478960de0866c7c15', class: "wrapper", ref: (el) => { this.navWrapperElement = el; }, style: { paddingLeft: `${this.leftPadding}px` } }, h("button", { key: 'b5b05a2f9bce1618c71594cf388dc83efce98915', "aria-label": this.tdsScrollLeftAriaLabel, class: { 'scroll-left-button': true, 'show': this.showLeftScroll, }, onClick: () => this.scrollLeft(), disabled: !this.showLeftScroll }, h("tds-icon", { key: 'aad19852d36ff8e1642ba464f9618a57ff37169c', name: "chevron_left", size: "20px" })), h("slot", { key: '3dcf0d7e6900df33a69863868a3a48dcb4aa8141', onSlotchange: () => this.handleSlotChange() }), h("button", { key: '352539955226aecf92deb0c0db83c42a5b021e43', "aria-label": this.tdsScrollRightAriaLabel, class: { 'scroll-right-button': true, 'show': this.showRightScroll, }, onClick: () => this.scrollRight(), disabled: !this.showRightScroll }, h("tds-icon", { key: '0e3fe531374dd82b6ee5de1a5a1bea4a38e03500', name: "chevron_right", size: "20px" }))))); } static get is() { return "tds-navigation-tabs"; } static get encapsulation() { return "shadow"; } static get originalStyleUrls() { return { "$": ["navigation-tabs.scss"] }; } static get styleUrls() { return { "$": ["navigation-tabs.css"] }; } static get properties() { return { "modeVariant": { "type": "string", "mutable": false, "complexType": { "original": "'primary' | 'secondary'", "resolved": "\"primary\" | \"secondary\"", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Variant of the Tabs, primary= on white, secondary= on grey50" }, "attribute": "mode-variant", "reflect": false, "defaultValue": "'primary'" }, "defaultSelectedIndex": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Sets the default selected Tab." }, "attribute": "default-selected-index", "reflect": false, "defaultValue": "0" }, "selectedIndex": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Sets the selected Tab.\nIf this is set, all Tab changes need to be handled by the user." }, "attribute": "selected-index", "reflect": true }, "leftPadding": { "type": "number", "mutable": false, "complexType": { "original": "number", "resolved": "number", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Custom left padding value for the wrapper element." }, "attribute": "left-padding", "reflect": true, "defaultValue": "32" }, "tdsScrollLeftAriaLabel": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Defines aria-label on left scroll button" }, "attribute": "tds-scroll-left-aria-label", "reflect": false, "defaultValue": "'Scroll left'" }, "tdsScrollRightAriaLabel": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Defines aria-label on right scroll button" }, "attribute": "tds-scroll-right-aria-label", "reflect": false, "defaultValue": "'Scroll right'" } }; } static get states() { return { "showLeftScroll": {}, "showRightScroll": {} }; } static get events() { return [{ "method": "tdsChange", "name": "tdsChange", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "Event emitted when the selected Tab is changed." }, "complexType": { "original": "{\n selectedTabIndex: number;\n }", "resolved": "{ selectedTabIndex: number; }", "references": {} } }]; } static get methods() { return { "selectTab": { "complexType": { "signature": "(tabIndex: number) => Promise<{ selectedTabIndex: number; }>", "parameters": [{ "name": "tabIndex", "type": "number", "docs": "" }], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<{ selectedTabIndex: number; }>" }, "docs": { "text": "Sets the passed tabindex as the selected Tab.", "tags": [] } }, "reinitialize": { "complexType": { "signature": "() => Promise<void>", "parameters": [], "references": { "Promise": { "location": "global", "id": "global::Promise" } }, "return": "Promise<void>" }, "docs": { "text": "Reinitializes the component.", "tags": [] } } }; } static get elementRef() { return "host"; } static get watchers() { return [{ "propName": "selectedIndex", "methodName": "handleSelectedIndexUpdate" }]; } }