@postnord/web-components
Version:
PostNord Web Components
299 lines (298 loc) • 11.8 kB
JavaScript
/*!
* Built with Stencil
* By PostNord.
*/
import { Host, h, forceUpdate } from "@stencil/core";
import { open_in_new } from "pn-design-assets/pn-assets/icons.js";
import { ripple } from "../../../globals/helpers";
/**
* @slot illustration - Set a custom image/svg/illustration.
*/
export class PnTile {
mo;
hostElement;
/** 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 is() { return "pn-tile"; }
static get originalStyleUrls() {
return {
"$": ["pn-tile.scss"]
};
}
static get styleUrls() {
return {
"$": ["pn-tile.css"]
};
}
static get properties() {
return {
"label": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "Headline of the card and label of the link"
},
"getter": false,
"setter": false,
"attribute": "label",
"reflect": false
},
"illustration": {
"type": "string",
"mutable": true,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "The SVG content of an illustration."
},
"getter": false,
"setter": false,
"attribute": "illustration",
"reflect": false
},
"url": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": false,
"docs": {
"tags": [],
"text": "The link of the tile"
},
"getter": false,
"setter": false,
"attribute": "url",
"reflect": false
},
"horizontal": {
"type": "boolean",
"mutable": false,
"complexType": {
"original": "boolean",
"resolved": "boolean",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "Force horizontal tile"
},
"getter": false,
"setter": false,
"attribute": "horizontal",
"reflect": false,
"defaultValue": "false"
},
"target": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "The target attribute of the link"
},
"getter": false,
"setter": false,
"attribute": "target",
"reflect": false
},
"rel": {
"type": "string",
"mutable": false,
"complexType": {
"original": "string",
"resolved": "string",
"references": {}
},
"required": false,
"optional": true,
"docs": {
"tags": [],
"text": "The rel attribute of the link"
},
"getter": false,
"setter": false,
"attribute": "rel",
"reflect": false,
"defaultValue": "'noopener'"
}
};
}
static get states() {
return {
"simple": {},
"emInPx": {},
"longpress": {},
"timer": {},
"checkCardSize": {}
};
}
static get elementRef() { return "hostElement"; }
}
//# sourceMappingURL=pn-tile.js.map