@scania/tegel
Version:
Tegel Design System
401 lines (400 loc) • 15.9 kB
JavaScript
import { Host, h, } from "@stencil/core";
/**
* @slot <default> - <b>Unnamed slot.</b> For the tab elements.
*/
export class TdsInlineTabs {
constructor() {
/** Variant of the Tabs, primary= on white, secondary= on grey50 */
this.modeVariant = 'primary';
/** Sets the default selected Tab. */
this.defaultSelectedIndex = 0;
/** Defines aria-label on left scroll button */
this.tdsScrollLeftAriaLabel = 'Scroll left';
/** Defines aria-label on right scroll button */
this.tdsScrollRightAriaLabel = 'Scroll right';
/** Custom left padding value for the wrapper element. */
this.leftPadding = 32;
this.showLeftScroll = false;
this.showRightScroll = false;
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.scrollableWidth = 0; // total amount that is possible to scroll in the nav wrapper
this.tabElements = [];
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.scrollableWidth = buttonsWidth - componentWidth;
this.updateScrollButtons();
});
});
if (this.navWrapperElement)
resizeObserver.observe(this.navWrapperElement);
};
this.addEventListenerToTabs = () => {
this.tabElements = Array.from(this.host.children);
this.tabElements.map((item, index) => {
const clickHandler = () => {
if (!item.disabled) {
const tdsChangeEvent = this.tdsChange.emit({
selectedTabIndex: this.tabElements.indexOf(item),
});
if (!tdsChangeEvent.defaultPrevented) {
this.tabElements.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.tabElements.forEach((item) => {
const clickHandler = this.clickHandlers.get(item);
if (clickHandler) {
item.removeEventListener('click', clickHandler);
this.clickHandlers.delete(item);
}
});
};
}
/** Selects a Tab based on tabindex, will not select a disabled Tab. */
async selectTab(tabIndex) {
if (tabIndex < 0 || tabIndex >= this.tabElements.length) {
throw new Error('Tab index out of bounds');
}
this.tabElements = Array.from(this.host.children);
if (!this.tabElements[tabIndex].disabled) {
this.tabElements.forEach((element) => element.setSelected(false));
this.tabElements = this.tabElements.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.tabElements = Array.from(this.host.children).map((tabElement) => {
tabElement.setSelected(false);
return tabElement;
});
if (this.selectedIndex !== undefined) {
this.tabElements[this.selectedIndex].setSelected(true);
}
}
scrollRight() {
if (!this.navWrapperElement)
return;
const scroll = this.navWrapperElement.scrollLeft;
this.navWrapperElement.scrollLeft = scroll + this.buttonsWidth;
this.evaluateScrollButtons();
}
scrollLeft() {
if (!this.navWrapperElement)
return;
const scroll = this.navWrapperElement.scrollLeft;
this.navWrapperElement.scrollLeft = scroll - this.buttonsWidth;
this.evaluateScrollButtons();
}
evaluateScrollButtons() {
if (!this.navWrapperElement)
return;
const scroll = this.navWrapperElement.scrollLeft;
this.showRightScroll = scroll <= this.scrollableWidth;
this.showLeftScroll = scroll > 0;
}
initializeTabs() {
this.tabElements = Array.from(this.host.children);
// remove first and last class from other tabs in case of initialization
this.tabElements.forEach((child) => {
child.classList.remove('last');
child.classList.remove('first');
});
this.tabElements[0].classList.add('first');
this.tabElements[this.tabElements.length - 1].classList.add('last');
}
initializeSelectedTab() {
if (this.selectedIndex === undefined) {
this.addEventListenerToTabs();
this.tabElements[this.defaultSelectedIndex].setSelected(true);
this.selectedIndex = this.defaultSelectedIndex;
}
else {
this.tabElements[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;
}
}
handleSlotChange() {
this.initializeTabs();
this.addEventListenerToTabs();
this.initializeSelectedTab();
this.updateScrollButtons();
this.addResizeObserver();
}
connectedCallback() {
this.initializeTabs();
}
componentDidLoad() {
this.initializeSelectedTab();
}
componentDidRender() {
this.updateScrollButtons();
this.addResizeObserver();
}
disconnectedCallback() {
this.removeEventListenerFromTabs();
}
render() {
return (h(Host, { key: 'c560fe618bd56e56b83d98959a787d1d253b294a', role: "tablist", class: `${this.modeVariant ? `tds-mode-variant-${this.modeVariant}` : ''}`, style: { '--padding-left': `${this.leftPadding}px` } }, h("div", { key: '4a2070625881a955ddc8b7d58869ac2908f1aeef', class: "wrapper", ref: (el) => {
this.navWrapperElement = el;
} }, h("button", { key: '1df4f76b71faf9ea2e2f57c3da07c41315bcb59f', "aria-label": this.tdsScrollLeftAriaLabel, class: `scroll-left-button ${this.showLeftScroll ? 'show' : ''}`, onClick: () => this.scrollLeft(), disabled: !this.showLeftScroll }, h("tds-icon", { key: '9e0e80a1791ccf9fb28b1c597ce498808365f9c1', name: "chevron_left", size: "20px" })), h("slot", { key: 'f702f8f7761d797cb121ce77f96276a615c211f9', onSlotchange: () => this.handleSlotChange() }), h("button", { key: '735e8a5e76d32903ecf666e59a646d4bbf0b8c4d', "aria-label": this.tdsScrollRightAriaLabel, class: `scroll-right-button ${this.showRightScroll ? 'show' : ''}`, onClick: () => this.scrollRight(), disabled: !this.showRightScroll }, h("tds-icon", { key: 'f0a59a1305c1ae93908ca307c03d57b9a171c4ec', name: "chevron_right", size: "20px" })))));
}
static get is() { return "tds-inline-tabs"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() {
return {
"$": ["inline-tabs.scss"]
};
}
static get styleUrls() {
return {
"$": ["inline-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"
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "mode-variant",
"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."
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "default-selected-index",
"defaultValue": "0"
},
"selectedIndex": {
"type": "number",
"mutable": false,
"complexType": {
"original": "number",
"resolved": "number | undefined",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Sets the selected Tab.\nIf this is set, all Tab changes need to be handled by the user."
},
"getter": false,
"setter": false,
"reflect": true,
"attribute": "selected-index"
},
"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"
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "tds-scroll-left-aria-label",
"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"
},
"getter": false,
"setter": false,
"reflect": false,
"attribute": "tds-scroll-right-aria-label",
"defaultValue": "'Scroll right'"
},
"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."
},
"getter": false,
"setter": false,
"reflect": true,
"attribute": "left-padding",
"defaultValue": "32"
}
};
}
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 | undefined; }>",
"parameters": [{
"name": "tabIndex",
"type": "number",
"docs": ""
}],
"references": {
"Promise": {
"location": "global",
"id": "global::Promise"
},
"Array": {
"location": "global",
"id": "global::Array"
},
"HTMLTdsInlineTabElement": {
"location": "global",
"id": "global::HTMLTdsInlineTabElement"
}
},
"return": "Promise<{ selectedTabIndex: number | undefined; }>"
},
"docs": {
"text": "Selects a Tab based on tabindex, will not select a disabled 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"
}];
}
}