@scania/tegel
Version:
Tegel Design System
349 lines (348 loc) • 13.7 kB
JavaScript
import { Host, h, } from "@stencil/core";
/**
* @slot <default> - <b>Unnamed slot.</b> For the tab elements.
*/
export class TdsFolderTabs {
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 = null;
this.defaultSelectedIndex = 0;
this.selectedIndex = undefined;
this.tdsScrollLeftAriaLabel = 'Scroll left';
this.tdsScrollRightAriaLabel = 'Scroll right';
this.buttonWidth = 0;
this.showLeftScroll = false;
this.showRightScroll = false;
}
/** Sets the passed tabindex as the selected Tab. */
async selectTab(tabIndex) {
if (tabIndex < 0 || tabIndex >= this.children.length) {
throw new Error('Tab index out of bounds');
}
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');
});
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;
}
}
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: 'c56bf987e23ade8b30cd2470ab1ed0f4273bd191', role: "tablist", class: `${this.modeVariant ? `tds-mode-variant-${this.modeVariant}` : ''}` }, h("div", { key: 'afab8a86427918d5ac4bbc17dd2a436a986df577', class: "wrapper", ref: (el) => {
this.navWrapperElement = el;
} }, h("button", { key: '0b82a953932e888d05478d82944c90768f97bb36', "aria-label": this.tdsScrollLeftAriaLabel, class: `scroll-left-button ${this.showLeftScroll ? 'show' : ''}`, disabled: !this.showLeftScroll, onClick: () => this.scrollLeft() }, h("tds-icon", { key: '50cd22c5be43ae452e0896b449570774c3ab0151', name: "chevron_left", size: "20px" })), h("slot", { key: '3cb17dddc8592241c467167b3a0028355f2b7d5e', onSlotchange: () => this.handleSlotChange() }), h("button", { key: '23c289b70d723b2d0705692835a510b8a650fcfa', "aria-label": this.tdsScrollRightAriaLabel, class: `scroll-right-button ${this.showRightScroll ? 'show' : ''}`, disabled: !this.showRightScroll, onClick: () => this.scrollRight() }, h("tds-icon", { key: 'fff2bfcbcf916d1b391aa25ee5f6fda3c8e63d23', name: "chevron_right", size: "20px" })))));
}
static get is() { return "tds-folder-tabs"; }
static get encapsulation() { return "shadow"; }
static get originalStyleUrls() {
return {
"$": ["folder-tabs.scss"]
};
}
static get styleUrls() {
return {
"$": ["folder-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": "null"
},
"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
},
"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 {
"buttonWidth": {},
"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"
}];
}
}