@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
174 lines (157 loc) • 5.68 kB
text/typescript
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
}
export class PktButton extends PktElement<IPktButton> implements IPktButton {
slotController: PktSlotController
defaultSlot: Ref<HTMLElement> = createRef()
constructor() {
super()
this.slotController = new PktSlotController(this, this.defaultSlot)
}
// Properties
iconName: string = 'user'
secondIconName: string = 'user'
mode?: TPktButtonMode = 'light'
size: TPktButtonSize = 'medium'
color?: TPktButtonColor
skin: TPktButtonSkin = 'primary'
variant: TPktButtonVariant = 'label-only'
state?: TPktButtonState = 'normal'
type: TPktButtonType = 'button'
form: string | undefined = undefined
isLoading: Booleanish = false
disabled: Booleanish = false
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