@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
143 lines (122 loc) • 4.48 kB
text/typescript
import { classMap } from 'lit/directives/class-map.js'
import { customElement, property, state } from 'lit/decorators.js'
import { html, nothing, PropertyValues } from 'lit'
import { PktElementWithSlot } from '@/base-elements/element-with-slot'
import { slotContent } from '@/directives/slot-content'
import type { TAriaLive, TAlertSkin } from 'shared-types'
import { updateClassAttribute } from '@/utils/classutils'
import specs from 'componentSpecs/alert.json'
import '@/components/icon'
import '@/components/button'
export type { TAlertSkin }
export interface IPktAlert {
skin?: TAlertSkin
closeAlert?: boolean
title?: string
date?: string | null
ariaLive?: TAriaLive | null
compact?: boolean
role?: string
}
export class PktAlert extends PktElementWithSlot implements IPktAlert {
constructor() {
super()
this._isClosed = false
}
// Properties
({ type: Boolean, reflect: false }) compact = specs.props.compact.default
({ type: String, reflect: true }) title: string = ''
({ type: String, reflect: true }) skin: TAlertSkin = specs.props.skin
.default as TAlertSkin
({ type: String }) ariaLive: TAriaLive = specs.props.ariaLive.default as TAriaLive
({ type: String, reflect: true, attribute: 'aria-live' })
ariaLiveAttr: TAriaLive | null = null
({ type: Boolean, reflect: true }) closeAlert = specs.props.closeAlert.default
({ type: String, reflect: true }) date: string | null = null
({ type: String, reflect: true }) role: string = 'status'
() _isClosed: boolean = false
// Lifecycle
connectedCallback(): void {
super.connectedCallback()
this.ariaLiveAttr = (this.getAttribute('aria-live') as TAriaLive) || this.ariaLive
}
attributeChangedCallback(name: string, _old: string | null, value: string | null): void {
if (name === 'ariaLive') {
this.ariaLiveAttr = value as TAriaLive
}
super.attributeChangedCallback(name, _old, value)
}
protected updated(_changedProperties: PropertyValues): void {
super.updated(_changedProperties)
if (_changedProperties.has('ariaLive')) {
this.ariaLiveAttr = this.ariaLive
}
if (_changedProperties.has('_isClosed')) {
updateClassAttribute(this, 'pkt-hide', this._isClosed)
}
}
// Render
render() {
const classes = {
'pkt-alert': true,
'pkt-alert--compact': this.compact,
[`pkt-alert--${this.skin}`]: this.skin,
'pkt-hide': this._isClosed,
}
const gridClasses = {
'pkt-alert__grid': true,
'pkt-alert__noTitle': !this.title,
'pkt-alert__noDate': !this.date,
}
return html`
<div class=${classMap(classes)} aria-live=${this.ariaLiveAttr}>
<div class=${classMap(gridClasses)}>
<pkt-icon
class="pkt-alert__icon"
aria-hidden="true"
name=${this.skin === 'info' ? 'alert-information' : `alert-${this.skin}`}
></pkt-icon>
${this.closeAlert
? html`
<div class="pkt-alert__close">
<pkt-button
tabindex="0"
aria-label="close"
size=${this.compact ? 'small' : 'medium'}
type="button"
skin="tertiary"
iconName="close"
variant="icon-only"
@click=${this.close}
>
<span class="sr-only">Lukk</span>
</pkt-button>
</div>
`
: nothing}
${this.title ? html`<div class="pkt-alert__title">${this.title}</div>` : nothing}
<div class="pkt-alert__text">${slotContent(this)}</div>
${this.date
? html`<div class="pkt-alert__date">Sist oppdatert: ${this.date}</div>`
: nothing}
</div>
</div>
`
}
// Methods
private close = (event: MouseEvent) => {
this._isClosed = true
this.dispatchEvent(
new CustomEvent('close', { detail: { origin: event }, bubbles: true, composed: true }),
)
// Historical support of old Vue implementations…
this.dispatchEvent(
new CustomEvent('on-close', { detail: { origin: event }, bubbles: true, composed: true }),
)
}
}
try {
customElement('pkt-alert')(PktAlert)
} catch (e) {
console.warn('Forsøker å definere <pkt-alert>, men den er allerede definert')
}