UNPKG

@oslokommune/punkt-elements

Version:

Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo

186 lines (162 loc) 6.42 kB
import { html, nothing, PropertyValues } from 'lit' import { PktElement } from '@/base-elements/element' import { customElement, property, state } from 'lit/decorators.js' import { classMap } from 'lit/directives/class-map.js' import { ref, createRef, Ref } from 'lit/directives/ref.js' import { formatISODate, newDate } from 'shared-utils/date-utils' import { PktCalendar } from '../calendar/calendar' import { calendarUtils } from './datepicker-utils' export class PktDatepickerPopup extends PktElement { @property({ type: Boolean, reflect: true }) open = false @property({ type: Boolean }) multiple = false @property({ type: Boolean }) range = false @property({ type: Boolean }) weeknumbers = false @property({ type: Boolean }) withcontrols = false @property({ type: Number }) maxMultiple: number | null = null @property({ type: Array }) selected: string[] = [] @property({ type: String }) earliest: string | null = null @property({ type: String }) latest: string | null = null @property({ type: Array }) excludedates: string[] = [] @property({ type: Array }) excludeweekdays: string[] = [] @property({ type: String }) currentmonth: string | null = null @property({ type: String }) today: string | null = null @state() private _hasBeenOpened = false popupRef: Ref<HTMLElement> = createRef() calendarRef: Ref<HTMLElement> = createRef() firstUpdated() { // expose calendarRef for external use this.calRef = this.calendarRef } updated(changedProperties: PropertyValues) { super.updated(changedProperties) if (changedProperties.has('open')) { if (this.open) { this._hasBeenOpened = true document.addEventListener('keydown', this.handleDocumentKeydown) document.addEventListener('click', this.handleDocumentClick) } else { document.removeEventListener('click', this.handleDocumentClick) document.removeEventListener('keydown', this.handleDocumentKeydown) } } } disconnectedCallback() { super.disconnectedCallback() document.removeEventListener('click', this.handleDocumentClick) document.removeEventListener('keydown', this.handleDocumentKeydown) } handleDocumentClick = (e: MouseEvent) => { if (!this.open) return const path = e.composedPath() as EventTarget[] const host = this.parentElement as EventTarget | null const popupNode = this.popupRef.value as EventTarget | null if ( !path.includes(this) && !path.includes(popupNode as EventTarget) && !(host && path.includes(host)) ) { this.hide() this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true })) } } handleDocumentKeydown = (e: KeyboardEvent) => { if (!this.open) return if (e.key === 'Escape') { this.hide() this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true })) } } show() { this.open = true ;(this.calendarRef.value as HTMLElement | null)?.focus() } hide() { this.open = false } toggle() { this.open ? this.hide() : this.show() } contains(node: Node | null) { return !!node && !!(this.popupRef.value as HTMLElement | null)?.contains(node as Node) } focusOnCurrentDate() { const cal = this.calendarRef.value as PktCalendar if (cal && typeof cal.focusOnCurrentDate === 'function') cal.focusOnCurrentDate() } addToSelected(e: Event, min?: string | null, max?: string | null) { const cal = this.calendarRef.value as PktCalendar if (cal && typeof calendarUtils.addToSelected === 'function') { return calendarUtils.addToSelected(e, this.calendarRef as any, min, max) } // Calendar not rendered yet — handle add directly const target = e.target as HTMLInputElement if (!target.value) return const minAsDate = min ? newDate(min) : null const maxAsDate = max ? newDate(max) : null const date = newDate(target.value.split(',')[0]) if (date && !isNaN(date.getTime()) && (!minAsDate || date >= minAsDate) && (!maxAsDate || date <= maxAsDate)) { this.handleDateSelect(date) } target.value = '' } handleDateSelect(date: Date) { const cal = this.calendarRef.value as PktCalendar if (cal && typeof cal.handleDateSelect === 'function') return cal.handleDateSelect(date) // Calendar not rendered yet — handle selection toggle directly const dateStr = formatISODate(date) const index = this.selected.indexOf(dateStr) const newSelected = index >= 0 ? this.selected.filter((d) => d !== dateStr) : [...this.selected, dateStr] this.selected = newSelected this.dispatchEvent( new CustomEvent('date-selected', { detail: newSelected, bubbles: true, composed: true }), ) return undefined } render() { const classes = { 'pkt-calendar-popup': true, show: this.open, hide: !this.open } return html` <div class="${classMap(classes)}" ${ref(this.popupRef)} id="date-popup" ?hidden=${!this.open} aria-hidden="${!this.open}" > ${this.open || this._hasBeenOpened ? html`<pkt-calendar ${ref(this.calendarRef)} ?multiple=${this.multiple} ?range=${this.range} ?weeknumbers=${this.weeknumbers} ?withcontrols=${this.withcontrols} .maxMultiple=${this.maxMultiple} .selected=${this.selected} .earliest=${this.earliest} .latest=${this.latest} .excludedates=${this.excludedates} .excludeweekdays=${this.excludeweekdays} .currentmonth=${this.currentmonth} .today=${this.today} @date-selected=${(e: CustomEvent) => { this.selected = e.detail this.dispatchEvent( new CustomEvent('date-selected', { detail: e.detail, bubbles: true, composed: true }), ) }} @close=${() => { this.hide() this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true })) }} ></pkt-calendar>` : nothing} </div> ` } } try { customElement('pkt-datepicker-popup')(PktDatepickerPopup) } catch (e) { console.warn('Forsøker å definere <pkt-datepicker-popup>, men den er allerede definert') }