@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
210 lines (187 loc) • 7.07 kB
text/typescript
import { customElement, property } from 'lit/decorators.js'
import { PktInputElement } from '@/base-elements/input-element'
import { Ref, createRef, ref } from 'lit/directives/ref.js'
import { html, nothing, PropertyValues } from 'lit'
import { classMap } from 'lit/directives/class-map.js'
import { ElementProps } from '@/types/typeUtils'
type Props = ElementProps<
PktCheckbox,
| 'checkHelptext'
| 'defaultChecked'
| 'hasTile'
| 'isSwitch'
| 'labelPosition'
| 'hideLabel'
| 'tagText'
| 'optionalTag'
| 'optionalText'
| 'requiredTag'
| 'requiredText'
>
export class PktCheckbox extends PktInputElement<Props> {
inputRef: Ref<HTMLInputElement> = createRef()
value: string = ''
checkHelptext: string | null = null
defaultChecked: boolean = false
hasTile: boolean = false
isSwitch: boolean = false
labelPosition: 'right' | 'left' = 'right'
hideLabel: boolean = false
checked: boolean | string | null = null
indeterminate: boolean | 'true' | 'false' | '' = false
type: string = 'checkbox'
tagText: string | null = null
optionalTag: boolean = false
optionalText: string = 'Valgfritt'
requiredTag: boolean = false
requiredText: string = 'Må fylles ut'
connectedCallback() {
super.connectedCallback()
}
attributeChangedCallback(name: string, _old: string | null, value: string | null): void {
if (name === 'defaultChecked' && !this.checked) {
this.checked = this.defaultChecked
}
if (name === 'checked') {
this.checked = this.checked === '' || this.checked === 'true' || this.checked === true
if (this.inputRef.value) this.inputRef.value.checked = this.checked as boolean
}
if (name === 'indeterminate') {
this.indeterminate =
this.indeterminate === '' || this.indeterminate === 'true' || this.indeterminate === true
if (this.inputRef.value) this.inputRef.value.indeterminate = this.indeterminate as boolean
}
super.attributeChangedCallback(name, _old, value)
}
protected firstUpdated(_changedProperties: PropertyValues): void {
if (_changedProperties.has('defaultChecked') && !this.checked) {
this.checked = this.defaultChecked
}
if (this.inputRef.value) {
this.inputRef.value.indeterminate =
this.indeterminate === '' || this.indeterminate === 'true' || this.indeterminate === true
}
super.firstUpdated(_changedProperties)
}
protected updated(changedProperties: PropertyValues): void {
if (changedProperties.has('defaultChecked') && !this.checked) {
this.checked = this.defaultChecked
}
if (changedProperties.has('checked') && this.inputRef.value) {
this.inputRef.value.checked =
this.checked === '' || this.checked === 'true' || this.checked === true
}
if (changedProperties.has('indeterminate') && this.inputRef.value) {
this.inputRef.value.indeterminate =
this.indeterminate === '' || this.indeterminate === 'true' || this.indeterminate === true
}
super.updated(changedProperties)
}
render() {
const inputTileClasses = classMap({
'pkt-input-check__input': true,
'pkt-input-check__input--tile': this.hasTile,
'pkt-input-check__input--tile-disabled': this.disabled && this.hasTile,
})
const inputCheckBoxClasses = classMap({
'pkt-input-check__input-checkbox': true,
'pkt-input-check__input-checkbox--error': this.hasError,
})
const labelClasses = classMap({
'pkt-input-check__input-label': true,
'pkt-input-check__input-label--disabled': this.disabled,
'pkt-input-check__input-label--left': this.labelPosition === 'left',
'pkt-input-check__input-label--right': this.labelPosition === 'right',
'pkt-sr-only': this.hideLabel,
})
const tagClasses = 'pkt-tag pkt-tag--small pkt-tag--thin-text'
const tags = () => {
return html`
${this.tagText
? html`<span class=${tagClasses + ' pkt-tag--gray'}>${this.tagText}</span>`
: nothing}
${this.optionalTag
? html`<span class=${tagClasses + ' pkt-tag--blue-light'}>${this.optionalText}</span>`
: nothing}
${this.requiredTag
? html`<span class=${tagClasses + ' pkt-tag--beige'}>${this.requiredText}</span>`
: nothing}
`
}
const labelAndHelptext = () => {
return html`
<label class=${labelClasses} for=${this.id + '-internal'}>
${this.label} ${tags()}
${this.checkHelptext
? html`<div class="pkt-input-check__input-helptext">${this.checkHelptext}</div>`
: nothing}
</label>
`
}
return html`
<div class="pkt-input-check">
<div class=${inputTileClasses}>
${this.labelPosition === 'left' ? labelAndHelptext() : nothing}
<input
id=${this.id + '-internal'}
class=${inputCheckBoxClasses}
type="checkbox"
?disabled=${this.disabled}
name=${this.name + '-internal'}
${ref(this.inputRef)}
=${this.handleChange}
=${this.handleClick}
=${this.onBlur}
=${this.onFocus}
?checked=${this.checked}
role=${this.isSwitch ? 'switch' : 'checkbox'}
/>
${this.labelPosition === 'right' ? labelAndHelptext() : nothing}
</div>
</div>
`
}
private handleClick(e: Event) {
// Prevent click on disabled checkbox
if (this.disabled) {
e.preventDefault()
e.stopImmediatePropagation()
return false
}
}
private handleChange(e: Event) {
// Don't process change if disabled
if (this.disabled) {
e.preventDefault()
e.stopImmediatePropagation()
return false
}
this.toggleChecked(e)
}
private toggleChecked(e: Event) {
// Don't toggle if disabled
if (this.disabled) {
e.preventDefault()
e.stopImmediatePropagation()
return
}
// Also check if the input element itself is disabled
const target = e.target as HTMLInputElement
if (target && target.disabled) {
e.preventDefault()
e.stopImmediatePropagation()
return
}
e.stopImmediatePropagation()
this.touched = true
if (this.inputRef.value) {
this.checked = this.inputRef.value.matches(':checked')
this.valueChecked(this.checked)
}
}
}
try {
customElement('pkt-checkbox')(PktCheckbox)
} catch (e) {
console.warn('Forsøker å definere <pkt-checkbox>, men den er allerede definert')
}