UNPKG

@oslokommune/punkt-elements

Version:

Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo

135 lines (118 loc) 4.09 kB
import { PktElement } from '@/base-elements/element' import { classMap } from 'lit/directives/class-map.js' import { customElement, property, state } from 'lit/decorators.js' import { createRef, ref, Ref } from 'lit/directives/ref.js' import { html, nothing } from 'lit' import { PktSlotController } from '@/controllers/pkt-slot-controller' import { TPktSize } from '@/types/size' import '@/components/icon' // Allow global override of animation assets path window.pktAnimationPath = window.pktAnimationPath || 'https://punkt-cdn.oslo.kommune.no/latest/animations/' export type TPktLoaderVariant = 'blue' | 'rainbow' | 'shapes' export interface IPktLoader { /** * The `delay` prop controls how much time the loading should be given before the loader is displayed. * This is handy for situations where the load time might be so short that loader is not necessary. * Delay time is in milliseconds. */ delay?: number /** * The `inline` prop decides whether the loader should be displayed inline or not. */ inline?: boolean /** * The boolean 'isLoading' decides whether the loader or the children will be displayed. * If set to false, the children will be displayed. */ isLoading?: boolean /** * The message to display when the loader is loading. */ message?: string | null /** * The size of the loader. Default is "medium". */ size?: TPktSize /** * The variant of the loader. Default is "shapes" which is the OSLO wave loader. * Other variants are "blue" and "rainbow" which are spinner variants. */ variant?: TPktLoaderVariant /** * Override path to loading animations. */ loadingAnimationPath?: string } @customElement('pkt-loader') export class PktLoader extends PktElement implements IPktLoader { defaultSlot: Ref<HTMLElement> = createRef() constructor() { super() this.slotController = new PktSlotController(this, this.defaultSlot) } @property({ type: Number }) delay: number = 0 @property({ type: Boolean }) inline: boolean = false @property({ type: Boolean }) isLoading: boolean = true @property({ type: String }) message: string | null = null @property({ type: String }) size: TPktSize = 'medium' @property({ type: String }) variant: TPktLoaderVariant = 'shapes' @property({ type: String }) loadingAnimationPath: string | undefined = window.pktAnimationPath @state() private _shouldDisplayLoader: boolean = false connectedCallback() { super.connectedCallback() this._shouldDisplayLoader = this.delay === 0 if (this.delay > 0) { this.setupLoader() } } updated(changedProperties: Map<string, unknown>) { if (changedProperties.has('delay')) { this.setupLoader() } } render() { const classes = classMap({ 'pkt-loader': true, [`pkt-loader--${this.inline ? 'inline' : 'box'}`]: true, [`pkt-loader--${this.size}`]: true, }) const slotClasses = classMap({ 'pkt-contents': true, 'pkt-hide': this.isLoading, }) return html`<div role="status" aria-live="polite" .aria-busy=${this.isLoading} class=${classes}> ${this.isLoading && this._shouldDisplayLoader ? html`<div class="pkt-loader__spinner"> <pkt-icon name=${this.getVariant(this.variant)} path=${this.loadingAnimationPath} class="pkt-loader__svg pkt-loader__${this.variant}" ></pkt-icon> ${this.message && html`<p>${this.message}</p>`} </div>` : nothing} <div class=${slotClasses} ${ref(this.defaultSlot)}></div> </div>` } private getVariant(variant: TPktLoaderVariant): string { switch (variant) { case 'blue': return 'spinner-blue' case 'rainbow': return 'spinner' default: return 'loader' } } private setupLoader() { if (this.delay > 0) { this._shouldDisplayLoader = false setTimeout(() => { this._shouldDisplayLoader = true this.requestUpdate() }, this.delay) } } } export default PktLoader