@postnord/web-components
Version:
PostNord Web Components
200 lines (195 loc) • 11.8 kB
JavaScript
/*!
* Built with Stencil
* By PostNord.
*/
import { proxyCustomElement, HTMLElement, forceUpdate, h, Host } from '@stencil/core/internal/client';
import { o as open_in_new } from './open_in_new.js';
import { r as ripple } from './helpers.js';
import { d as defineCustomElement$2 } from './pn-icon2.js';
const pnTileCss = "pn-tile{position:relative;font-size:1em;}pn-tile a.pn-tile-link{position:absolute;top:0;left:0;height:100%;width:100%;pointer-events:none;opacity:0}pn-tile .pn-tile{border:0.1rem solid #d3cecb;outline:none;background:#ffffff;box-shadow:0 0.1rem 0.2rem rgba(0, 0, 0, 0.2);cursor:pointer;display:flex;flex-wrap:wrap;align-items:flex-start;justify-content:center;padding:1.5em;padding:min(7%, 1.5em);word-break:break-word;text-decoration:none;border-radius:0.8rem;transition:box-shadow 0.1s cubic-bezier(0.6, 0, 0.2, 1) 0.1s, background 0.2s linear, border 0.1s linear;position:relative;overflow:hidden;text-align:left;user-select:text;height:100%}pn-tile .pn-tile.vertical,pn-tile .pn-tile.simple{text-align:center}pn-tile .pn-tile.vertical h3,pn-tile .pn-tile.simple h3{font-size:1.5em;font-size:clamp(1em, var(--w) * 1.4, 1.5em)}pn-tile .pn-tile.vertical .tile-slot,pn-tile .pn-tile.simple .tile-slot{font-size:1em;font-size:clamp(1.2rem, var(--w), 1em)}pn-tile .pn-tile.vertical>*,pn-tile .pn-tile.simple>*{margin:0.5em 0}pn-tile .pn-tile.simple{flex-direction:column;align-items:center}pn-tile .pn-tile.simple .tile-slot{display:none}pn-tile .pn-tile.simple h3>pn-icon{margin-left:0.1em;vertical-align:bottom}pn-tile .pn-tile.simple>*{margin:0.5em}pn-tile .pn-tile.simple>.text-cont{margin:0.5em}pn-tile .pn-tile.simple>svg,pn-tile .pn-tile.simple>.tile-illustration-slot{max-width:clamp(3em, 70%, 10em)}pn-tile .pn-tile.simple>svg,pn-tile .pn-tile.simple>.tile-illustration-slot,pn-tile .pn-tile.simple>.text-cont{flex:auto}pn-tile .pn-tile.horizontal{flex-wrap:nowrap}pn-tile .pn-tile>*{margin:0.5em;z-index:2}pn-tile .pn-tile>svg,pn-tile .pn-tile>.tile-illustration-slot{width:100%;max-width:8em;flex:0.5 3 calc((32rem - 100%) * 999);z-index:2}pn-tile .pn-tile>.tile-illustration-slot{max-width:13em}pn-tile .pn-tile>.tile-illustration-slot>*{width:100%;display:block}pn-tile .pn-tile>.text-cont{flex:1 1 calc((32rem - 100%) * 999)}pn-tile .pn-tile>.text-cont.horizontal{flex:1 1 0}pn-tile .pn-tile>.text-cont.horizontal>h3{white-space:nowrap}pn-tile .pn-tile h3{color:#005d92;font-size:1.5em}pn-tile .pn-tile .tile-slot{color:#5e554a;margin-top:0.25em}pn-tile a:focus-visible+.pn-tile{box-shadow:0 0 0 0.3rem #ffffff, 0 0 0 0.6rem #005d92}pn-tile .pn-tile:hover,pn-tile a:focus-visible+.pn-tile{background:#effbff;border:0.1rem solid #8eddf9}pn-tile .pn-tile:hover.vertical .tile-circle,pn-tile .pn-tile:hover.simple .tile-circle,pn-tile a:focus-visible+.pn-tile.vertical .tile-circle,pn-tile a:focus-visible+.pn-tile.simple .tile-circle{transform:translate(-50%, -90%);transform:translate(-50%, calc(var(--w) * 2 - 90%));opacity:1}pn-tile .pn-tile .tile-circle{position:absolute;top:0;left:50%;width:40em;height:40em;margin:0;background-color:#005d92;border-radius:50%;z-index:1;transform:translate(-50%, -100%);transition:transform 0.5s cubic-bezier(0.7, 0, 0.3, 1), opacity 0.4s cubic-bezier(0.7, 0, 0.3, 1);opacity:0}pn-tile .pn-ripple{animation:ripple 0.4s cubic-bezier(0.7, 0, 0.3, 1);position:absolute;border-radius:50%;background:#005d92;transform:translate(-50%, -50%) scale(0);opacity:0.05}@keyframes ripple{to{transform:translate(-50%, -50%) scale(1);opacity:0}}";
const PnTileStyle0 = pnTileCss;
const PnTile$1 = /*@__PURE__*/ proxyCustomElement(class PnTile extends HTMLElement {
constructor() {
super();
this.__registerHost();
}
mo;
get hostElement() { return this; }
/** This is to keep track of whether the tile has a description or just a title */
simple = false;
emInPx = 16;
longpress = false;
timer;
/** Headline of the card and label of the link */
label;
/** The SVG content of an illustration. */
illustration;
/** The link of the tile */
url;
/** Force horizontal tile */
horizontal = false;
/** The target attribute of the link */
target;
/** The rel attribute of the link */
rel = 'noopener';
checkCardSize = new ResizeObserver(cards => {
const getCardWidth = (width) => {
// CardWidth is the em equivalent to 10% of the width of the card
const cardWidth = (width * 0.1) / this.emInPx;
return `${cardWidth}em`;
};
cards.forEach(card => {
if (this.horizontal)
return;
const width = card.contentRect.width;
card.target.classList.toggle('vertical', width < this.remToPx(31.99));
requestAnimationFrame(() => {
card.target.style.setProperty('--w', getCardWidth(width));
});
});
});
setIllustration() {
const illustrationElement = this.hostElement.querySelector('.tile-illustration');
if (!this.illustration) {
illustrationElement.remove();
return;
}
const illustration = this.illustration;
illustrationElement.outerHTML = illustration;
}
componentWillLoad() {
const slottedIllustration = this.hostElement.querySelector('[slot="illustration"]');
this.illustration = slottedIllustration ? null : this.illustration;
}
componentDidLoad() {
if (this.mo)
this.mo.disconnect();
this.mo = new MutationObserver(() => forceUpdate(this.hostElement));
this.mo.observe(this.hostElement, { childList: true });
this.setIllustration();
const cardEl = this.hostElement.querySelector('.pn-tile');
const linkEl = this.hostElement.querySelector('.pn-tile-link');
this.checkCardSize.observe(cardEl);
this.clickHandler([linkEl, cardEl], (e) => ripple(e, cardEl));
this.simple = !this.horizontal && !this.hostElement.querySelector('.tile-slot').textContent.trim();
this.emInPx = parseFloat(window.getComputedStyle(this.hostElement).fontSize);
}
remToPx(rem) {
return rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
}
/*---------------------------------------UTILS-------------------------------------------*/
clickHandler(elements, callback) {
const events = ['mouseup', 'mousedown'];
const excludedElements = ['pn-button', 'button', 'a', 'input', 'pn-checkbox', 'pn-radio-button'];
/**
* @description Stop the tile-link from being pressed if the user clicks a link or a button that exists on the tile
* This is to allow the consumer to add external actions to the tile
*/
const isExcluded = (event) => excludedElements.some(element => event.target.closest(element));
/**
* @description If this is a mousdown event, set state longpress to true after 200 milliseconds.
*/
const longPressTrigger = (event) => {
if (event.type === 'mousedown') {
this.longpress = false;
this.timer = setTimeout(() => (this.longpress = true), 200);
}
};
/**
* @description Loop through all elements and attach out event listeners. See `events` array.
*/
const loopAllElements = (element, eventName) => {
element.addEventListener(eventName, (event) => {
if (isExcluded(event))
return;
longPressTrigger(event);
if (event.type === 'mouseup') {
clearTimeout(this.timer);
// Stop clicks in case of:
// * Long press
// * Right mouse button is used
if (this.longpress || event.button === 2)
return;
const link = event.target.closest('pn-tile').querySelector('a');
event.preventDefault();
callback(event);
// Don't refocus if already in focus
if (!link.matches(':focus'))
link.focus();
// If the tile or link was clicked when the ctrl key, cmd key or middle mouse button
// was pressed, open in a new window
if (event.ctrlKey || event.metaKey || event.button === 1) {
window.open(link.getAttribute('href'), '_blank');
return;
}
link.click();
}
});
};
elements.forEach(element => {
if (element.tagName === 'A') {
element.addEventListener('click', event => callback(event));
return;
}
events.forEach(eventName => loopAllElements(element, eventName));
});
}
/*---------------------------------------/UTILS-------------------------------------------*/
getClassNames() {
let classNames = 'pn-tile ';
if (this.simple)
classNames += 'simple ';
if (this.horizontal)
classNames += 'horizontal ';
return classNames;
}
getTextContClassNames() {
let classNames = 'text-cont ';
if (this.horizontal)
classNames += 'horizontal ';
return classNames;
}
render() {
const linkAttributes = {
href: this.url,
target: this.target,
rel: !this.rel && this.target === '_blank' ? 'noopener' : this.rel,
};
return (h(Host, { key: 'ff4eb09a7bbe9b1fb48524adf400e61c137a22e1' }, h("a", { key: '9910f4f0ced66a1fb371610221f98358722256f4', class: "pn-tile-link", ...linkAttributes }, this.label), h("div", { key: '28dfbc275f37318f598d87e7259bc974d6f4e005', class: this.getClassNames() }, h("div", { key: '2a8c55b27898e42100991b2f66c26d1cc0b20360', class: "tile-circle" }), h("div", { key: '9d4c121e2d5fccdd83a1c1d3f84978e8006be483', class: "tile-illustration" }), !this.illustration && (h("div", { key: 'cd5abb9f8faeb36a8d9fad505fd691456bb2f69a', class: "tile-illustration-slot" }, h("slot", { key: 'd3f42654d23b220a9b5f8527e479e07f51c3b1ef', name: "illustration" }))), h("div", { key: '82fe3c9f45e968acb08945ae0a155eb595a57556', class: this.getTextContClassNames() }, h("h3", { key: '127bdf4704f51afb10d2b7057d176798035ac929' }, this.label, this.target === '_blank' ? h("pn-icon", { icon: open_in_new, small: true, color: "blue700" }) : null), h("div", { key: 'd27a02a87e368e88947569d511edee7e27659ed0', class: "tile-slot" }, h("slot", { key: '6a4de1735bc2e720c53afb3274b6ecf13e674a07' }))))));
}
static get style() { return PnTileStyle0; }
}, [4, "pn-tile", {
"label": [1],
"illustration": [1025],
"url": [1],
"horizontal": [4],
"target": [1],
"rel": [1],
"simple": [32],
"emInPx": [32],
"longpress": [32],
"timer": [32],
"checkCardSize": [32]
}]);
function defineCustomElement$1() {
if (typeof customElements === "undefined") {
return;
}
const components = ["pn-tile", "pn-icon"];
components.forEach(tagName => { switch (tagName) {
case "pn-tile":
if (!customElements.get(tagName)) {
customElements.define(tagName, PnTile$1);
}
break;
case "pn-icon":
if (!customElements.get(tagName)) {
defineCustomElement$2();
}
break;
} });
}
const PnTile = PnTile$1;
const defineCustomElement = defineCustomElement$1;
export { PnTile, defineCustomElement };
//# sourceMappingURL=pn-tile.js.map