@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
152 lines (128 loc) • 3.86 kB
text/typescript
import { html, TemplateResult } from 'lit'
import { property } from 'lit/decorators.js'
import { Ref, createRef } from 'lit/directives/ref.js'
import { classMap } from 'lit/directives/class-map.js'
import { PktElement } from '@/base-elements/element'
import { cssUtils } from './datepicker-utils'
import { IDatepickerStrings } from './datepicker-types'
import '@/components/icon'
/**
* Abstract base class for datepicker input components
*
* Consolidates shared properties, methods, and event dispatchers
* used by all three datepicker input types (single, multiple, range).
*
* Subclasses must implement:
* - `strings` property with component-specific defaults
* - `render()` method with component-specific template
*/
export abstract class PktDatepickerBase extends PktElement {
// Shared properties (9 identical across all sub-components)
({ type: String })
inputType: string = 'date'
({ type: String })
id: string = ''
({ type: String })
min?: string
({ type: String })
max?: string
({ type: String })
placeholder?: string
({ type: Boolean })
readonly: boolean = false
({ type: Boolean })
disabled: boolean = false
({ type: Object })
inputClasses: Record<string, boolean> = {}
({ type: Object })
internals?: ElementInternals
// Abstract property - must be implemented by subclasses
abstract strings: IDatepickerStrings
// Shared refs
inputRef: Ref<HTMLInputElement> = createRef()
btnRef: Ref<HTMLButtonElement> = createRef()
// Shared getters
get inputElement(): HTMLInputElement | undefined {
return this.inputRef.value
}
get buttonElement(): HTMLButtonElement | undefined {
return this.btnRef.value
}
get isInputReadonly(): boolean {
return this.readonly || this.inputType === 'text'
}
// Shared event dispatchers (protected so subclasses can use them)
protected dispatchToggleCalendar(e: Event): void {
if (this.readonly) return
this.dispatchEvent(
new CustomEvent('toggle-calendar', {
detail: e,
bubbles: true,
composed: true,
}),
)
}
protected dispatchInput(e: Event): void {
this.dispatchEvent(
new CustomEvent('input-change', {
detail: e,
bubbles: true,
composed: true,
}),
)
}
protected dispatchFocus(): void {
this.dispatchEvent(
new CustomEvent('input-focus', {
bubbles: true,
composed: true,
}),
)
}
protected dispatchBlur(e: FocusEvent): void {
this.dispatchEvent(
new CustomEvent('input-blur', {
detail: e,
bubbles: true,
composed: true,
}),
)
}
protected dispatchChange(e: Event): void {
this.dispatchEvent(
new CustomEvent('input-changed', {
detail: e,
bubbles: true,
composed: true,
}),
)
}
// Shared render helper for calendar button
protected renderCalendarButton(): TemplateResult {
return html`
<button
class="${classMap(cssUtils.getButtonClasses())}"
type="button"
@click=${(e: Event) => this.dispatchToggleCalendar(e)}
@keydown=${(e: KeyboardEvent) => {
const { key } = e
if (key === 'Enter' || key === ' ' || key === 'Space') {
e.preventDefault()
this.dispatchToggleCalendar(e)
}
}}
?disabled=${this.disabled}
${this.btnRef}
>
<pkt-icon name="calendar"></pkt-icon>
<span class="pkt-btn__text">${this.strings.calendar?.buttonAltText || 'Åpne kalender'}</span>
</button>
`
}
// Shared method - no shadow DOM
createRenderRoot() {
return this
}
// Abstract render method - must be implemented by subclasses
abstract render(): TemplateResult
}