@oslokommune/punkt-elements 13.6.11 → 13.6.12
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.
- package/CHANGELOG.md +17 -0
- package/dist/{combobox-cK_746ek.cjs → combobox-BFOjlFIj.cjs} +1 -1
- package/dist/{combobox-DxNotM0u.js → combobox-DaiEdUKx.js} +1 -1
- package/dist/datepicker-C244h82t.cjs +190 -0
- package/dist/datepicker-DwOkktaP.js +859 -0
- package/dist/index.d.ts +30 -3
- package/dist/{input-wrapper-D_JdEqcO.js → input-wrapper-CQzXG44g.js} +22 -22
- package/dist/{input-wrapper-C9rZEgju.cjs → input-wrapper-DVjNwf8-.cjs} +11 -12
- package/dist/pkt-combobox.cjs +1 -1
- package/dist/pkt-combobox.js +1 -1
- package/dist/pkt-datepicker.cjs +1 -1
- package/dist/pkt-datepicker.js +1 -1
- package/dist/pkt-index.cjs +1 -1
- package/dist/pkt-index.js +6 -6
- package/dist/pkt-input-wrapper.cjs +1 -1
- package/dist/pkt-input-wrapper.js +1 -1
- package/dist/pkt-select.cjs +1 -1
- package/dist/pkt-select.js +1 -1
- package/dist/pkt-textarea.cjs +1 -1
- package/dist/pkt-textarea.js +1 -1
- package/dist/pkt-textinput.cjs +1 -1
- package/dist/pkt-textinput.js +1 -1
- package/dist/{select-D7OQaUrQ.js → select-DKkoxmgj.js} +1 -1
- package/dist/{select-Cf1RWSsI.cjs → select-DynzsPo0.cjs} +1 -1
- package/dist/{textarea-CXu8UUsY.cjs → textarea-BS1tgktz.cjs} +1 -1
- package/dist/{textarea-C0vTWTov.js → textarea-COG1CH_s.js} +1 -1
- package/dist/{textinput-C6wccDhZ.cjs → textinput-CCK8ti2y.cjs} +1 -1
- package/dist/{textinput-CmZrfH4A.js → textinput-CTOtfcTR.js} +1 -1
- package/package.json +2 -2
- package/src/components/datepicker/datepicker-popup.test.ts +77 -0
- package/src/components/datepicker/datepicker-popup.ts +137 -0
- package/src/components/datepicker/datepicker-utils.ts +13 -8
- package/src/components/datepicker/datepicker.ts +42 -47
- package/src/components/input-wrapper/input-wrapper.ts +7 -7
- package/dist/datepicker-BEMo4X9s.js +0 -770
- package/dist/datepicker-n49TAIAt.cjs +0 -169
|
@@ -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
|
+
}
|
|
@@ -24,18 +24,23 @@ export const sleep = (ms: number): Promise<void> =>
|
|
|
24
24
|
export const deviceDetection = {
|
|
25
25
|
/**
|
|
26
26
|
* Detects if the current device is iOS (iPhone, iPad, iPod)
|
|
27
|
+
* Handles modern iPad Safari which uses desktop user agent since iOS 13
|
|
27
28
|
*/
|
|
28
29
|
isIOS(): boolean {
|
|
29
30
|
const ua = navigator.userAgent
|
|
30
|
-
return /iP(hone|od|ad)/.test(ua)
|
|
31
|
-
},
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
// Legacy iOS detection (iPhone, iPod, older iPads)
|
|
33
|
+
if (/iP(hone|od|ad)/.test(ua)) {
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Modern iPad detection (iOS 13+ iPads identify as Mac)
|
|
38
|
+
// Check for Mac + touch support
|
|
39
|
+
if (/Macintosh/.test(ua) && 'ontouchend' in document) {
|
|
40
|
+
return true
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return false
|
|
39
44
|
},
|
|
40
45
|
}
|
|
41
46
|
|
|
@@ -12,6 +12,7 @@ import '@/components/calendar'
|
|
|
12
12
|
import '@/components/icon'
|
|
13
13
|
import '@/components/input-wrapper'
|
|
14
14
|
import './date-tags'
|
|
15
|
+
import './datepicker-popup'
|
|
15
16
|
import { PktSlotController } from '@/controllers/pkt-slot-controller'
|
|
16
17
|
import { keyboardUtils } from './datepicker-utils'
|
|
17
18
|
import {
|
|
@@ -20,11 +21,11 @@ import {
|
|
|
20
21
|
valueUtils,
|
|
21
22
|
inputTypeUtils,
|
|
22
23
|
calendarUtils,
|
|
23
|
-
eventUtils,
|
|
24
24
|
cssUtils,
|
|
25
25
|
dateProcessingUtils,
|
|
26
26
|
formUtils,
|
|
27
27
|
} from './datepicker-utils'
|
|
28
|
+
import { PktDatepickerPopup } from './datepicker-popup'
|
|
28
29
|
import { ElementProps } from '@/types/typeUtils'
|
|
29
30
|
|
|
30
31
|
type Props = ElementProps<
|
|
@@ -52,8 +53,7 @@ export class PktDatepicker extends PktInputElement<Props> {
|
|
|
52
53
|
* Element attributes and properties
|
|
53
54
|
*/
|
|
54
55
|
private _valueProperty: string = ''
|
|
55
|
-
|
|
56
|
-
private documentKeydownListener?: (e: KeyboardEvent) => void
|
|
56
|
+
datepickerPopupRef: Ref<PktDatepickerPopup> = createRef()
|
|
57
57
|
|
|
58
58
|
@property({ type: String, reflect: true })
|
|
59
59
|
get value(): string {
|
|
@@ -141,30 +141,10 @@ export class PktDatepicker extends PktInputElement<Props> {
|
|
|
141
141
|
}
|
|
142
142
|
this.name =
|
|
143
143
|
valueUtils.normalizeNameForMultiple(this.name, this.multiple, this.range) || this.name
|
|
144
|
-
this.documentClickListener = eventUtils.createDocumentClickListener(
|
|
145
|
-
this.inputRef,
|
|
146
|
-
this.inputRefTo,
|
|
147
|
-
this.btnRef,
|
|
148
|
-
() => this.calendarOpen,
|
|
149
|
-
this.onBlur.bind(this),
|
|
150
|
-
this.hideCalendar.bind(this),
|
|
151
|
-
)
|
|
152
|
-
this.documentKeydownListener = eventUtils.createDocumentKeydownListener(
|
|
153
|
-
() => this.calendarOpen,
|
|
154
|
-
this.hideCalendar.bind(this),
|
|
155
|
-
)
|
|
156
|
-
document.addEventListener('click', this.documentClickListener)
|
|
157
|
-
document.addEventListener('keydown', this.documentKeydownListener)
|
|
158
144
|
}
|
|
159
145
|
|
|
160
146
|
disconnectedCallback(): void {
|
|
161
147
|
super.disconnectedCallback()
|
|
162
|
-
if (this.documentClickListener) {
|
|
163
|
-
document.removeEventListener('click', this.documentClickListener)
|
|
164
|
-
}
|
|
165
|
-
if (this.documentKeydownListener) {
|
|
166
|
-
document.removeEventListener('keydown', this.documentKeydownListener)
|
|
167
|
-
}
|
|
168
148
|
}
|
|
169
149
|
|
|
170
150
|
onInput(): void {
|
|
@@ -292,7 +272,7 @@ export class PktDatepicker extends PktInputElement<Props> {
|
|
|
292
272
|
}}
|
|
293
273
|
@focus=${() => {
|
|
294
274
|
this.onFocus()
|
|
295
|
-
if (deviceDetection.
|
|
275
|
+
if (deviceDetection.isIOS()) {
|
|
296
276
|
this.showCalendar()
|
|
297
277
|
}
|
|
298
278
|
}}
|
|
@@ -348,7 +328,7 @@ export class PktDatepicker extends PktInputElement<Props> {
|
|
|
348
328
|
}}
|
|
349
329
|
@focus=${() => {
|
|
350
330
|
this.onFocus()
|
|
351
|
-
if (deviceDetection.
|
|
331
|
+
if (deviceDetection.isIOS()) {
|
|
352
332
|
this.showCalendar()
|
|
353
333
|
}
|
|
354
334
|
}}
|
|
@@ -401,7 +381,7 @@ export class PktDatepicker extends PktInputElement<Props> {
|
|
|
401
381
|
}}
|
|
402
382
|
@focus=${() => {
|
|
403
383
|
this.onFocus()
|
|
404
|
-
if (deviceDetection.
|
|
384
|
+
if (deviceDetection.isIOS()) {
|
|
405
385
|
this.showCalendar()
|
|
406
386
|
}
|
|
407
387
|
}}
|
|
@@ -462,7 +442,7 @@ export class PktDatepicker extends PktInputElement<Props> {
|
|
|
462
442
|
}}
|
|
463
443
|
@focus=${() => {
|
|
464
444
|
this.onFocus()
|
|
465
|
-
if (deviceDetection.
|
|
445
|
+
if (deviceDetection.isIOS()) {
|
|
466
446
|
this.showCalendar()
|
|
467
447
|
}
|
|
468
448
|
}}
|
|
@@ -486,16 +466,10 @@ export class PktDatepicker extends PktInputElement<Props> {
|
|
|
486
466
|
}
|
|
487
467
|
|
|
488
468
|
renderCalendar() {
|
|
489
|
-
return html
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
}}
|
|
494
|
-
id="${this.id}-popup"
|
|
495
|
-
${ref(this.popupRef)}
|
|
496
|
-
>
|
|
497
|
-
<pkt-calendar
|
|
498
|
-
id="${this.id}-calendar"
|
|
469
|
+
return html`
|
|
470
|
+
<pkt-datepicker-popup
|
|
471
|
+
class="pkt-contents"
|
|
472
|
+
?open=${this.calendarOpen}
|
|
499
473
|
?multiple=${this.multiple}
|
|
500
474
|
?range=${this.range}
|
|
501
475
|
?weeknumbers=${this.weeknumbers}
|
|
@@ -525,9 +499,9 @@ export class PktDatepicker extends PktInputElement<Props> {
|
|
|
525
499
|
this.onBlur()
|
|
526
500
|
this.hideCalendar()
|
|
527
501
|
}}
|
|
528
|
-
${ref(this.
|
|
529
|
-
></pkt-
|
|
530
|
-
|
|
502
|
+
${ref(this.datepickerPopupRef)}
|
|
503
|
+
></pkt-datepicker-popup>
|
|
504
|
+
`
|
|
531
505
|
}
|
|
532
506
|
|
|
533
507
|
render() {
|
|
@@ -572,7 +546,13 @@ export class PktDatepicker extends PktInputElement<Props> {
|
|
|
572
546
|
strings=${this.strings}
|
|
573
547
|
id-base=${this.id}
|
|
574
548
|
@date-tag-removed=${(e: CustomEvent) => {
|
|
575
|
-
this.
|
|
549
|
+
const popup = this.datepickerPopupRef.value
|
|
550
|
+
const date = fromISOToDate(e.detail)
|
|
551
|
+
if (popup && date && typeof popup.handleDateSelect === 'function') {
|
|
552
|
+
popup.handleDateSelect(date)
|
|
553
|
+
} else {
|
|
554
|
+
this.calRef.value?.handleDateSelect(date)
|
|
555
|
+
}
|
|
576
556
|
}}
|
|
577
557
|
></pkt-date-tags>`
|
|
578
558
|
: nothing}
|
|
@@ -616,28 +596,43 @@ export class PktDatepicker extends PktInputElement<Props> {
|
|
|
616
596
|
}
|
|
617
597
|
|
|
618
598
|
addToSelected = (e: Event | KeyboardEvent) => {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
599
|
+
const popup = this.datepickerPopupRef.value
|
|
600
|
+
if (popup && typeof popup.addToSelected === 'function') {
|
|
601
|
+
return popup.addToSelected(e, this.min, this.max)
|
|
602
|
+
}
|
|
603
|
+
return calendarUtils.addToSelected(e, this.calRef, this.min, this.max)
|
|
624
604
|
}
|
|
625
605
|
|
|
626
606
|
public async showCalendar() {
|
|
607
|
+
const popup = this.datepickerPopupRef.value
|
|
627
608
|
this.calendarOpen = true
|
|
609
|
+
if (popup && typeof popup.show === 'function') {
|
|
610
|
+
popup.show()
|
|
611
|
+
if (deviceDetection.isIOS()) popup.focusOnCurrentDate()
|
|
612
|
+
return
|
|
613
|
+
}
|
|
628
614
|
await sleep(20)
|
|
629
615
|
this.handleCalendarPosition()
|
|
630
|
-
if (deviceDetection.
|
|
616
|
+
if (deviceDetection.isIOS()) {
|
|
631
617
|
this.calRef.value?.focusOnCurrentDate()
|
|
632
618
|
}
|
|
633
619
|
}
|
|
634
620
|
|
|
635
621
|
public hideCalendar() {
|
|
622
|
+
const popup = this.datepickerPopupRef.value
|
|
636
623
|
this.calendarOpen = false
|
|
624
|
+
if (popup && typeof popup.hide === 'function') return popup.hide()
|
|
637
625
|
}
|
|
638
626
|
|
|
639
627
|
public async toggleCalendar(e: Event) {
|
|
640
628
|
e.preventDefault()
|
|
629
|
+
const popup = this.datepickerPopupRef.value
|
|
630
|
+
if (popup && typeof popup.toggle === 'function') {
|
|
631
|
+
const wasOpen = !!popup.open
|
|
632
|
+
popup.toggle()
|
|
633
|
+
this.calendarOpen = !wasOpen
|
|
634
|
+
return
|
|
635
|
+
}
|
|
641
636
|
this.calendarOpen ? this.hideCalendar() : this.showCalendar()
|
|
642
637
|
}
|
|
643
638
|
|
|
@@ -11,6 +11,7 @@ import { uuidish } from '@/utils/stringutils'
|
|
|
11
11
|
import specs from 'componentSpecs/input-wrapper.json'
|
|
12
12
|
import '@/components/helptext'
|
|
13
13
|
import '@/components/icon'
|
|
14
|
+
import '@/components/alert'
|
|
14
15
|
|
|
15
16
|
type TCounterPosition = 'top' | 'bottom'
|
|
16
17
|
type Props = ElementProps<
|
|
@@ -190,16 +191,15 @@ export class PktInputWrapper extends PktElement<Props> {
|
|
|
190
191
|
|
|
191
192
|
const errorElement = () => {
|
|
192
193
|
if (this.hasError && this.errorMessage) {
|
|
193
|
-
return html`<
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
return html`<pkt-alert
|
|
195
|
+
skin="error"
|
|
196
|
+
compact
|
|
197
|
+
id=${`${this.forId}-error`}
|
|
196
198
|
aria-live="assertive"
|
|
197
199
|
aria-atomic="true"
|
|
198
|
-
id="${this.forId}-error"
|
|
199
200
|
>
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
</div>`
|
|
201
|
+
${unsafeHTML(this.errorMessage)}
|
|
202
|
+
</pkt-alert>`
|
|
203
203
|
} else {
|
|
204
204
|
return nothing
|
|
205
205
|
}
|