@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
110 lines (96 loc) • 3.44 kB
text/typescript
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'>
('pkt-icon')
export class PktIcon extends PktElement<Props> {
({ type: String, reflect: false })
path: string | undefined = window.pktIconPath
({ type: String, reflect: true })
name: PktIconName = ''
({ type: SVGElement })
private icon: any = unsafeSVG(errorSvg)
({ 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
}
}