UNPKG

@postnord/web-components

Version:

PostNord Web Components

218 lines (217 loc) 8.77 kB
/*! * Built with Stencil * By PostNord. */ import { h, Host, forceUpdate } from "@stencil/core"; import { arrow_left, arrow_right } from "pn-design-assets/pn-assets/icons.js"; /** * The `pn-segmented-control` and `pn-segmented` is built with native `input[type="radio"]` in the background. * This means you can use native events to listen to the changes. * * @nativeChange Use the `change` event to listen when the segment state is being changed. */ export class PnSegmentedControl { constructor() { this.showScrollArrows = false; this.showLeftArrow = false; this.showRightArrow = false; this.name = undefined; this.value = undefined; } mo; segmentContainer; segments = []; activeBg; hoverBg; scrollRegistered = false; hostElement; valueHandler() { this.setActiveSegment(); } changeHandler(event) { const target = event.target; this.value = target.value; this.setActiveSegment(); } handleResize() { this.rerender(); } handleHover(event) { const { target } = event.detail; this.calcHighlight(target, true); } componentDidLoad() { if (this.mo) this.mo.disconnect(); this.mo = new MutationObserver(() => { forceUpdate(this.hostElement); this.setSegments(); }); this.mo.observe(this.hostElement, { childList: true }); this.setSegments(); } setSegments() { this.segments = Array.from(this.hostElement.querySelectorAll('pn-segment')); this.setActiveSegment(); } setActiveSegment() { this.segments.forEach(segmentEl => { if (this.value === segmentEl.value) segmentEl.setAttribute('selected', 'true'); else segmentEl.removeAttribute('selected'); }); this.rerender(); } rerender() { requestAnimationFrame(() => { this.calcHighlight(); this.scrollArrowRender(); }); } /*---------------------------------------HIGHLIGHT LOGIC-------------------------------------------*/ calcHighlight(element, hover) { const chip = hover ? this.hoverBg : this.activeBg; requestAnimationFrame(() => { const el = element || this.segments.find(({ value }) => value === this.value); const elSegment = el?.closest('pn-segment'); if (!elSegment) return; const elRect = elSegment.getBoundingClientRect(); const { left: hostLeft } = this.segmentContainer.getBoundingClientRect(); const { left: segmentLeft, height: segmentHeight, width: segmentWidth } = elRect; const offset = segmentLeft - hostLeft + this.segmentContainer.scrollLeft; chip.style.setProperty('transform', `translate(${offset}px, -50%`); chip.style.setProperty('width', `${segmentWidth}px`); chip.style.setProperty('height', `${segmentHeight}px`); el.disabled ? (chip.dataset.disabled = '') : delete chip.dataset.disabled; }); } /*---------------------------------------/HIGHLIGHT LOGIC-------------------------------------------*/ /*---------------------------------------SCROLL ARROW LOGIC-------------------------------------------*/ scrollArrowRender() { if (this.segmentContainer.scrollWidth > this.segmentContainer.clientWidth) { this.showScrollArrows = true; if (!this.scrollRegistered) { this.segmentContainer.addEventListener('scroll', this.scrollArrowRender.bind(this)); this.scrollRegistered = true; } const amountScrolled = Math.round(this.segmentContainer.scrollWidth - this.segmentContainer.scrollLeft); const distanceToEnd = amountScrolled - this.segmentContainer.clientWidth; const distanceToStart = this.segmentContainer.scrollLeft; this.showLeftArrow = distanceToStart > 0; this.showRightArrow = distanceToEnd > 0; return; } this.showLeftArrow = false; this.showRightArrow = false; this.showScrollArrows = false; } scroll(val) { let amount = this.segmentContainer.scrollLeft + val; this.segmentContainer.scroll({ left: amount, behavior: 'smooth', }); } scrollArrowClasses() { const list = ['pn-sc-arrows']; if (this.showLeftArrow) list.push('pn-sc-left-visible'); if (this.showRightArrow) list.push('pn-sc-right-visible'); return list.join(' '); } /*---------------------------------------/SCROLL ARROW LOGIC-------------------------------------------*/ render() { return (h(Host, { key: 'e9ae7b8a52027c3a16b75e57097cf8c9de5f6ac2' }, h("div", { key: 'f29c57449726a72c699bd76652742f875c518c9f', class: "pn-segmented-control", ref: el => (this.segmentContainer = el) }, h("slot", { key: '253edcfe5d9171dcd5ea833b254f7ac5deba7f31' }), h("div", { key: '9b6a8a0194ffff718ec96bf5c490a4dc8a78cb06', class: "pn-sc-bg", "data-active": true, ref: el => (this.activeBg = el) }), h("div", { key: '23c5e00d3b05795f52dc6ae92b6feade51592d13', class: "pn-sc-bg", "data-hover": true, ref: el => (this.hoverBg = el) })), h("div", { key: 'e8b85e3c05f9afa18937c7356f1db5110155c9de', class: this.scrollArrowClasses(), hidden: !this.showScrollArrows }, h("button", { key: '84ebd54950d98d5ceca770a0095e95fe8c2597d0', "aria-label": "Left", class: "pn-sc-arrow pn-sc-arrow-left", onClick: () => this.scroll(-120), tabindex: "-1" }, h("pn-icon", { key: '100859353d859924ab2de1028deb12deab09a8a8', icon: arrow_left })), h("button", { key: '9a739b38b210fb26b12b68c139c373eee439139c', "aria-label": "Right", class: "pn-sc-arrow pn-sc-arrow-right", onClick: () => this.scroll(120), tabindex: "-1" }, h("pn-icon", { key: '861a4d87897175100157a3af3ed7d5699206c153', icon: arrow_right }))))); } static get is() { return "pn-segmented-control"; } static get originalStyleUrls() { return { "$": ["pn-segmented-control.scss"] }; } static get styleUrls() { return { "$": ["pn-segmented-control.css"] }; } static get properties() { return { "name": { "type": "string", "mutable": false, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": true, "optional": false, "docs": { "tags": [{ "name": "deprecated", "text": "Set the name in the nested `pn-segment` components." }], "text": "This is the name of the radio buttons inside the controller." }, "attribute": "name", "reflect": false }, "value": { "type": "string", "mutable": true, "complexType": { "original": "string", "resolved": "string", "references": {} }, "required": false, "optional": false, "docs": { "tags": [], "text": "Currently active segment value." }, "attribute": "value", "reflect": false } }; } static get states() { return { "showScrollArrows": {}, "showLeftArrow": {}, "showRightArrow": {} }; } static get elementRef() { return "hostElement"; } static get watchers() { return [{ "propName": "value", "methodName": "valueHandler" }]; } static get listeners() { return [{ "name": "change", "method": "changeHandler", "target": undefined, "capture": false, "passive": false }, { "name": "resize", "method": "handleResize", "target": "window", "capture": false, "passive": true }, { "name": "segmentHover", "method": "handleHover", "target": undefined, "capture": false, "passive": false }]; } } //# sourceMappingURL=pn-segmented-control.js.map