/** * @license * Copyright 2019 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ import {LitElement, html, css, unsafeCSS} from 'lit'; import {customElement, property} from 'lit/decorators.js'; import { breakpoints } from '../../breakpoints'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; /** * An example element. * * @fires count-changed - Indicates when the count changes * @slot - This element has a slot * @csspart button - The button */ @customElement('iu-featured-content') export class FeaturedContent extends LitElement { static override styles = css` :host{ display: block; background: var(--iu-color-black); color: #fff; padding-top: 2.5rem /* 40px */; padding-bottom: 2.5rem /* 40px */; margin-bottom: 4.5rem; } :host iu-heading::part(title){ color: #fff; } :host iu-heading::part(link){ color: var(--iu-color-grey-400); } ::slotted(p){ font-size: 1.625rem !important; line-height: 2.1875rem !important; color: #fff !important; } .img{ aspect-ratio: 3/2; margin-bottom: 2rem; overflow: hidden; } @media ${unsafeCSS(breakpoints.md)} { .img{ aspect-ratio: 2/1; } } @media ${unsafeCSS(breakpoints.xl)} { .img{ aspect-ratio: 21/9; } } .img img{ width: 100%; } .text *{ font: var(--iu-f-md); } `; @property() heading = ''; @property() link = ''; @property() href = ''; @property() img = ''; // @TODO check if this is safe @property({ type: String, converter: (value: string | null) => { if (!value) return ''; // Create a new DOMParser const parser = new DOMParser(); // Parse the HTML string const doc = parser.parseFromString(value, 'text/html'); // Remove potentially dangerous elements and attributes const sanitize = (document: Document) => { // Only allow specific safe tags const safeTags = ['p', 'b', 'i', 'em', 'strong', 'span', 'br']; // Remove unsafe elements Array.from(document.body.getElementsByTagName('*')).forEach(el => { if (!safeTags.includes(el.tagName.toLowerCase())) { el.remove(); } // Remove all attributes except class Array.from(el.attributes).forEach(attr => { if (attr.name !== 'class') { el.removeAttribute(attr.name); } }); }); return document.body.innerHTML; }; return sanitize(doc); }}) text? = ''; override render() { return html` <iu-container> <iu-heading text="${this.heading}" noDivider nested href="${this.href}" size="2" link="${this.link}"></iu-heading> <div class="img"><img src="${this.img}" /></div> ${this.text && html`<div class="text">${unsafeHTML(this.text)}</div>`} </iu-container> `; } } declare global { interface HTMLElementTagNameMap { 'iu-featured-content': FeaturedContent; } }