@oslokommune/punkt-elements 13.6.11 → 13.6.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/{combobox-cK_746ek.cjs → combobox-BFOjlFIj.cjs} +1 -1
  3. package/dist/{combobox-DxNotM0u.js → combobox-DaiEdUKx.js} +1 -1
  4. package/dist/datepicker-BR1imflE.cjs +289 -0
  5. package/dist/datepicker-CbVSKaOY.js +1390 -0
  6. package/dist/index.d.ts +125 -7
  7. package/dist/{input-wrapper-D_JdEqcO.js → input-wrapper-CQzXG44g.js} +22 -22
  8. package/dist/{input-wrapper-C9rZEgju.cjs → input-wrapper-DVjNwf8-.cjs} +11 -12
  9. package/dist/pkt-combobox.cjs +1 -1
  10. package/dist/pkt-combobox.js +1 -1
  11. package/dist/pkt-datepicker.cjs +1 -1
  12. package/dist/pkt-datepicker.js +8 -4
  13. package/dist/pkt-index.cjs +1 -1
  14. package/dist/pkt-index.js +6 -6
  15. package/dist/pkt-input-wrapper.cjs +1 -1
  16. package/dist/pkt-input-wrapper.js +1 -1
  17. package/dist/pkt-select.cjs +1 -1
  18. package/dist/pkt-select.js +1 -1
  19. package/dist/pkt-textarea.cjs +1 -1
  20. package/dist/pkt-textarea.js +1 -1
  21. package/dist/pkt-textinput.cjs +1 -1
  22. package/dist/pkt-textinput.js +1 -1
  23. package/dist/{select-D7OQaUrQ.js → select-DKkoxmgj.js} +1 -1
  24. package/dist/{select-Cf1RWSsI.cjs → select-DynzsPo0.cjs} +1 -1
  25. package/dist/{textarea-CXu8UUsY.cjs → textarea-BS1tgktz.cjs} +1 -1
  26. package/dist/{textarea-C0vTWTov.js → textarea-COG1CH_s.js} +1 -1
  27. package/dist/{textinput-C6wccDhZ.cjs → textinput-CCK8ti2y.cjs} +1 -1
  28. package/dist/{textinput-CmZrfH4A.js → textinput-CTOtfcTR.js} +1 -1
  29. package/package.json +3 -3
  30. package/src/components/datepicker/datepicker-multiple.ts +202 -0
  31. package/src/components/datepicker/datepicker-popup.test.ts +77 -0
  32. package/src/components/datepicker/datepicker-popup.ts +137 -0
  33. package/src/components/datepicker/datepicker-range.ts +281 -0
  34. package/src/components/datepicker/datepicker-single.ts +198 -0
  35. package/src/components/datepicker/datepicker-utils.ts +22 -9
  36. package/src/components/datepicker/datepicker.ts +179 -256
  37. package/src/components/datepicker/index.ts +5 -1
  38. package/src/components/input-wrapper/input-wrapper.ts +7 -7
  39. package/dist/datepicker-BEMo4X9s.js +0 -770
  40. package/dist/datepicker-n49TAIAt.cjs +0 -169
@@ -0,0 +1,202 @@
1
+ import { html } from 'lit'
2
+ import { customElement, property } from 'lit/decorators.js'
3
+ import { classMap } from 'lit/directives/class-map.js'
4
+ import { ifDefined } from 'lit/directives/if-defined.js'
5
+ import { Ref, createRef, ref } from 'lit/directives/ref.js'
6
+ import { PktElement } from '@/base-elements/element'
7
+ import { keyboardUtils, formUtils, deviceDetection, cssUtils } from './datepicker-utils'
8
+ import '@/components/icon'
9
+
10
+ @customElement('pkt-datepicker-multiple')
11
+ export class PktDatepickerMultiple extends PktElement {
12
+ @property({ type: Array })
13
+ value: string[] = []
14
+
15
+ @property({ type: String })
16
+ inputType: string = 'date'
17
+
18
+ @property({ type: String })
19
+ id: string = ''
20
+
21
+ @property({ type: String })
22
+ min?: string
23
+
24
+ @property({ type: String })
25
+ max?: string
26
+
27
+ @property({ type: String })
28
+ placeholder?: string
29
+
30
+ @property({ type: Boolean })
31
+ readonly: boolean = false
32
+
33
+ @property({ type: Boolean })
34
+ disabled: boolean = false
35
+
36
+ @property({ type: Number })
37
+ maxlength?: number
38
+
39
+ @property({ type: Object })
40
+ inputClasses: Record<string, boolean> = {}
41
+
42
+ @property({ type: Object })
43
+ internals?: any
44
+
45
+ @property({ type: Object })
46
+ strings: any = { calendar: { buttonAltText: 'Åpne kalender' } }
47
+
48
+ inputRef: Ref<HTMLInputElement> = createRef()
49
+ btnRef: Ref<HTMLButtonElement> = createRef()
50
+
51
+ get inputElement(): HTMLInputElement | undefined {
52
+ return this.inputRef.value
53
+ }
54
+
55
+ get buttonElement(): HTMLButtonElement | undefined {
56
+ return this.btnRef.value
57
+ }
58
+
59
+ get isInputReadonly(): boolean {
60
+ return this.readonly || this.inputType === 'text'
61
+ }
62
+
63
+ get isInputDisabled(): boolean {
64
+ return (
65
+ this.disabled ||
66
+ (this.maxlength !== undefined &&
67
+ this.maxlength !== null &&
68
+ this.value.length >= this.maxlength)
69
+ )
70
+ }
71
+
72
+ private dispatchToggleCalendar(e: Event) {
73
+ if (this.readonly) return
74
+
75
+ this.dispatchEvent(
76
+ new CustomEvent('toggle-calendar', {
77
+ detail: e,
78
+ bubbles: true,
79
+ composed: true,
80
+ }),
81
+ )
82
+ }
83
+
84
+ private dispatchInput(e: Event) {
85
+ this.dispatchEvent(
86
+ new CustomEvent('input-change', {
87
+ detail: e,
88
+ bubbles: true,
89
+ composed: true,
90
+ }),
91
+ )
92
+ }
93
+
94
+ private dispatchFocus() {
95
+ this.dispatchEvent(
96
+ new CustomEvent('input-focus', {
97
+ bubbles: true,
98
+ composed: true,
99
+ }),
100
+ )
101
+ }
102
+
103
+ private dispatchBlur(e: FocusEvent) {
104
+ this.dispatchEvent(
105
+ new CustomEvent('input-blur', {
106
+ detail: e,
107
+ bubbles: true,
108
+ composed: true,
109
+ }),
110
+ )
111
+ }
112
+
113
+ private dispatchChange(e: Event) {
114
+ this.dispatchEvent(
115
+ new CustomEvent('input-changed', {
116
+ detail: e,
117
+ bubbles: true,
118
+ composed: true,
119
+ }),
120
+ )
121
+ }
122
+
123
+ private dispatchAddToSelected(e: Event | KeyboardEvent) {
124
+ this.dispatchEvent(
125
+ new CustomEvent('add-to-selected', {
126
+ detail: e,
127
+ bubbles: true,
128
+ composed: true,
129
+ }),
130
+ )
131
+ }
132
+
133
+ createRenderRoot() {
134
+ return this
135
+ }
136
+
137
+ render() {
138
+ return html`
139
+ <div class="pkt-input__container">
140
+ <input
141
+ class=${classMap(this.inputClasses)}
142
+ .type=${this.inputType}
143
+ id="${this.id}-input"
144
+ min=${ifDefined(this.min)}
145
+ max=${ifDefined(this.max)}
146
+ placeholder=${ifDefined(this.placeholder)}
147
+ ?readonly=${this.isInputReadonly}
148
+ ?disabled=${this.isInputDisabled}
149
+ @click=${(e: MouseEvent) => {
150
+ e.preventDefault()
151
+ this.dispatchToggleCalendar(e)
152
+ }}
153
+ @touchend=${(e: TouchEvent) => {
154
+ e.preventDefault()
155
+ this.dispatchToggleCalendar(e)
156
+ }}
157
+ @blur=${(e: FocusEvent) => {
158
+ this.dispatchBlur(e)
159
+ this.dispatchAddToSelected(e)
160
+ }}
161
+ @input=${(e: Event) => {
162
+ this.dispatchInput(e)
163
+ e.stopImmediatePropagation()
164
+ }}
165
+ @focus=${() => {
166
+ this.dispatchFocus()
167
+ if (deviceDetection.isIOS()) {
168
+ this.dispatchToggleCalendar(new Event('focus'))
169
+ }
170
+ }}
171
+ @keydown=${(e: KeyboardEvent) =>
172
+ keyboardUtils.handleInputKeydown(
173
+ e,
174
+ (event) => this.dispatchToggleCalendar(event),
175
+ () =>
176
+ formUtils.submitFormOrFallback(this.internals, () => this.inputRef.value?.blur()),
177
+ undefined,
178
+ undefined,
179
+ (event) => this.dispatchAddToSelected(event),
180
+ )}
181
+ @change=${(e: Event) => {
182
+ this.dispatchChange(e)
183
+ e.stopImmediatePropagation()
184
+ }}
185
+ ${ref(this.inputRef)}
186
+ />
187
+ <button
188
+ class="${classMap(cssUtils.getButtonClasses())}"
189
+ type="button"
190
+ @click=${(e: Event) => this.dispatchToggleCalendar(e)}
191
+ @keydown=${(e: KeyboardEvent) =>
192
+ keyboardUtils.handleButtonKeydown(e, (event) => this.dispatchToggleCalendar(event))}
193
+ ?disabled=${this.disabled}
194
+ ${ref(this.btnRef)}
195
+ >
196
+ <pkt-icon name="calendar"></pkt-icon>
197
+ <span class="pkt-btn__text">${this.strings.calendar.buttonAltText}</span>
198
+ </button>
199
+ </div>
200
+ `
201
+ }
202
+ }
@@ -0,0 +1,77 @@
1
+ import '@testing-library/jest-dom'
2
+ import { fireEvent } from '@testing-library/dom'
3
+ import { vi } from 'vitest'
4
+
5
+ import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
6
+ import './datepicker-popup'
7
+ import '../calendar/calendar'
8
+
9
+ import { PktDatepickerPopup } from './datepicker-popup'
10
+ import { PktCalendar } from '../calendar/calendar'
11
+
12
+ export interface IDatepickerPopupTest extends BaseTestConfig {
13
+ open?: boolean
14
+ }
15
+
16
+ const createPopupTest = async (config: IDatepickerPopupTest = {}) => {
17
+ const { container, element } = await createElementTest('pkt-datepicker-popup' as any, config)
18
+ return { container, popup: element as PktDatepickerPopup }
19
+ }
20
+
21
+ afterEach(() => {
22
+ document.body.innerHTML = ''
23
+ })
24
+
25
+ describe('PktDatepickerPopup', () => {
26
+ test('opens when show() is called', async () => {
27
+ const { popup } = await createPopupTest()
28
+ await popup.updateComplete
29
+
30
+ popup.show()
31
+ await popup.updateComplete
32
+ expect(popup.open).toBe(true)
33
+ })
34
+
35
+ test('closes when clicking outside', async () => {
36
+ const { popup } = await createPopupTest()
37
+ await popup.updateComplete
38
+
39
+ popup.show()
40
+ await popup.updateComplete
41
+ expect(popup.open).toBe(true)
42
+
43
+ fireEvent.click(document.body)
44
+ await popup.updateComplete
45
+ expect(popup.open).toBe(false)
46
+ })
47
+
48
+ test('closes on Escape key', async () => {
49
+ const { popup } = await createPopupTest()
50
+ await popup.updateComplete
51
+
52
+ popup.show()
53
+ await popup.updateComplete
54
+ expect(popup.open).toBe(true)
55
+
56
+ fireEvent.keyDown(popup, { key: 'Escape' })
57
+ await popup.updateComplete
58
+ expect(popup.open).toBe(false)
59
+ })
60
+
61
+ test('re-dispatches date-selected from child calendar', async () => {
62
+ const { popup } = await createPopupTest()
63
+ await popup.updateComplete
64
+
65
+ const handler = vi.fn()
66
+ popup.addEventListener('date-selected', (e: any) => handler(e))
67
+
68
+ const cal = popup.querySelector('pkt-calendar') as PktCalendar | null
69
+ expect(cal).toBeTruthy()
70
+ const detail = ['2025-09-17']
71
+ cal?.dispatchEvent(new CustomEvent('date-selected', { detail, bubbles: true, composed: true }))
72
+
73
+ expect(handler).toHaveBeenCalled()
74
+ const eventArg = handler.mock.calls[0][0]
75
+ expect(eventArg.detail).toEqual(detail)
76
+ })
77
+ })
@@ -0,0 +1,137 @@
1
+ import { html } from 'lit'
2
+ import { PktElement } from '@/base-elements/element'
3
+ import { customElement, property } from 'lit/decorators.js'
4
+ import { classMap } from 'lit/directives/class-map.js'
5
+ import { ref, createRef, Ref } from 'lit/directives/ref.js'
6
+
7
+ import { PktCalendar } from '../calendar/calendar'
8
+ import { calendarUtils } from './datepicker-utils'
9
+
10
+ @customElement('pkt-datepicker-popup')
11
+ export class PktDatepickerPopup extends PktElement {
12
+ @property({ type: Boolean, reflect: true }) open = false
13
+ @property({ type: Boolean }) multiple = false
14
+ @property({ type: Boolean }) range = false
15
+ @property({ type: Boolean }) weeknumbers = false
16
+ @property({ type: Boolean }) withcontrols = false
17
+ @property({ type: Number }) maxMultiple: number | null = null
18
+ @property({ type: Array }) selected: string[] = []
19
+ @property({ type: String }) earliest: string | null = null
20
+ @property({ type: String }) latest: string | null = null
21
+ @property({ type: Array }) excludedates: string[] = []
22
+ @property({ type: Array }) excludeweekdays: string[] = []
23
+ @property({ type: String }) currentmonth: string | null = null
24
+
25
+ popupRef: Ref<HTMLElement> = createRef()
26
+ calendarRef: Ref<HTMLElement> = createRef()
27
+
28
+ firstUpdated() {
29
+ // expose calendarRef for external use
30
+ this.calRef = this.calendarRef
31
+
32
+ document.addEventListener('keydown', this.handleDocumentKeydown)
33
+ document.addEventListener('click', this.handleDocumentClick)
34
+ }
35
+
36
+ disconnectedCallback() {
37
+ super.disconnectedCallback()
38
+ document.removeEventListener('click', this.handleDocumentClick)
39
+ document.removeEventListener('keydown', this.handleDocumentKeydown)
40
+ }
41
+
42
+ handleDocumentClick = (e: MouseEvent) => {
43
+ if (!this.open) return
44
+ const path = e.composedPath() as EventTarget[]
45
+ const host = this.parentElement as EventTarget | null
46
+ const popupNode = this.popupRef.value as EventTarget | null
47
+ if (
48
+ !path.includes(this) &&
49
+ !path.includes(popupNode as EventTarget) &&
50
+ !(host && path.includes(host))
51
+ ) {
52
+ this.hide()
53
+ this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }))
54
+ }
55
+ }
56
+
57
+ handleDocumentKeydown = (e: KeyboardEvent) => {
58
+ if (!this.open) return
59
+ if (e.key === 'Escape') {
60
+ this.hide()
61
+ this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }))
62
+ }
63
+ }
64
+
65
+ show() {
66
+ this.open = true
67
+ ;(this.calendarRef.value as HTMLElement | null)?.focus()
68
+ }
69
+
70
+ hide() {
71
+ this.open = false
72
+ }
73
+
74
+ toggle() {
75
+ this.open ? this.hide() : this.show()
76
+ }
77
+
78
+ contains(node: Node | null) {
79
+ return !!node && !!(this.popupRef.value as HTMLElement | null)?.contains(node as Node)
80
+ }
81
+
82
+ focusOnCurrentDate() {
83
+ const cal = this.calendarRef.value as PktCalendar
84
+ if (cal && typeof cal.focusOnCurrentDate === 'function') cal.focusOnCurrentDate()
85
+ }
86
+
87
+ addToSelected(e: Event, min?: string | null, max?: string | null) {
88
+ if (typeof calendarUtils.addToSelected === 'function') {
89
+ return calendarUtils.addToSelected(e, this.calendarRef as any, min, max)
90
+ }
91
+ return undefined
92
+ }
93
+
94
+ handleDateSelect(date: Date) {
95
+ const cal = this.calendarRef.value as PktCalendar
96
+ if (cal && typeof cal.handleDateSelect === 'function') return cal.handleDateSelect(date)
97
+ return undefined
98
+ }
99
+
100
+ render() {
101
+ const classes = { 'pkt-calendar-popup': true, show: this.open, hide: !this.open }
102
+ return html`
103
+ <div
104
+ class="${classMap(classes)}"
105
+ ${ref(this.popupRef)}
106
+ id="date-popup"
107
+ ?hidden=${!this.open}
108
+ aria-hidden="${!this.open}"
109
+ >
110
+ <pkt-calendar
111
+ ${ref(this.calendarRef)}
112
+ ?multiple=${this.multiple}
113
+ ?range=${this.range}
114
+ ?weeknumbers=${this.weeknumbers}
115
+ ?withcontrols=${this.withcontrols}
116
+ .maxMultiple=${this.maxMultiple}
117
+ .selected=${this.selected}
118
+ .earliest=${this.earliest}
119
+ .latest=${this.latest}
120
+ .excludedates=${this.excludedates}
121
+ .excludeweekdays=${this.excludeweekdays}
122
+ .currentmonth=${this.currentmonth}
123
+ @date-selected=${(e: CustomEvent) => {
124
+ this.selected = e.detail
125
+ this.dispatchEvent(
126
+ new CustomEvent('date-selected', { detail: e.detail, bubbles: true, composed: true }),
127
+ )
128
+ }}
129
+ @close=${() => {
130
+ this.hide()
131
+ this.dispatchEvent(new CustomEvent('close', { bubbles: true, composed: true }))
132
+ }}
133
+ ></pkt-calendar>
134
+ </div>
135
+ `
136
+ }
137
+ }
@@ -0,0 +1,281 @@
1
+ import { html, nothing } from 'lit'
2
+ import { customElement, property } from 'lit/decorators.js'
3
+ import { classMap } from 'lit/directives/class-map.js'
4
+ import { ifDefined } from 'lit/directives/if-defined.js'
5
+ import { Ref, createRef, ref } from 'lit/directives/ref.js'
6
+ import { PktElement } from '@/base-elements/element'
7
+ import { keyboardUtils, formUtils, deviceDetection, cssUtils } from './datepicker-utils'
8
+ import '@/components/icon'
9
+
10
+ @customElement('pkt-datepicker-range')
11
+ export class PktDatepickerRange extends PktElement {
12
+ @property({ type: Array })
13
+ value: string[] = []
14
+
15
+ @property({ type: String })
16
+ inputType: string = 'date'
17
+
18
+ @property({ type: String })
19
+ id: string = ''
20
+
21
+ @property({ type: String })
22
+ min?: string
23
+
24
+ @property({ type: String })
25
+ max?: string
26
+
27
+ @property({ type: String })
28
+ placeholder?: string
29
+
30
+ @property({ type: Boolean })
31
+ readonly: boolean = false
32
+
33
+ @property({ type: Boolean })
34
+ disabled: boolean = false
35
+
36
+ @property({ type: Boolean })
37
+ showRangeLabels: boolean = false
38
+
39
+ @property({ type: Object })
40
+ inputClasses: Record<string, boolean> = {}
41
+
42
+ @property({ type: Object })
43
+ internals?: any
44
+
45
+ @property({ type: Object })
46
+ strings: any = { generic: { from: 'Fra', to: 'Til' } }
47
+
48
+ inputRef: Ref<HTMLInputElement> = createRef()
49
+ inputRefTo: Ref<HTMLInputElement> = createRef()
50
+ btnRef: Ref<HTMLButtonElement> = createRef()
51
+
52
+ get inputElement(): HTMLInputElement | undefined {
53
+ return this.inputRef.value
54
+ }
55
+
56
+ get inputElementTo(): HTMLInputElement | undefined {
57
+ return this.inputRefTo.value
58
+ }
59
+
60
+ get buttonElement(): HTMLButtonElement | undefined {
61
+ return this.btnRef.value
62
+ }
63
+
64
+ get isInputReadonly(): boolean {
65
+ return this.readonly || this.inputType === 'text'
66
+ }
67
+
68
+ private dispatchToggleCalendar(e: Event) {
69
+ if (this.readonly) return
70
+
71
+ this.dispatchEvent(
72
+ new CustomEvent('toggle-calendar', {
73
+ detail: e,
74
+ bubbles: true,
75
+ composed: true,
76
+ }),
77
+ )
78
+ }
79
+
80
+ private dispatchInput(e: Event) {
81
+ this.dispatchEvent(
82
+ new CustomEvent('input-change', {
83
+ detail: e,
84
+ bubbles: true,
85
+ composed: true,
86
+ }),
87
+ )
88
+ }
89
+
90
+ private dispatchFocus() {
91
+ this.dispatchEvent(
92
+ new CustomEvent('input-focus', {
93
+ bubbles: true,
94
+ composed: true,
95
+ }),
96
+ )
97
+ }
98
+
99
+ private dispatchBlur(e: FocusEvent) {
100
+ this.dispatchEvent(
101
+ new CustomEvent('input-blur', {
102
+ detail: e,
103
+ bubbles: true,
104
+ composed: true,
105
+ }),
106
+ )
107
+ }
108
+
109
+ private dispatchChange(e: Event) {
110
+ this.dispatchEvent(
111
+ new CustomEvent('input-changed', {
112
+ detail: e,
113
+ bubbles: true,
114
+ composed: true,
115
+ }),
116
+ )
117
+ }
118
+
119
+ private dispatchManageValidity(input: HTMLInputElement) {
120
+ this.dispatchEvent(
121
+ new CustomEvent('manage-validity', {
122
+ detail: input,
123
+ bubbles: true,
124
+ composed: true,
125
+ }),
126
+ )
127
+ }
128
+
129
+ createRenderRoot() {
130
+ return this
131
+ }
132
+
133
+ render() {
134
+ const rangeLabelClasses = cssUtils.getRangeLabelClasses(this.showRangeLabels)
135
+
136
+ return html`
137
+ <div class="pkt-input__container">
138
+ ${this.showRangeLabels
139
+ ? html` <div class="pkt-input-prefix">${this.strings.generic.from}</div> `
140
+ : nothing}
141
+ <input
142
+ class=${classMap(this.inputClasses)}
143
+ .type=${this.inputType}
144
+ id="${this.id}-input"
145
+ .value=${this.value[0] ?? ''}
146
+ min=${ifDefined(this.min)}
147
+ max=${ifDefined(this.max)}
148
+ placeholder=${ifDefined(this.placeholder)}
149
+ ?readonly=${this.isInputReadonly}
150
+ ?disabled=${this.disabled}
151
+ @click=${(e: MouseEvent) => {
152
+ e.preventDefault()
153
+ this.dispatchToggleCalendar(e)
154
+ }}
155
+ @touchend=${(e: TouchEvent) => {
156
+ e.preventDefault()
157
+ this.dispatchToggleCalendar(e)
158
+ }}
159
+ @keydown=${(e: KeyboardEvent) =>
160
+ keyboardUtils.handleInputKeydown(
161
+ e,
162
+ (event) => this.dispatchToggleCalendar(event),
163
+ () =>
164
+ formUtils.submitFormOrFallback(this.internals, () =>
165
+ this.inputRefTo.value?.focus(),
166
+ ),
167
+ () => this.inputRefTo.value?.focus(),
168
+ () => this.inputRef.value?.blur(),
169
+ )}
170
+ @input=${(e: Event) => {
171
+ this.dispatchInput(e)
172
+ e.stopImmediatePropagation()
173
+ }}
174
+ @focus=${() => {
175
+ this.dispatchFocus()
176
+ if (deviceDetection.isIOS()) {
177
+ this.dispatchToggleCalendar(new Event('focus'))
178
+ }
179
+ }}
180
+ @blur=${(e: Event) => {
181
+ this.dispatchEvent(
182
+ new CustomEvent('range-blur', {
183
+ detail: {
184
+ event: e,
185
+ values: this.value,
186
+ inputType: 'from',
187
+ },
188
+ bubbles: true,
189
+ composed: true,
190
+ }),
191
+ )
192
+ }}
193
+ @change=${(e: Event) => {
194
+ e.stopImmediatePropagation()
195
+ }}
196
+ ${ref(this.inputRef)}
197
+ />
198
+ <div class="${classMap(rangeLabelClasses)}" id="${this.id}-to-label">
199
+ ${this.strings.generic.to}
200
+ </div>
201
+ ${!this.showRangeLabels ? html` <div class="pkt-input-separator">–</div> ` : nothing}
202
+ <input
203
+ class=${classMap(this.inputClasses)}
204
+ .type=${this.inputType}
205
+ id="${this.id}-to"
206
+ aria-labelledby="${this.id}-to-label"
207
+ .value=${this.value[1] ?? ''}
208
+ min=${ifDefined(this.min)}
209
+ max=${ifDefined(this.max)}
210
+ placeholder=${ifDefined(this.placeholder)}
211
+ ?readonly=${this.isInputReadonly}
212
+ ?disabled=${this.disabled}
213
+ @click=${(e: MouseEvent) => {
214
+ e.preventDefault()
215
+ this.dispatchToggleCalendar(e)
216
+ }}
217
+ @touchend=${(e: TouchEvent) => {
218
+ e.preventDefault()
219
+ this.dispatchToggleCalendar(e)
220
+ }}
221
+ @keydown=${(e: KeyboardEvent) =>
222
+ keyboardUtils.handleInputKeydown(
223
+ e,
224
+ (event) => this.dispatchToggleCalendar(event),
225
+ () =>
226
+ formUtils.submitFormOrFallback(this.internals, () => this.inputRefTo.value?.blur()),
227
+ undefined,
228
+ () => this.inputRefTo.value?.blur(),
229
+ )}
230
+ @input=${(e: Event) => {
231
+ this.dispatchInput(e)
232
+ e.stopImmediatePropagation()
233
+ }}
234
+ @focus=${() => {
235
+ this.dispatchFocus()
236
+ if (deviceDetection.isIOS()) {
237
+ this.dispatchToggleCalendar(new Event('focus'))
238
+ }
239
+ }}
240
+ @blur=${(e: FocusEvent) => {
241
+ this.dispatchBlur(e)
242
+ if ((e.target as HTMLInputElement).value) {
243
+ this.dispatchManageValidity(e.target as HTMLInputElement)
244
+ this.dispatchEvent(
245
+ new CustomEvent('validate-date-input', {
246
+ detail: e.target as HTMLInputElement,
247
+ bubbles: true,
248
+ composed: true,
249
+ }),
250
+ )
251
+ this.dispatchEvent(
252
+ new CustomEvent('handle-date-select', {
253
+ detail: (e.target as HTMLInputElement).value,
254
+ bubbles: true,
255
+ composed: true,
256
+ }),
257
+ )
258
+ }
259
+ }}
260
+ @change=${(e: Event) => {
261
+ this.dispatchChange(e)
262
+ e.stopImmediatePropagation()
263
+ }}
264
+ ${ref(this.inputRefTo)}
265
+ />
266
+ <button
267
+ class="${classMap(cssUtils.getButtonClasses())}"
268
+ type="button"
269
+ @click=${(e: Event) => this.dispatchToggleCalendar(e)}
270
+ @keydown=${(e: KeyboardEvent) =>
271
+ keyboardUtils.handleButtonKeydown(e, (event) => this.dispatchToggleCalendar(event))}
272
+ ?disabled=${this.disabled}
273
+ ${ref(this.btnRef)}
274
+ >
275
+ <pkt-icon name="calendar"></pkt-icon>
276
+ <span class="pkt-btn__text">${this.strings.calendar.buttonAltText}</span>
277
+ </button>
278
+ </div>
279
+ `
280
+ }
281
+ }