@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
183 lines (172 loc) • 6.7 kB
text/typescript
import { html, nothing, PropertyValues } from 'lit'
import { ifDefined } from 'lit/directives/if-defined.js'
import { customElement, property, state } from 'lit/decorators.js'
import { Ref, createRef, ref } from 'lit/directives/ref.js'
import { ElementProps } from '@/types/typeUtils'
import { classMap } from 'lit/directives/class-map.js'
import { PktInputElement } from '@/base-elements/input-element'
import { PktSlotController } from '@/controllers/pkt-slot-controller'
import '@/components/input-wrapper'
import '@/components/icon'
type Props = ElementProps<
PktTextinput,
| 'value'
| 'type'
| 'size'
| 'autocomplete'
| 'iconNameRight'
| 'prefix'
| 'suffix'
| 'omitSearchIcon'
>
export class PktTextinput extends PktInputElement<Props> {
private inputRef: Ref<HTMLTextAreaElement> = createRef()
private helptextSlot: Ref<HTMLElement> = createRef()
value: string = ''
type: string = 'text'
size: number | null = null
autocomplete: string | null = null
iconNameRight: string | null = null
prefix: string | null = null
suffix: string | null = null
omitSearchIcon: boolean = false
counterCurrent = 0
constructor() {
super()
this.slotController = new PktSlotController(this, this.helptextSlot)
}
attributeChangedCallback(name: string, _old: string | null, value: string | null): void {
if (name === 'value' && this.value !== _old) {
this.counterCurrent = value ? value.length : 0
this.valueChanged(value, _old)
}
super.attributeChangedCallback(name, _old, value)
}
updated(changedProperties: PropertyValues) {
super.updated(changedProperties)
if (changedProperties.has('value')) {
this.counterCurrent = this.value?.length || 0
this.valueChanged(this.value, changedProperties.get('value'))
}
if (changedProperties.has('id')) {
!this.name && this.id && (this.name = this.id)
}
}
render() {
const shouldShowSearchIcon =
this.type === 'search' && !this.iconNameRight && !this.omitSearchIcon
const inputClasses = classMap({
'pkt-input': true,
'pkt-input--fullwidth': this.fullwidth,
'pkt-input--counter-error':
this.counter &&
this.counterMaxLength &&
this.value.length &&
this.value.length > this.counterMaxLength,
})
const labelledBy = this.ariaLabelledby || `${this.id}-input-label`
return html`
<pkt-input-wrapper
label="${this.label}"
?counter=${this.counter}
?disabled=${this.disabled}
?hasError=${this.hasError}
?hasFieldset=${this.hasFieldset}
?inline=${this.inline}
?optionalTag=${this.optionalTag}
?required=${this.required}
?requiredTag=${this.requiredTag}
?useWrapper=${this.useWrapper}
.ariaDescribedBy=${this.ariaDescribedBy}
.counterCurrent=${this.counterCurrent}
.counterMaxLength=${this.counterMaxLength}
.errorMessage=${this.errorMessage}
.forId="${this.id + '-input'}"
.helptext=${this.helptext}
.helptextDropdown=${this.helptextDropdown}
.helptextDropdownButton=${this.helptextDropdownButton}
.optionalText=${this.optionalText}
.requiredText=${this.requiredText}
.tagText=${this.tagText}
class="pkt-textinput"
>
<div class="pkt-contents" ${ref(this.helptextSlot)} name="helptext" slot="helptext"></div>
<div class="pkt-input__container">
${this.prefix ? html`<div class="pkt-input-prefix">${this.prefix}</div>` : nothing}
<input
${ref(this.inputRef)}
class=${inputClasses}
type=${this.type}
name=${(this.name || this.id) + '-input'}
id=${this.id + '-input'}
placeholder=${ifDefined(this.placeholder)}
aria-labelledby=${labelledBy}
autocomplete=${this.autocomplete || 'off'}
minlength=${ifDefined(this.minlength || undefined)}
maxlength=${ifDefined(this.maxlength || undefined)}
min=${ifDefined(this.min || undefined)}
max=${ifDefined(this.max || undefined)}
step=${ifDefined(this.step || undefined)}
?readonly=${this.readonly}
size=${this.size || nothing}
.value=${this.value}
?disabled=${this.disabled}
aria-invalid=${this.hasError}
aria-errormessage=${`${this.id}-error`}
=${(e: InputEvent) => {
this.value = (e.target as HTMLInputElement).value
this.onInput()
e.stopImmediatePropagation()
}}
=${(e: Event) => {
e.stopImmediatePropagation()
}}
=${(e: FocusEvent) => {
this.onFocus()
e.stopImmediatePropagation()
}}
=${(e: FocusEvent) => {
this.value = (e.target as HTMLInputElement).value
this.onBlur()
e.stopImmediatePropagation()
}}
=${(e: KeyboardEvent) => {
if (e.key === 'Enter') {
const form = this.internals.form as HTMLFormElement
if (form) {
form.requestSubmit()
} else {
this.inputRef.value?.blur()
}
}
}}
/>
${this.suffix
? html`<div class="pkt-input-suffix">
${this.suffix}
${this.iconNameRight
? html`<pkt-icon
class="pkt-input-suffix-icon"
name=${this.iconNameRight}
></pkt-icon>`
: nothing}
${shouldShowSearchIcon
? html`<pkt-icon
class="pkt-input-suffix-icon"
name="magnifying-glass-big"
></pkt-icon>`
: nothing}
</div>`
: nothing}
${!this.suffix && this.iconNameRight
? html`<pkt-icon class="pkt-input-icon" name=${this.iconNameRight}></pkt-icon>`
: nothing}
${!this.suffix && shouldShowSearchIcon
? html`<pkt-icon class="pkt-input-icon" name="magnifying-glass-big"></pkt-icon>`
: nothing}
</div>
</pkt-input-wrapper>
`
}
}