@postnord/web-components
Version:
PostNord Web Components
311 lines (307 loc) • 18.4 kB
JavaScript
/*!
* Built with Stencil
* By PostNord.
*/
import { r as registerInstance, c as createEvent, g as getElement, f as forceUpdate, h, a as Host } from './index-dc6e40e7.js';
import { u as uuidv4 } from './helpers-88f72b54.js';
import { a as angle_down } from './angle_down-d788f691.js';
import { a as arrow_left } from './arrow_left-f0d725e6.js';
import { a as arrow_right } from './arrow_right-1b36bbb0.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 PnPageNavStyle0 = pnPageNavCss;
const PnPageNav = class {
constructor(hostRef) {
registerInstance(this, hostRef);
this.navchange = createEvent(this, "navchange", 7);
}
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 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-${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(() => {
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) {
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 (h(Host, { key: 'a16348355135afdc75c1ed76084a706e55bf3eb9' }, h("div", { key: '0a3922bb0e86db9cfb6a4d2a8dee8852a9cd322b', class: "pn-page-nav-wrapper" }, h("nav", { key: 'e75d068b812297d93a50fd3dbab26e10df819e97', class: "pn-page-nav" }, this.dropdownActive && (h("button", { key: '47dcca744b1c9a85249844f7998ce9580e08987a', type: "button", class: this.dropdownButtonClasses(), onClick: () => this.toggleDropdown(), "aria-controls": "page-nav-dropdown", "aria-expanded": `${this.dropdownOpen}` }, this.dropdown, h("pn-icon", { key: '091aa26f5c08be56f2e7e3c8d1c8639e69793c87', icon: angle_down, color: "white", small: true }), h("div", { key: '69d2cb22c5d127c51d787fba2b8383f8f18f3f15', class: "pn-page-nav-divider" }))), h("ul", { key: 'df06400cb898e3f6afdb4b8b3e7e2f971b538160', class: "pn-page-nav-items" }, h("slot", { key: 'b7f52c9a58863626297e73f4305309b07f85c6b7' }), h("li", { key: '49e0ca11422ad4609785ab6aac5b43d8336d9706', class: "pn-pn-bg pn-pn-active", role: "presentation" }), h("li", { key: 'de694047d2fa461556d08a4a2c2a9d6baf28bbfd', class: "pn-pn-bg pn-pn-hover", role: "presentation" }))), this.showScrollArrows && (h("div", { key: '8cd2f0d10b2c8ca4adf8960dd0db65f6c0f748bb', class: this.scrollArrowClasses() }, h("button", { key: 'b0c6b2ca3f8664d7ea41a75a55b67373cc47da5d', class: "pn-pn-arrow-left", onClick: () => this.scroll(-120), tabindex: "-1" }, h("pn-icon", { key: '7a863252aa47471dab420cb528926ea7aa8f555e', icon: arrow_left, color: "white" })), h("button", { key: 'eced21ee4a4aaa2c3965b18d22f4c6100fb5fdf8', class: "pn-pn-arrow-right", onClick: () => this.scroll(120), tabindex: "-1" }, h("pn-icon", { key: 'f39d940505c69024aea9482539e49ce5d4e3acdb', icon: arrow_right, color: "blue700" }))))), this.dropdownActive && (h("ul", { key: '2f2a5e80abfffb0d8946748b96934c800622c741', id: this.navid, class: this.dropdownClasses() }, h("slot", { key: 'a3b453557e0900f2f2e8d98005f69e94f2176d9e', name: "dropdown-item" })))));
}
static get watchers() { return {
"value": ["valueHandler"],
"dropdownOpen": ["dropdownHandler"]
}; }
};
PnPageNav.style = PnPageNavStyle0;
export { PnPageNav as pn_page_nav };
//# sourceMappingURL=pn-page-nav.entry.js.map