UNPKG

@postnord/web-components

Version:
313 lines (308 loc) 18.6 kB
/*! * Built with Stencil * By PostNord. */ 'use strict'; var index = require('./index-DVv2io0H.js'); var index$1 = require('./index.cjs.js'); var arrow_left = require('./arrow_left-Crkz484c.js'); var arrow_right = require('./arrow_right-CABi9Wkp.js'); var chevron_down = require('./chevron_down-BLU6_yOG.js'); const pnPageNavCss = "pn-page-nav{display:block;position:relative;font-size:clamp(0.8em, 2vw, 1em);z-index:5}.pn-page-nav-wrapper{transform:translateZ(0);font-size:inherit;max-width:100%;overflow:hidden;position:relative;display:inline-flex;border-radius:3em}.pn-page-nav{display:flex;align-items:center;border-radius:3em;min-height:3em;background:#0d234b;border:0.0625em solid #d3cecb;position:relative;overflow-x:auto;scroll-snap-type:x mandatory}.pn-page-nav::-webkit-scrollbar{display:none}.pn-page-nav ul.pn-page-nav-items{display:flex;list-style-type:none;padding:0.25em;margin:0;position:relative}.pn-page-nav-items>.pn-pn-bg{position:absolute;top:50%;transform:translateY(-50%);will-change:transform;left:0;border-radius:3em;transition:width 0.25s cubic-bezier(0.29, 0.15, 0.24, 0.97), transform 0.25s cubic-bezier(0.29, 0.15, 0.24, 0.97), background 0.15s, opacity 0.15s, box-shadow 0.15s}.pn-page-nav-items>.pn-pn-bg.pn-pn-active{box-shadow:0px 0.3px 0.9px rgba(0, 0, 0, 0.1), 0px 1.6px 3.6px rgba(0, 0, 0, 0.13);background:#005d92;z-index:1}.pn-page-nav-items>.pn-pn-bg.pn-pn-hover{background:#ffffff;opacity:0;border:0.0625em solid transparent;z-index:0}.pn-page-nav-items>.pn-pn-bg.hidden{opacity:0}@media (hover: hover){ul.pn-page-nav-items:focus-within .pn-pn-hover,ul.pn-page-nav-items:hover .pn-pn-hover{opacity:0.12}}pn-page-nav .pn-pn-arrows{position:absolute;top:50%;transform:translateY(-50%);left:0;height:100%;width:100%;z-index:3;display:flex;justify-content:space-between;align-items:center;pointer-events:none;padding:0.125em 0.1em}pn-page-nav .pn-pn-arrows svg{width:1.5em}pn-page-nav .pn-pn-arrows svg.pn-icon-svg path{fill:#005d92}pn-page-nav .pn-pn-arrows>*{transition:transform 0.2s, opacity 0.2s, background 0.1s, box-shadow 0.1s;pointer-events:all;height:100%;aspect-ratio:1;background:#ffffff;display:flex;align-items:center;justify-content:center;outline:none;cursor:pointer;opacity:0;will-change:transform;-webkit-tap-highlight-color:transparent;border-radius:50%;border:0.0625em solid #0d234b}pn-page-nav .pn-pn-arrows>*.pn-pn-arrow-left{transform:translateX(-100%)}pn-page-nav .pn-pn-arrows>*.pn-pn-arrow-right{transform:translateX(100%)}@media screen and (max-width: 30em){pn-page-nav .pn-pn-arrows>*{border:none}pn-page-nav .pn-pn-arrows>* pn-icon .pn-icon-svg{width:100%;height:2em;animation:arrow 0.6s ease-in-out alternate infinite}pn-page-nav .pn-pn-arrows>* pn-icon .pn-icon-svg path{fill:#ffffff}pn-page-nav .pn-pn-arrows>*.pn-pn-arrow-left{background:linear-gradient(to left, transparent, #0d234b 60%);border-radius:50% 0 0 50%}pn-page-nav .pn-pn-arrows>*.pn-pn-arrow-right{background:linear-gradient(to right, transparent, #0d234b 40%);border-radius:0 50% 50% 0}}pn-page-nav .pn-pn-arrows.pn-pn-left-visible .pn-pn-arrow-left,pn-page-nav .pn-pn-arrows.pn-pn-right-visible .pn-pn-arrow-right{opacity:1;transform:translateX(0)}@keyframes arrow{to{transform:translateX(10%)}}button.pn-page-nav-dropdown-button{font-size:1em;border-radius:3em 0 0 3em;background:#0d234b;color:#ffffff;padding:0.5em 1em 0.5em 1.5em;cursor:pointer;display:flex;align-items:center;border:none;transition:background 0.15s;outline:none;height:100%;position:relative;margin:0 0.5em 0 0;font-weight:500}button.pn-page-nav-dropdown-button pn-icon{margin-left:0.5em}button.pn-page-nav-dropdown-button:hover,button.pn-page-nav-dropdown-button:focus{background:#263655}button.pn-page-nav-dropdown-button:focus{box-shadow:inset 0 0 0 0.1em #005d92, inset 0 0 0 0.2em #d3cecb}button.pn-page-nav-dropdown-button.pn-page-nav-dropdown-active{background:#005d92}button.pn-page-nav-dropdown-button.pn-page-nav-dropdown-active:hover,button.pn-page-nav-dropdown-button.pn-page-nav-dropdown-active:focus{background:#005d92}.pn-page-nav-divider{height:60%;background:#d3cecb;width:0.1em;position:absolute;right:0;transform:translateX(50%)}ul.pn-page-nav-dropdown{position:absolute;left:0;top:110%;background:#0d234b;border-radius:0.5em;margin:0;padding:0;list-style-type:none;box-shadow:0px 6.4000000954px 14.3999996185px 0px rgba(0, 0, 0, 0.1294117647), 0px 1.2000000477px 3.5999999046px 0px rgba(0, 0, 0, 0.1019607843);font-size:inherit;display:none}ul.pn-page-nav-dropdown.pn-page-nav-dropdown-open{display:block}"; const PnPageNav = class { constructor(hostRef) { index.registerInstance(this, hostRef); this.navchange = index.createEvent(this, "navchange"); } mo; navContainer; navWrapper; navItems = []; dropdownButton; dropdownEl; dropdownItems; dropdownEls; eventHandler = this.keyboardEvents.bind(this); globalEventHandler = this.globalEvents.bind(this); activeBg; hoverBg; scrollRegistered = false; get hostElement() { return index.getElement(this); } currentSelection; showScrollArrows = false; showLeftArrow = false; showRightArrow = false; dropdownOpen = false; dropdownLinks = []; dropdownActive = false; /** Currently active menu item value */ value; /** Pass a string which will be the text on the dropdown button. */ dropdown = false; /** Set a unique HTML ID. */ navid = `pn-page-nav-${index$1.uuidv4()}`; /** Emits the value of the selected item. */ navchange; changeHandler({ target }) { this.currentSelection = target.closest('pn-page-nav-item'); if (target.value) this.value = target.value; if (this.dropdownOpen) this.dropdownOpen = false; } handleResize() { this.rerender(); } valueHandler() { if (!this.value) this.currentSelection = null; this.calcHighlight(this.currentSelection, this.activeBg); this.navchange.emit(this.value); if (!this.dropdownActive) return; this.isDropdownItemActive(); } dropdownHandler() { if (this.dropdownOpen) { requestAnimationFrame(() => { this.addGlobalEventListeners(); }); return; } this.removeGlobalEventListeners(); } /* ---------------------------------------LIFECYCLE--------------------------------------- */ componentWillLoad() { if (!this.dropdown) return; this.dropdownEls = Array.from(this.hostElement.querySelectorAll('pn-page-nav-dropdown-item')); if (this.dropdownEls.length) { this.dropdownActive = true; this.initiateDropdown(); } } componentDidLoad() { if (this.mo) this.mo.disconnect(); this.mo = new MutationObserver(() => { index.forceUpdate(this.hostElement); this.setActiveNavItem(); this.rerender(); }); this.mo.observe(this.hostElement, { childList: true, subtree: true }); this.navWrapper = this.hostElement.querySelector('.pn-page-nav'); this.navContainer = this.hostElement.querySelector('.pn-page-nav-items'); this.activeBg = this.hostElement.querySelector('.pn-pn-active'); this.hoverBg = this.hostElement.querySelector('.pn-pn-hover'); this.hostElement.addEventListener('mouseover', ({ target }) => this.calcHighlight(target, this.hoverBg)); this.setActiveNavItem(); this.rerender(); } /* ---------------------------------------/LIFECYCLE--------------------------------------- */ setActiveNavItem() { this.navItems = Array.from(this.hostElement.querySelectorAll('pn-page-nav-item')); this.navItems.forEach(navItemEl => { if (this.value === navItemEl.value) { /** @ts-ignore This component will be removed soon, no need to deal with this. */ this.currentSelection = navItemEl; } else { navItemEl.removeAttribute('selected'); } navItemEl .querySelector('a') .addEventListener('focus', ({ target }) => this.calcHighlight(target, this.hoverBg)); }); /* -----------------dropdown------------------ */ if (!this.dropdownActive) return; this.dropdownItems = Array.from(this.hostElement.querySelectorAll('.pn-page-nav-dropdown-item')); //Check active state on each item this.isDropdownItemActive(); //Store all values to check if dropdown button should be active this.dropdownLinks = this.dropdownItems.map((el, i) => { el.setAttribute('data-index', `${i}`); return el.closest('pn-page-nav-dropdown-item').value; }); /* -----------------/dropdown------------------ */ } rerender() { requestAnimationFrame(() => { this.calcHighlight(this.currentSelection, this.activeBg); this.scrollArrowRender(); }); } /*---------------------------------------HIGHLIGHT LOGIC-------------------------------------------*/ calcHighlight(el, bgEl) { if (!el?.closest('pn-page-nav-item')) { bgEl?.classList.add('hidden'); return; } if (bgEl) bgEl.classList.remove('hidden'); const elRect = el.closest('pn-page-nav-item').getBoundingClientRect(); const { left: hostLeft } = this.navContainer.getBoundingClientRect(); const { left: navLeft, height: navHeight, width: navWidth } = elRect; const offset = navLeft - hostLeft + this.navContainer.scrollLeft; bgEl.style.setProperty('transform', `translate(${offset}px, -50%`); bgEl.style.setProperty('width', `${navWidth}px`); bgEl.style.setProperty('height', `${navHeight}px`); } /*---------------------------------------/HIGHLIGHT LOGIC-------------------------------------------*/ /*---------------------------------------SCROLL ARROW LOGIC-------------------------------------------*/ scrollArrowRender() { if (!this.navWrapper) return; if (this.navWrapper.scrollWidth > this.navWrapper.clientWidth) { this.showScrollArrows = true; if (!this.scrollRegistered) { this.navWrapper.addEventListener('scroll', this.scrollArrowRender.bind(this)); this.scrollRegistered = true; } const amountScrolled = Math.round(this.navWrapper.scrollWidth - this.navWrapper.scrollLeft); const distanceToEnd = amountScrolled - this.navWrapper.clientWidth; const distanceToStart = this.navWrapper.scrollLeft; this.showLeftArrow = distanceToStart > 0; this.showRightArrow = distanceToEnd > 0; return; } this.showLeftArrow = false; this.showRightArrow = false; this.showScrollArrows = false; } scroll(val) { let amount = this.navWrapper.scrollLeft + val; this.navWrapper.scroll({ left: amount, behavior: 'smooth', }); } scrollArrowClasses() { let classNames = 'pn-pn-arrows '; if (this.showLeftArrow) classNames += 'pn-pn-left-visible '; if (this.showRightArrow) classNames += 'pn-pn-right-visible '; return classNames; } /*---------------------------------------/SCROLL ARROW LOGIC-------------------------------------------*/ /* ---------------------------------------DROPDOWN LOGIC--------------------------------------- */ initiateDropdown() { requestAnimationFrame(() => { this.dropdownButton = this.hostElement.querySelector('.pn-page-nav-dropdown-button'); this.dropdownEl = this.hostElement.querySelector('.pn-page-nav-dropdown'); this.addDropdownEventListeners(); }); } toggleDropdown() { this.dropdownOpen = !this.dropdownOpen; } isDropdownItemActive() { this.dropdownEls.forEach(el => { if (el.value && this.value === el.value) { el.setAttribute('active', 'true'); return; } el.removeAttribute('active'); }); } /* -----------------events------------------ */ /* -----------------temporary events------------------ */ addDropdownEventListeners() { this.hostElement.addEventListener('keydown', this.eventHandler); this.hostElement.addEventListener('click', this.eventHandler); } addGlobalEventListeners() { const root = this.hostElement.getRootNode(); root.addEventListener('focusin', this.globalEventHandler); root.addEventListener('keydown', this.globalEventHandler); root.addEventListener('click', this.globalEventHandler); } removeGlobalEventListeners() { const root = this.hostElement.getRootNode(); root.removeEventListener('focusin', this.globalEventHandler); root.removeEventListener('keydown', this.globalEventHandler); root.removeEventListener('click', this.globalEventHandler); } /* -----------------/temporary events------------------ */ /* -----------------Open dropdown with keyboard------------------ */ keyboardEvents(e) { const target = e.composedPath()[0]; // As long as the dropdown is closed, we only want it to react to keyboard input // is the user has focus on the button if (e.type === 'keydown') { if (!this.dropdownOpen && target === this.dropdownButton && ['ArrowUp', 'ArrowDown'].includes(e.code)) { this.dropdownOpen = true; requestAnimationFrame(() => { this.focusNextDropdownItem(); }); } } } /* -----------------/Open dropdown with keyboard------------------ */ globalEvents(e) { const target = e.composedPath()[0]; if (e.type === 'keydown' && e.code === 'Escape') { this.dropdownOpen = false; this.dropdownButton.focus(); } if (e.code === 'ArrowDown') this.focusNextDropdownItem(); if (e.code === 'ArrowUp') this.focusPrevDropdownItem(); if ((e.type === 'click' || e.type === 'focusin') && !this.dropdownEl.contains(target)) { this.dropdownOpen = false; } } /* -----------------/events------------------ */ /* -----------------focusing------------------ */ focusNextDropdownItem() { const { activeElement } = this.hostElement.getRootNode(); if (!activeElement.classList.contains('pn-page-nav-dropdown-item')) { this.dropdownItems[0].focus(); return; } // focus next item const index = parseInt(activeElement.getAttribute('data-index')); if (index < this.dropdownItems.length - 1) { this.dropdownItems[index + 1].focus(); } } focusPrevDropdownItem() { const { activeElement } = this.hostElement.getRootNode(); if (!activeElement.classList.contains('dropdown-item')) { this.dropdownItems[this.dropdownItems.length - 1].focus(); } // focus previous item const index = parseInt(activeElement.getAttribute('data-index')); if (index > 0) { this.dropdownItems[index - 1].focus(); return; } this.dropdownButton.focus(); } /* -----------------/focusing------------------ */ dropdownButtonClasses() { let classList = 'pn-page-nav-dropdown-button '; if (this.dropdownLinks.includes(this.value)) classList += 'pn-page-nav-dropdown-active '; return classList; } dropdownClasses() { let classList = 'pn-page-nav-dropdown '; if (this.dropdownOpen) classList += 'pn-page-nav-dropdown-open '; return classList; } /* ---------------------------------------/DROPDOWN LOGIC--------------------------------------- */ render() { return (index.h(index.Host, { key: '84aa7d48fb35062728db273ed972832a2387c17f' }, index.h("div", { key: 'e3b748a26a92de0be7e5f34ac07bbd02d79ac051', class: "pn-page-nav-wrapper" }, index.h("nav", { key: '7f368d201e6b4f6fdb8d3bbb88a82fd083248b66', class: "pn-page-nav" }, this.dropdownActive && (index.h("button", { key: 'b195fad1c0f965fd206781c2f68bf1e096ec1145', type: "button", class: this.dropdownButtonClasses(), onClick: () => this.toggleDropdown(), "aria-controls": "page-nav-dropdown", "aria-expanded": `${this.dropdownOpen}` }, this.dropdown, index.h("pn-icon", { key: '7d861107335a022e21f00f9ad724adfb7ec6e83c', icon: chevron_down.chevron_down, color: "white", small: true }), index.h("div", { key: '724514a04c237aab0bde8eeaf0a5ea0ba4d57754', class: "pn-page-nav-divider" }))), index.h("ul", { key: 'a9904dfd2acf7425900bd5b1c400a40ef46930e5', class: "pn-page-nav-items" }, index.h("slot", { key: '16240b85d64bc3667315389756505f426d202f16' }), index.h("li", { key: 'da34a7805394b01718ab808c5c12f24fd434c692', class: "pn-pn-bg pn-pn-active", role: "presentation" }), index.h("li", { key: '6431c8c54c3547dd20a12b758740327729e41ce7', class: "pn-pn-bg pn-pn-hover", role: "presentation" }))), this.showScrollArrows && (index.h("div", { key: '94c7037eac435f475df4bf74214d423ab6a222a2', class: this.scrollArrowClasses() }, index.h("button", { key: '4f54461490ffd67f0bc8c049b81d956704b5d2a0', class: "pn-pn-arrow-left", onClick: () => this.scroll(-120), tabindex: "-1" }, index.h("pn-icon", { key: '17c0a6744795cac8a6be6e876b440f4bf6cc826c', icon: arrow_left.arrow_left, color: "white" })), index.h("button", { key: '80548ab34ed33c15cae5e375de5971fbeb40efa2', class: "pn-pn-arrow-right", onClick: () => this.scroll(120), tabindex: "-1" }, index.h("pn-icon", { key: '6a6ed35f46f835a7c57584b2ef4faf5376ffa90e', icon: arrow_right.arrow_right, color: "blue700" }))))), this.dropdownActive && (index.h("ul", { key: '6e48de81da28bd439fcea04f33a5e1ac6da5f1cd', id: this.navid, class: this.dropdownClasses() }, index.h("slot", { key: 'cd0c26b03d91afc099cefe622e46907eea1cb85a', name: "dropdown-item" }))))); } static get watchers() { return { "value": ["valueHandler"], "dropdownOpen": ["dropdownHandler"] }; } }; PnPageNav.style = pnPageNavCss; exports.pn_page_nav = PnPageNav; //# sourceMappingURL=pn-page-nav.entry.cjs.js.map