@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
191 lines (174 loc) • 6.29 kB
text/typescript
import { PktElementWithSlot } from '@/base-elements/element-with-slot'
import { slotContent } from '@/directives/slot-content'
import { PktIconName } from '@oslokommune/punkt-assets/dist/icons/icon'
import type { Booleanish, THTMLButtonType } from 'shared-types'
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 '@/components/icon'
// Allow global override of animation assets path
window.pktAnimationPath =
window.pktAnimationPath || 'https://punkt-cdn.oslo.kommune.no/latest/animations/'
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 = THTMLButtonType
export interface IPktButton {
iconName?: PktIconName
secondIconName?: PktIconName
iconPath?: string
secondIconPath?: string
mode?: TPktButtonMode
size?: TPktButtonSize
fullWidth?: Booleanish
fullWidthOnMobile?: Booleanish
color?: TPktButtonColor
skin?: TPktButtonSkin
variant?: TPktButtonVariant
state?: TPktButtonState
type?: TPktButtonType
isLoading?: Booleanish
disabled?: Booleanish
loadingAnimationPath?: string
}
export class PktButton extends PktElementWithSlot<IPktButton> implements IPktButton {
// Properties
iconName: string = 'user'
secondIconName: string = 'user'
iconPath: string | undefined
secondIconPath: string | undefined
mode?: TPktButtonMode = 'light'
size: TPktButtonSize = 'medium'
fullWidth: Booleanish = false
fullWidthOnMobile: Booleanish =
false
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--full': !!this.fullWidth,
'pkt-btn--full-small': !!this.fullWidthOnMobile,
'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}
path=${ifDefined(this.iconPath)}
></pkt-icon>`
: nothing}
<span class="pkt-btn__text">${slotContent(this)}</span>
${this.variant === 'icons-right-and-left'
? html`<pkt-icon
class="pkt-btn__icon"
name=${this.secondIconName}
path=${ifDefined(this.secondIconPath)}
></pkt-icon>`
: nothing}
</button>
`
}
}
export default PktButton
try {
customElement('pkt-button')(PktButton)
} catch (e) {
console.warn('Forsøker å definere <pkt-button>, men den er allerede definert')
}