UNPKG

@oslokommune/punkt-elements

Version:

Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo

140 lines (125 loc) 4.33 kB
import { classMap } from 'lit/directives/class-map.js' import { customElement, property, state } from 'lit/decorators.js' import { html, PropertyValues } from 'lit' import { PktElement } from '@/base-elements/element' import { PktSlotController } from '@/controllers/pkt-slot-controller' import { ref } from 'lit/directives/ref.js' import { Ref, createRef } from 'lit/directives/ref.js' import { TPktSize } from '@/types/size' import specs from 'componentSpecs/tag.json' import '@/components/icon' import { IAriaAttributes } from '@/types/aria' import { PktIconName } from '@oslokommune/punkt-assets/dist/icons/icon' import { ifDefined } from 'lit/directives/if-defined.js' export type TTagSkin = | 'blue' | 'blue-dark' | 'blue-light' | 'green' | 'red' | 'yellow' | 'beige' | 'gray' | 'grey' export type TTagType = 'button' | 'reset' | 'submit' export interface IPktTag { closeTag?: boolean size?: TPktSize skin?: TTagSkin textStyle?: string | null iconName?: PktIconName type?: TTagType ariaLabel?: IAriaAttributes['aria-label'] | null } @customElement('pkt-tag') export class PktTag extends PktElement<IPktTag> implements IPktTag { slotController: PktSlotController defaultSlot: Ref<HTMLElement> = createRef() constructor() { super() this.slotController = new PktSlotController(this, this.defaultSlot) this._isClosed = false } /** * Element attributes */ @property({ type: Boolean, reflect: true }) closeTag: boolean = specs.props.closeTag.default @property({ type: String, reflect: true }) size: TPktSize = specs.props.size.default as TPktSize @property({ type: String, reflect: true }) skin: TTagSkin = specs.props.skin.default as TTagSkin @property({ type: String, reflect: true }) textStyle: string | null = null @property({ type: String, reflect: true }) iconName: string | undefined = undefined @property({ type: String }) type: TTagType = specs.props.type.default as TTagType @property({ type: String }) ariaLabel: string | null = null /** * Element state */ @state() _isClosed: boolean = false @state() _ariaDescription: string | null = null /** * Lifecycle */ protected firstUpdated(_changedProperties: PropertyValues): void { super.firstUpdated(_changedProperties) if (this.closeTag && !this.ariaLabel) { const label = this.defaultSlot.value?.textContent?.trim() if (label) { this._ariaDescription = `Klikk for å fjerne ${label}` } } } /** * Element functions */ 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 }), ) } render() { const classes = { 'pkt-tag': true, [`pkt-tag--${this.size}`]: !!this.size, [`pkt-tag--${this.skin}`]: !!this.skin, [`pkt-tag--${this.textStyle}`]: !!this.textStyle, } const btnClasses = { 'pkt-tag': true, 'pkt-btn': true, 'pkt-btn--tertiary': true, [`pkt-tag--${this.textStyle}`]: !!this.textStyle, [`pkt-tag--${this.size}`]: !!this.size, [`pkt-tag--${this.skin}`]: !!this.skin, 'pkt-btn--icons-right-and-left': this.closeTag && !!this.iconName, 'pkt-hide': this._isClosed, } if (this.closeTag) { return html` <button class=${classMap(btnClasses)} type=${this.type} @click=${this.close} aria-label=${ifDefined(this.ariaLabel || undefined)} aria-description=${ifDefined(this._ariaDescription || undefined)} > ${this.iconName && html`<pkt-icon class="pkt-tag__icon" name=${this.iconName}></pkt-icon>`} <span ${ref(this.defaultSlot)}></span> <pkt-icon class="pkt-tag__close-btn" name="close"></pkt-icon> </button> ` } else { return html` <span class=${classMap(classes)}> ${this.iconName && html`<pkt-icon class="pkt-tag__icon" name=${this.iconName}></pkt-icon>`} <span ${ref(this.defaultSlot)}></span> </span> ` } } }