UNPKG

@postnord/web-components

Version:

PostNord Web Components

312 lines (311 loc) 11.9 kB
/*! * Built with Stencil * By PostNord. */ import { h, Host, forceUpdate, } from "@stencil/core"; import { angle_left, angle_right } from "pn-design-assets/pn-assets/icons.js"; import translations from "./translations"; import { awaitTopbar, en } from "../../../globals/helpers"; /** * @slot menu - Can only be used inside of a `pn-header`. Allows the `href` attribute on the `pn-tab` when active. */ export class PnTablist { constructor() { this.showScrollArrows = false; this.arrowLeft = false; this.arrowRight = false; this.label = undefined; this.value = undefined; this.small = undefined; this.stackedicons = false; this.language = null; } mo; isMenu = false; isTabHovered = false; tabListEl; lineActive; lineHovered; tabElement; hostElement; /** * This will emit when a tab is changed. The detail property of the event will contain the value of the selected tab. * This is the event and value you listen to when you toggle the visibility among your tabpanels. **/ tabchange; setValue() { const tabs = Array.from(this.hostElement.querySelectorAll('pn-tab')); tabs.forEach(tab => { tab.activeTab = this.value; }); } scrollHandler() { if (this.showScrollArrows) { this.tabListEl.addEventListener('scroll', this.scrollIndicators.bind(this)); } else { this.tabListEl.removeEventListener('scroll', this.scrollIndicators); } } setActiveTabHandler({ detail }) { this.tabElement = detail.el; requestAnimationFrame(() => this.activateTab(detail.el)); if (this.value === detail.val) return; this.value = detail.val; this.tabchange.emit(this.value); this.tabElement.querySelector(this.isMenu ? 'a' : 'button').focus(); } rerender() { requestAnimationFrame(() => { this.scrollIndicators(); this.isTabHovered = false; }); } handleEnter(e) { this.isTabHovered = true; this.styleLines(e.target); } handleLeave() { this.isTabHovered = false; setTimeout(() => { if (!this.isTabHovered) this.lineHovered.style.setProperty('--pn-hover-opacity', '0'); }, 500); } async componentWillLoad() { this.isMenu = this.hostElement.slot === 'menu'; if (this.language === null) await awaitTopbar(this.hostElement); } componentDidLoad() { if (this.mo) this.mo.disconnect(); this.mo = new MutationObserver(() => { forceUpdate(this.hostElement); this.rerender(); }); this.mo.observe(this.hostElement, { childList: true, subtree: true }); } componentDidRender() { this.rerender(); this.setValue(); } getRect(element) { return element.getBoundingClientRect(); } activateTab(element) { this.styleLines(element, true); } styleLines(element, active = false) { const tabListCoords = this.getRect(this.tabListEl); const scrollOffset = this.tabListEl.scrollLeft; const line = this.getRect(element); const width = line.width; const offset = line.left + scrollOffset - tabListCoords.left; const cssVar = active ? 'active' : 'hover'; const prop = active ? 'lineActive' : 'lineHovered'; this[prop].style.setProperty(`--pn-${cssVar}-width`, `${width}px`); this[prop].style.setProperty(`--pn-${cssVar}-offset`, `${offset}px`); this[prop].style.setProperty(`--pn-${cssVar}-opacity`, '1'); } scrollIndicators() { const { scrollWidth, scrollLeft } = this.tabListEl; const { clientWidth } = this.hostElement; this.showScrollArrows = scrollWidth > clientWidth; this.arrowLeft = this.showScrollArrows && scrollLeft > 0; this.arrowRight = this.showScrollArrows && clientWidth + 16 + scrollLeft < scrollWidth; } scroll({ right = false } = {}) { const tabList = this.tabListEl; const { scrollLeft, clientWidth } = tabList; let left = scrollLeft; // The width of the scroll arrow is 32px, so we remove that from this calculation so the scroll const scrollAmount = clientWidth - 64; if (right) left += scrollAmount; else left -= scrollAmount; tabList.scrollTo({ left, behavior: 'smooth', }); } translate(prop) { return translations?.[prop]?.[this.language || en]; } render() { return (h(Host, { key: '443c87a427ed5ff701fe1334f531f22c052c4b2e' }, h("nav", { key: '1460f6f94cb54b1f72e8c81a8f13cb8bc57624ba', class: "pn-tablist", role: this.isMenu ? null : 'tablist', "aria-label": this.label, "data-stacked": this.stackedicons, "data-small": !this.isMenu && this.small, "data-large": this.isMenu, "data-scroll": this.showScrollArrows, ref: el => (this.tabListEl = el) }, h("slot", { key: '4253824a84a33c1262c2e551af17a75fdedb73dd' }), h("div", { key: '592caba939bba39c822e85edb2b45d974db2f1ba', class: "pn-line" }, h("div", { key: '3c29c82373e44fb3823b1308237710c84269c444', ref: el => (this.lineActive = el), class: "pn-line-item pn-line-active" }), h("div", { key: '39b8827aa624826a19d1eb396521982c39a9399d', ref: el => (this.lineHovered = el), class: "pn-line-item pn-line-hovered" }))), h("div", { key: '5a2a0f6bc02e0aa190a1c075e24f5ab953f0c74d', class: "pn-scroll-arrows" }, h("pn-button", { key: '8e0da8dfb83184106c0a0674910f288783f3585f', class: "pn-arrow", "data-show": this.arrowLeft, onClick: () => this.scroll(), noTab: true, appearance: "light", variant: "outlined", icon: angle_left, iconOnly: true, arialabel: this.translate('LEFT'), small: true }), h("pn-button", { key: 'f8e4bc2a947ac9e42b60731d79b70769bb14f30b', class: "pn-arrow", "data-show": this.arrowRight, onClick: () => this.scroll({ right: true }), noTab: true, appearance: "light", variant: "outlined", icon: angle_right, iconOnly: true, arialabel: this.translate('RIGHT'), small: true })))); } static get is() { return "pn-tablist"; } static get originalStyleUrls() { return { "$": ["pn-tablist.scss"] }; } static get styleUrls() { return { "$": ["pn-tablist.css"] }; } static get properties() { return { "label": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": true, "optional": false, "docs": { "tags": [], "text": "Give the tablist a name for screenreaders" }, "attribute": "label", "reflect": false }, "value": { "type": "string", "mutable": true, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": true, "optional": false, "docs": { "tags": [], "text": "The value of the tab that is currently active, each `<pn-tab value=\"example-value\">` also expects a unique value" }, "attribute": "value", "reflect": true }, "small": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": true, "docs": { "tags": [], "text": "Make the tablist smaller" }, "attribute": "small", "reflect": false }, "stackedicons": { "type": "boolean", "mutable": false, "complexType": { "original": "boolean", "resolved": "boolean", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Icons are stacked vertically instead of the default rows" }, "attribute": "stackedicons", "reflect": false, "defaultValue": "false" }, "language": { "type": "string", "mutable": false, "complexType": { "original": "PnLanguages", "resolved": "\"\" | \"da\" | \"en\" | \"fi\" | \"no\" | \"sv\"", "references": { "PnLanguages": { "location": "import", "path": "@/globals/types", "id": "src/globals/types.ts::PnLanguages" } } }, "required": false, "optional": true, "docs": { "tags": [], "text": "Manually set the language." }, "attribute": "language", "reflect": false, "defaultValue": "null" } }; } static get states() { return { "showScrollArrows": {}, "arrowLeft": {}, "arrowRight": {} }; } static get events() { return [{ "method": "tabchange", "name": "tabchange", "bubbles": true, "cancelable": true, "composed": true, "docs": { "tags": [], "text": "This will emit when a tab is changed. The detail property of the event will contain the value of the selected tab.\nThis is the event and value you listen to when you toggle the visibility among your tabpanels." }, "complexType": { "original": "string", "resolved": "string", "references": {} } }]; } static get elementRef() { return "hostElement"; } static get watchers() { return [{ "propName": "value", "methodName": "setValue" }, { "propName": "showScrollArrows", "methodName": "scrollHandler" }]; } static get listeners() { return [{ "name": "setActiveTab", "method": "setActiveTabHandler", "target": undefined, "capture": false, "passive": false }, { "name": "resize", "method": "rerender", "target": "window", "capture": false, "passive": true }, { "name": "tabEnter", "method": "handleEnter", "target": undefined, "capture": false, "passive": false }, { "name": "tabLeave", "method": "handleLeave", "target": undefined, "capture": false, "passive": false }]; } } //# sourceMappingURL=pn-tablist.js.map