@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
140 lines (119 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 { PktElement } from '@/base-elements/element'
import { PktSlotController } from '@/controllers/pkt-slot-controller'
import { ref, createRef, Ref } from 'lit/directives/ref.js'
import { TAriaLive } from '@/types/aria'
import { updateClassAttribute } from '@/utils/classutils'
import specs from 'componentSpecs/alert.json'
import '@/components/icon'
export type TAlertSkin = 'error' | 'success' | 'warning' | 'info'
export interface IPktAlert {
skin?: TAlertSkin
closeAlert?: boolean
title?: string
date?: string | null
ariaLive?: TAriaLive | null
'aria-live'?: TAriaLive | null
compact?: boolean
role?: string
}
('pkt-alert')
export class PktAlert extends PktElement implements IPktAlert {
defaultSlot: Ref<HTMLElement> = createRef()
constructor() {
super()
this.slotController = new PktSlotController(this, this.defaultSlot)
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 }) 'aria-live': 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['aria-live'] = (this.getAttribute('aria-live') as TAriaLive) || this.ariaLive
}
attributeChangedCallback(name: string, _old: string | null, value: string | null): void {
if (name === 'ariaLive') {
this['aria-live'] = value as TAriaLive
}
super.attributeChangedCallback(name, _old, value)
}
protected updated(_changedProperties: PropertyValues): void {
super.updated(_changedProperties)
if (_changedProperties.has('ariaLive')) {
this['aria-live'] = 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['aria-live']}>
<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}
>
</pkt-button>
</div>
`
: nothing}
${this.title ? html`<div class="pkt-alert__title">${this.title}</div>` : nothing}
<div class="pkt-alert__text" ${ref(this.defaultSlot)}></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 }),
)
}
}