@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
135 lines (118 loc) • 4.09 kB
text/typescript
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
}
('pkt-loader')
export class PktLoader extends PktElement implements IPktLoader {
defaultSlot: Ref<HTMLElement> = createRef()
constructor() {
super()
this.slotController = new PktSlotController(this, this.defaultSlot)
}
({ type: Number }) delay: number = 0
({ type: Boolean }) inline: boolean = false
({ type: Boolean }) isLoading: boolean = true
({ type: String }) message: string | null = null
({ type: String }) size: TPktSize = 'medium'
({ type: String }) variant: TPktLoaderVariant = 'shapes'
({ type: String }) loadingAnimationPath: string | undefined = window.pktAnimationPath
() 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