UNPKG

@oslokommune/punkt-elements

Version:

Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo

110 lines (96 loc) 3.44 kB
import { PktElement } from '@/base-elements/element' import { ElementProps } from '@/types/typeUtils' import { PktIconName } from '@oslokommune/punkt-assets/dist/icons/icon' import { html, PropertyValues } from 'lit' import { customElement, property } from 'lit/decorators.js' import { unsafeSVG } from 'lit/directives/unsafe-svg.js' // Allow global override of icon fetch window.pktFetch = window.pktFetch === undefined ? fetch : window.pktFetch window.pktIconPath = window.pktIconPath || 'https://punkt-cdn.oslo.kommune.no/latest/icons/' const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) const errorSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"></svg>' const dlStatus: { [key: string]: string } = {} const downloadIconOrGetFromCache = async ( name: PktIconName, path: string | undefined, ): Promise<string | null> => { let i = 0 while (dlStatus[path + name + '.svg'] === 'fetching') { i++ if (i > 50) break await sleep(50) } if (localStorage.getItem(path + name + '.svg')) { return Promise.resolve(localStorage.getItem(path + name + '.svg')) } else if (typeof window.pktFetch === 'function') { dlStatus[path + name + '.svg'] = 'fetching' return Promise.resolve( window .pktFetch(path + name + '.svg') .then((response) => { if (!response.ok) { // eslint-disable-next-line no-console console.error('Missing icon: ' + path + name + '.svg') return errorSvg } else { return response.text() } }) .then((text) => { if (text !== errorSvg) { localStorage.setItem(path + name + '.svg', text) } dlStatus[path + name + '.svg'] = 'fetched' return text }), ) } return Promise.resolve(errorSvg) } type Props = ElementProps<PktIcon, 'path' | 'name'> @customElement('pkt-icon') export class PktIcon extends PktElement<Props> { @property({ type: String, reflect: false }) path: string | undefined = window.pktIconPath @property({ type: String, reflect: true }) name: PktIconName = '' @property({ type: SVGElement }) private icon: any = unsafeSVG(errorSvg) @property({ type: Array, noAccessor: true }) private _updatedProps: string[] = [] connectedCallback(): void { super.connectedCallback() this.classList.add('pkt-icon') } async attributeChangedCallback(name: string, _old: string | null, value: string | null) { super.attributeChangedCallback(name, _old, value) if (name === 'name' || name === 'path') this.getIcon(this.name) } protected async updated(_changedProperties: PropertyValues) { super.updated(_changedProperties) if (_changedProperties.has('name') || _changedProperties.has('path')) { this.getIcon(this.name) } } protected async getIcon(name: PktIconName = '') { if (this._updatedProps.length > 0) { if (!this.path) this.path === window.pktIconPath this.icon = unsafeSVG( await downloadIconOrGetFromCache(this.name || '', this.path).then((res) => res), ) as SVGElement this._updatedProps = [] } else { if (!this._updatedProps.includes(name)) { this._updatedProps.push(name) } } } render() { return html`${this.name && this.icon}` } } declare global { interface HTMLElementTagNameMap { 'pkt-icon': PktIcon } }