UNPKG

@oslokommune/punkt-elements

Version:

Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo

174 lines (157 loc) 5.68 kB
import { PktElement } from '@/base-elements/element' import { PktSlotController } from '@/controllers/pkt-slot-controller' import { PktIconName } from '@oslokommune/punkt-assets/dist/icons/icon' import { html, nothing, PropertyValues } from 'lit' import { property, customElement } from 'lit/decorators.js' import { classMap } from 'lit/directives/class-map.js' import { ifDefined } from 'lit/directives/if-defined.js' import { createRef, Ref, ref } from 'lit/directives/ref.js' // Allow global override of animation assets path window.pktAnimationPath = window.pktAnimationPath || 'https://punkt-cdn.oslo.kommune.no/latest/animations/' type Booleanish = boolean | 'true' | 'false' export type TPktButtonMode = 'light' | 'dark' export type TPktButtonSize = 'small' | 'medium' | 'large' export type TPktButtonColor = | 'blue' | 'blue-outline' | 'green' | 'green-outline' | 'green-dark' | 'green-dark-outline' | 'beige-light' | 'beige-dark-outline' | 'yellow' | 'yellow-outline' | 'red' | 'red-outline' export type TPktButtonSkin = 'primary' | 'secondary' | 'tertiary' export type TPktButtonVariant = | 'label-only' | 'icon-left' | 'icon-right' | 'icon-only' | 'icons-right-and-left' export type TPktButtonState = 'normal' | 'focus' | 'hover' | 'active' export type TPktButtonType = 'button' | 'submit' | 'reset' export interface IPktButton { iconName?: PktIconName secondIconName?: PktIconName mode?: TPktButtonMode size?: TPktButtonSize color?: TPktButtonColor skin?: TPktButtonSkin variant?: TPktButtonVariant state?: TPktButtonState type?: TPktButtonType isLoading?: Booleanish disabled?: Booleanish loadingAnimationPath?: string } @customElement('pkt-button') export class PktButton extends PktElement<IPktButton> implements IPktButton { slotController: PktSlotController defaultSlot: Ref<HTMLElement> = createRef() constructor() { super() this.slotController = new PktSlotController(this, this.defaultSlot) } // Properties @property({ type: String }) iconName: string = 'user' @property({ type: String }) secondIconName: string = 'user' @property({ type: String }) mode?: TPktButtonMode = 'light' @property({ type: String }) size: TPktButtonSize = 'medium' @property({ type: String }) color?: TPktButtonColor @property({ type: String }) skin: TPktButtonSkin = 'primary' @property({ type: String }) variant: TPktButtonVariant = 'label-only' @property({ type: String, reflect: true }) state?: TPktButtonState = 'normal' @property({ type: String, reflect: true }) type: TPktButtonType = 'button' @property({ type: String }) form: string | undefined = undefined @property({ type: Boolean, reflect: true }) isLoading: Booleanish = false @property({ type: Boolean, reflect: true }) disabled: Booleanish = false @property({ type: String }) loadingAnimationPath: string | undefined = window.pktAnimationPath // Lifecycle connectedCallback(): void { super.connectedCallback() this.addEventListener( 'click', (e) => { if (this.disabled || this.hasAttribute('disabled') || this.isLoading) { e.preventDefault() e.stopImmediatePropagation() } }, true, ) this.addEventListener( 'keydown', (e) => { if (!(this.disabled || this.hasAttribute('disabled') || this.isLoading)) return if (e.key === 'Enter' || e.key === ' ') { e.preventDefault() e.stopImmediatePropagation() } }, true, ) } attributeChangedCallback(name: string, _old: string | null, value: string | null): void { super.attributeChangedCallback(name, _old, value) // Convert strings to booleans if (name === 'disabled' && value === 'false') { this.disabled = false } if ((name === 'isloading' || name === 'isLoading') && value === 'false') { this.isLoading = false } } protected firstUpdated(_changedProperties: PropertyValues): void { super.firstUpdated(_changedProperties) if (this.disabled === 'false') { this.disabled = false } if (this.isLoading === 'false') { this.isLoading = false } } // Render render() { const formId = this.form ?? this.getAttribute('form') ?? undefined const classes = { 'pkt-btn': true, [`pkt-btn--${this.size}`]: !!this.size, [`pkt-btn--${this.skin}`]: !!this.skin, [`pkt-btn--${this.variant}`]: !!this.variant, [`pkt-btn--${this.color}`]: !!this.color, [`pkt-btn--${this.state}`]: !!this.state, 'pkt-btn--disabled': !!this.disabled, 'pkt-btn--isLoading': !!this.isLoading, } return html` <button class=${classMap(classes)} type=${this.type} ?disabled=${!!this.disabled} aria-busy=${ifDefined(this.isLoading ? 'true' : undefined)} aria-disabled=${ifDefined(this.disabled || this.isLoading ? 'true' : undefined)} form=${ifDefined(formId)} > ${this.isLoading ? html`<pkt-icon class="pkt-btn__icon pkt-btn__spinner" name="spinner-blue" path=${ifDefined(this.loadingAnimationPath)} ></pkt-icon>` : nothing} ${this.variant !== 'label-only' ? html`<pkt-icon class="pkt-btn__icon pkt-icon" name=${this.iconName}></pkt-icon>` : nothing} <span class="pkt-btn__text" ${ref(this.defaultSlot)}></span> ${this.variant === 'icons-right-and-left' ? html`<pkt-icon class="pkt-btn__icon" name=${this.secondIconName}></pkt-icon>` : nothing} </button> ` } } export default PktButton