@oslokommune/punkt-elements 20.1.2 → 20.1.3

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 (106) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/dist/{calendar-xYEsKilS.cjs → calendar-KZlln5k8.cjs} +2 -2
  3. package/dist/{calendar-CkAAgviq.js → calendar-T_iFl5mR.js} +2 -2
  4. package/dist/{checkbox-Cg7wnn8U.cjs → checkbox-BS0iHeSP.cjs} +1 -1
  5. package/dist/{checkbox-XvlUQmg5.js → checkbox-DCaSNEjy.js} +1 -1
  6. package/dist/{checkbox-group-BF29zyLd.cjs → checkbox-group-BZhrLn4e.cjs} +1 -1
  7. package/dist/{checkbox-group-CRIV5CX0.js → checkbox-group-GsfZSfMs.js} +2 -2
  8. package/dist/{combobox-otZu39eS.cjs → combobox-DoUwp_6k.cjs} +1 -1
  9. package/dist/{combobox-CLVwmqkY.js → combobox-_2jd4CHR.js} +3 -3
  10. package/dist/components/datepicker/datepicker-popup.d.ts +0 -1
  11. package/dist/components/datepicker/datepicker-utils.d.ts +2 -13
  12. package/dist/components/datepicker/datepicker.d.ts +32 -4
  13. package/dist/components/datepicker/index.d.ts +1 -0
  14. package/dist/{datepicker-DHdFA8Dx.js → datepicker-D60fq9D5.js} +227 -472
  15. package/dist/datepicker-DErgngTf.cjs +286 -0
  16. package/dist/{fileupload-Cjde62AR.cjs → fileupload-Cn7CvQtX.cjs} +1 -1
  17. package/dist/{fileupload-BQw5nlzQ.js → fileupload-dmzxRqGF.js} +4 -4
  18. package/dist/{header-YbbGgmsD.cjs → header-BQCGdOPT.cjs} +1 -1
  19. package/dist/{header-CZnKVVt4.js → header-Djjhqu7w.js} +2 -2
  20. package/dist/{helptext-CQaNvbiv.cjs → helptext-4xBIABJJ.cjs} +1 -1
  21. package/dist/{helptext-kKmVQkwo.js → helptext-CNg695BI.js} +1 -1
  22. package/dist/{input-element-CfZIyfon.cjs → input-element-BPzDBWdd.cjs} +1 -1
  23. package/dist/{input-element-DE0FzjgP.js → input-element-DsWlt-6T.js} +1 -1
  24. package/dist/{input-wrapper-DYg1VHPv.js → input-wrapper-BBUcHEKx.js} +2 -2
  25. package/dist/{input-wrapper-CbYMk21W.cjs → input-wrapper-EKGhSUKU.cjs} +1 -1
  26. package/dist/{listbox-df8gHweY.js → listbox-BcJbeoAT.js} +1 -1
  27. package/dist/{listbox-Du8llYth.cjs → listbox-gjzS_z0P.cjs} +1 -1
  28. package/dist/{modal-SBJ8aGvM.js → modal-CvqH9R3w.js} +1 -1
  29. package/dist/{modal-B3rnE83a.cjs → modal-Du-2AmLz.cjs} +1 -1
  30. package/dist/{option-group-element-dN0ntPZk.cjs → option-group-element-DgqdU9HX.cjs} +1 -1
  31. package/dist/{option-group-element-DtSw52Dr.js → option-group-element-Dr2FR6v7.js} +2 -2
  32. package/dist/pkt-calendar.cjs +1 -1
  33. package/dist/pkt-calendar.js +1 -1
  34. package/dist/pkt-checkbox-group.cjs +1 -1
  35. package/dist/pkt-checkbox-group.js +1 -1
  36. package/dist/pkt-checkbox.cjs +1 -1
  37. package/dist/pkt-checkbox.js +1 -1
  38. package/dist/pkt-combobox.cjs +1 -1
  39. package/dist/pkt-combobox.js +1 -1
  40. package/dist/pkt-datepicker.cjs +1 -1
  41. package/dist/pkt-datepicker.js +1 -1
  42. package/dist/pkt-fileupload.cjs +1 -1
  43. package/dist/pkt-fileupload.js +1 -1
  44. package/dist/pkt-header.cjs +1 -1
  45. package/dist/pkt-header.js +1 -1
  46. package/dist/pkt-helptext.cjs +1 -1
  47. package/dist/pkt-helptext.js +1 -1
  48. package/dist/pkt-index.cjs +1 -1
  49. package/dist/pkt-index.js +18 -18
  50. package/dist/pkt-input-wrapper.cjs +1 -1
  51. package/dist/pkt-input-wrapper.js +1 -1
  52. package/dist/pkt-listbox.cjs +1 -1
  53. package/dist/pkt-listbox.js +1 -1
  54. package/dist/pkt-modal.cjs +1 -1
  55. package/dist/pkt-modal.js +1 -1
  56. package/dist/pkt-progressbar.cjs +1 -1
  57. package/dist/pkt-progressbar.js +1 -1
  58. package/dist/pkt-radio-group.cjs +1 -1
  59. package/dist/pkt-radio-group.js +1 -1
  60. package/dist/pkt-radiobutton.cjs +1 -1
  61. package/dist/pkt-radiobutton.js +1 -1
  62. package/dist/pkt-searchinput.cjs +1 -1
  63. package/dist/pkt-searchinput.js +1 -1
  64. package/dist/pkt-select.cjs +1 -1
  65. package/dist/pkt-select.js +1 -1
  66. package/dist/pkt-textarea.cjs +1 -1
  67. package/dist/pkt-textarea.js +1 -1
  68. package/dist/pkt-textinput.cjs +1 -1
  69. package/dist/pkt-textinput.js +1 -1
  70. package/dist/pkt-timepicker.cjs +1 -1
  71. package/dist/pkt-timepicker.js +1 -1
  72. package/dist/{progressbar-Wyq_Q1Ur.cjs → progressbar-BKA9zFnE.cjs} +1 -1
  73. package/dist/{progressbar-DlbkKRgP.js → progressbar-aKjj5VZg.js} +1 -1
  74. package/dist/{radio-group-u1IH7g-4.cjs → radio-group-BLVe7aMS.cjs} +1 -1
  75. package/dist/{radio-group-bRF8fnPW.js → radio-group-DDymupLt.js} +2 -2
  76. package/dist/{radiobutton-Bf6aFfYt.js → radiobutton-CnExI3Vj.js} +1 -1
  77. package/dist/{radiobutton-86ZvvDmJ.cjs → radiobutton-XX3zhblA.cjs} +1 -1
  78. package/dist/{searchinput-D5l64Mkb.cjs → searchinput-C8UHexyf.cjs} +1 -1
  79. package/dist/{searchinput-DN8kDZQ1.js → searchinput-DUJlxKHH.js} +1 -1
  80. package/dist/{select-CXIEM2qY.js → select-C4RDEc9h.js} +2 -2
  81. package/dist/{select-C7kAlSxs.cjs → select-CbVmSYmi.cjs} +1 -1
  82. package/dist/shared-types/datepicker.d.ts +76 -0
  83. package/dist/shared-types/index.d.ts +2 -2
  84. package/dist/shared-utils/datepicker-utils.d.ts +18 -0
  85. package/dist/shared-utils/forms/focus-within.d.ts +13 -0
  86. package/dist/{textarea-CRcSfWgX.cjs → textarea-D4yCdiFY.cjs} +1 -1
  87. package/dist/{textarea-12le8b4I.js → textarea-WktT3N3w.js} +2 -2
  88. package/dist/{textinput-CC0zxUBT.js → textinput-BPrC2neL.js} +2 -2
  89. package/dist/{textinput-Dooq2n8W.cjs → textinput-C6AcbxnQ.cjs} +1 -1
  90. package/dist/{timepicker-Cz81VQu3.js → timepicker-D77gssQg.js} +2 -2
  91. package/dist/{timepicker-BM4I5w-i.cjs → timepicker-DaqrwM-g.cjs} +1 -1
  92. package/dist/{utils-Dn5ykFtS.js → utils-BtXhdgpe.js} +2 -2
  93. package/dist/utils-dl1XA5EE.cjs +1 -0
  94. package/package.json +2 -2
  95. package/src/components/calendar/calendar.selection.test.ts +16 -0
  96. package/src/components/calendar/calendar.ts +4 -3
  97. package/src/components/datepicker/datepicker-multiple.ts +2 -6
  98. package/src/components/datepicker/datepicker-popup.ts +3 -6
  99. package/src/components/datepicker/datepicker-utils.test.ts +40 -203
  100. package/src/components/datepicker/datepicker-utils.ts +3 -94
  101. package/src/components/datepicker/datepicker.accessibility.test.ts +2 -2
  102. package/src/components/datepicker/datepicker.core.test.ts +236 -0
  103. package/src/components/datepicker/datepicker.ts +179 -115
  104. package/src/components/datepicker/index.ts +1 -0
  105. package/dist/datepicker-Cnrbqm62.cjs +0 -294
  106. package/dist/utils-63cRpWut.cjs +0 -1
@@ -1,11 +1,10 @@
1
1
  import { customElement, property, state } from 'lit/decorators.js'
2
- import { formatISODate, fromISOToDate, parseISODateString } from 'shared-utils/date-utils'
2
+ import { fromISOToDate, parseISODateString } from 'shared-utils/date-utils'
3
3
  import { html, nothing, PropertyValues } from 'lit'
4
4
  import { PktCalendar } from '@/components/calendar/calendar'
5
5
  import { PktInputElement } from '@/base-elements/input-element'
6
6
  import { Ref, createRef, ref } from 'lit/directives/ref.js'
7
7
  import converters from '@/helpers/converters'
8
- import specs from 'componentSpecs/datepicker.json'
9
8
  import '@/components/calendar'
10
9
  import '@/components/icon'
11
10
  import '@/components/input-wrapper'
@@ -18,20 +17,25 @@ import { forwardSlots } from '@/directives/slot-content'
18
17
  import {
19
18
  valueUtils,
20
19
  inputTypeUtils,
21
- calendarUtils,
22
20
  cssUtils,
23
21
  dateProcessingUtils,
24
- formUtils,
25
22
  } from './datepicker-utils'
26
23
  import { valueToArray, arrayToCsv } from 'shared-utils/value-utils'
27
- import { sleep } from 'shared-utils/utils'
28
24
  import { isIOS } from 'shared-utils/device-utils'
25
+ import { createFocusWithinTracker } from 'shared-utils/forms/focus-within'
26
+ import {
27
+ completeRangeWithTo,
28
+ datepickerHasCounter,
29
+ rangeCalendarSelection,
30
+ handleCalendarPosition as positionCalendarPopup,
31
+ } from 'shared-utils/datepicker-utils'
29
32
  import { PktDatepickerPopup } from './datepicker-popup'
30
33
  import { PktDatepickerSingle } from './datepicker-single'
31
34
  import { PktDatepickerRange } from './datepicker-range'
32
35
  import { PktDatepickerMultiple } from './datepicker-multiple'
33
36
  import { ElementProps } from '@/types/typeUtils'
34
37
  import type { IPktStringsProps } from 'shared-strings'
38
+ import { DATEPICKER_DEFAULTS, type Booleanish, type IPktDatepickerBase } from 'shared-types'
35
39
 
36
40
  type Props = ElementProps<
37
41
  PktDatepicker,
@@ -53,7 +57,30 @@ type Props = ElementProps<
53
57
  > &
54
58
  IPktStringsProps<'generic' | 'datepicker' | 'dates' | 'calendar'>
55
59
 
56
- export class PktDatepicker extends PktInputElement<Props> {
60
+ export interface IPktDatepicker extends Omit<
61
+ IPktDatepickerBase,
62
+ | 'form'
63
+ | 'min'
64
+ | 'max'
65
+ | 'maxlength'
66
+ | 'currentmonth'
67
+ | 'today'
68
+ | 'ariaDescribedby'
69
+ | 'helptextDropdownButton'
70
+ | 'useWrapper'
71
+ > {
72
+ min?: string | null
73
+ max?: string | null
74
+ maxlength?: number | null
75
+ currentmonth?: string | null
76
+ today?: string | null
77
+ ariaDescribedby?: string | null
78
+ /** @deprecated Bruk `strings={{ forms: { readMore: '…' } }}` eller `setPktStrings()`. */
79
+ helptextDropdownButton?: string | null
80
+ useWrapper?: Booleanish
81
+ }
82
+
83
+ export class PktDatepicker extends PktInputElement<Props> implements IPktDatepicker {
57
84
  /**
58
85
  * Element attributes and properties
59
86
  */
@@ -79,19 +106,19 @@ export class PktDatepicker extends PktInputElement<Props> {
79
106
  label: string = 'Datovelger'
80
107
 
81
108
  @property({ type: String })
82
- dateformat: string = specs.props.dateformat.default
109
+ dateformat: string = DATEPICKER_DEFAULTS.dateformat
83
110
 
84
111
  @property({ type: Boolean, reflect: true })
85
- multiple: boolean = specs.props.multiple.default
112
+ multiple: boolean = DATEPICKER_DEFAULTS.multiple
86
113
 
87
114
  @property({ type: Number, reflect: true })
88
115
  maxlength: number | null = null
89
116
 
90
117
  @property({ type: Boolean, reflect: true })
91
- range: boolean = specs.props.range.default
118
+ range: boolean = DATEPICKER_DEFAULTS.range
92
119
 
93
120
  @property({ type: Boolean, attribute: 'show-range-labels' })
94
- showRangeLabels: boolean = false
121
+ showRangeLabels: boolean = DATEPICKER_DEFAULTS.showRangeLabels
95
122
 
96
123
  @property({ type: String, reflect: true })
97
124
  min: string | null = null
@@ -100,10 +127,10 @@ export class PktDatepicker extends PktInputElement<Props> {
100
127
  max: string | null = null
101
128
 
102
129
  @property({ type: Boolean })
103
- weeknumbers: boolean = specs.props.weeknumbers.default
130
+ weeknumbers: boolean = DATEPICKER_DEFAULTS.weeknumbers
104
131
 
105
132
  @property({ type: Boolean, reflect: true })
106
- withcontrols: boolean = specs.props.withcontrols.default
133
+ withcontrols: boolean = DATEPICKER_DEFAULTS.withcontrols
107
134
 
108
135
  @property({ converter: converters.csvToArray })
109
136
  excludedates: string[] = []
@@ -118,13 +145,17 @@ export class PktDatepicker extends PktInputElement<Props> {
118
145
  today: string | null = null
119
146
 
120
147
  @property({ type: Boolean, reflect: true, attribute: 'calendar-open' })
121
- calendarOpen: boolean = false
148
+ calendarOpen: boolean = DATEPICKER_DEFAULTS.calendarOpen
122
149
 
123
150
  @property({ type: String })
124
151
  timezone: string = 'Europe/Oslo'
125
152
 
126
153
  @state() inputClasses = {}
127
154
 
155
+ @state() private toWithoutFrom: string | null = null
156
+
157
+ @state() private editedRangeFields: [string, string] | null = null
158
+
128
159
  /**
129
160
  * Computed properties
130
161
  */
@@ -132,6 +163,10 @@ export class PktDatepicker extends PktInputElement<Props> {
132
163
  return inputTypeUtils.getInputType()
133
164
  }
134
165
 
166
+ get hasCounter(): boolean {
167
+ return datepickerHasCounter(this.counter, this.multiple, this.maxlength)
168
+ }
169
+
135
170
  /**
136
171
  * Housekeeping / lifecycle methods
137
172
  */
@@ -141,10 +176,87 @@ export class PktDatepicker extends PktInputElement<Props> {
141
176
  if (this.timezone && this.timezone !== window.pktTz) {
142
177
  window.pktTz = this.timezone
143
178
  }
179
+ this.addEventListener('focusin', this.handleFocusIn)
180
+ this.addEventListener('focusout', this.handleFocusOut)
144
181
  }
145
182
 
146
183
  disconnectedCallback(): void {
147
184
  super.disconnectedCallback()
185
+ this.removeEventListener('focusin', this.handleFocusIn)
186
+ this.removeEventListener('focusout', this.handleFocusOut)
187
+ this.focusTracker.reset()
188
+ }
189
+
190
+ private focusTracker = createFocusWithinTracker(
191
+ (node) => this.contains(node),
192
+ (type, relatedTarget) => {
193
+ if (type === 'blur') this.dropToWithoutFrom()
194
+ this.dispatchEvent(new FocusEvent(type, { bubbles: true, composed: true, relatedTarget }))
195
+ },
196
+ )
197
+
198
+ private get rangeFieldValues(): string[] {
199
+ return this._value[0] ? this._value : ['', this.toWithoutFrom ?? '']
200
+ }
201
+
202
+ private lastCalendarSelected: string[] = []
203
+
204
+ private get calendarSelected(): string[] {
205
+ if (!this.range) return this._value
206
+ const [from, to] = this.editedRangeFields ?? this._value
207
+ const next = rangeCalendarSelection(from, to)
208
+ if (arrayToCsv(next) !== arrayToCsv(this.lastCalendarSelected)) this.lastCalendarSelected = next
209
+ return this.lastCalendarSelected
210
+ }
211
+
212
+ private trackRangeFields() {
213
+ this.editedRangeFields = [
214
+ this.currentInputElement?.value ?? '',
215
+ this.currentInputElementTo?.value ?? '',
216
+ ]
217
+ }
218
+
219
+ private syncRangeFromFields(e: FocusEvent) {
220
+ const fromInput = this.currentInputElement
221
+ const toInput = this.currentInputElementTo
222
+ if (!fromInput || !toInput) return
223
+ const from = fromInput.value
224
+ if (from && toInput.value && from > toInput.value) toInput.value = ''
225
+ const to = toInput.value
226
+ this.toWithoutFrom = from ? null : to || null
227
+ this.editedRangeFields = null
228
+ if (e.target instanceof HTMLInputElement && e.target.value) this.manageValidity(e.target)
229
+ this.value = from ? [from, to].filter(Boolean).join(',') : ''
230
+ }
231
+
232
+ private dropToWithoutFrom() {
233
+ const toInput = this.currentInputElementTo
234
+ if (this.range && toInput && !this.currentInputElement?.value) toInput.value = ''
235
+ this.toWithoutFrom = null
236
+ this.editedRangeFields = null
237
+ }
238
+
239
+ protected formResetCallback(): void {
240
+ this.toWithoutFrom = null
241
+ this.editedRangeFields = null
242
+ super.formResetCallback()
243
+ }
244
+
245
+ protected setFormValue(value: string | string[]): void {
246
+ super.setFormValue(this.multiple || this.range ? valueToArray(value) : value)
247
+ }
248
+
249
+ private handleFocusIn = (e: FocusEvent) => this.focusTracker.focusin(e.relatedTarget)
250
+
251
+ private handleFocusOut = (e: FocusEvent) => this.focusTracker.focusout(e.relatedTarget)
252
+
253
+ private focusField() {
254
+ this.currentInputElement?.focus()
255
+ }
256
+
257
+ private get focusIsInPopup(): boolean {
258
+ const popup = this.datepickerPopupRef.value?.popupRef.value
259
+ return !!popup && popup.contains(document.activeElement)
148
260
  }
149
261
 
150
262
  onInput(): void {
@@ -184,7 +296,14 @@ export class PktDatepicker extends PktInputElement<Props> {
184
296
  this._valueProperty = arrayToCsv(filteredValue)
185
297
 
186
298
  // Én dato er ingen fullført periode. Tom er en fullført endring.
187
- if (!(this.range && filteredValue.length === 1)) {
299
+ if (this.range && filteredValue.length === 1) {
300
+ this.setFormValue(this._valueProperty)
301
+ if (this.hasUpdated && this._valueProperty !== oldValue) {
302
+ this.dispatchEvent(
303
+ new CustomEvent('value-change', { detail: filteredValue, bubbles: true, composed: true }),
304
+ )
305
+ }
306
+ } else {
188
307
  super.valueChanged(this._valueProperty, oldValue)
189
308
  }
190
309
 
@@ -213,6 +332,8 @@ export class PktDatepicker extends PktInputElement<Props> {
213
332
  'excludeweekdays',
214
333
  ]
215
334
 
335
+ private static readonly POSITIONED_BY = ['calendarOpen', 'counter', 'multiple', 'maxlength']
336
+
216
337
  protected willUpdate(changedProperties: PropertyValues): void {
217
338
  super.willUpdate(changedProperties)
218
339
 
@@ -230,6 +351,12 @@ export class PktDatepicker extends PktInputElement<Props> {
230
351
  this.classList.toggle('pkt-datepicker--small', this.inputSize === 'small')
231
352
  this.classList.toggle('pkt-datepicker--xsmall', this.inputSize === 'xsmall')
232
353
  super.updated(changedProperties)
354
+ if (
355
+ this.calendarOpen &&
356
+ PktDatepicker.POSITIONED_BY.some((prop) => changedProperties.has(prop))
357
+ ) {
358
+ this.positionOpenCalendar()
359
+ }
233
360
  }
234
361
 
235
362
  /**
@@ -247,7 +374,6 @@ export class PktDatepicker extends PktInputElement<Props> {
247
374
  return { value: element } as Ref<HTMLInputElement>
248
375
  }
249
376
 
250
- calRef: Ref<PktCalendar> = createRef()
251
377
  popupRef: Ref<HTMLDivElement> = createRef()
252
378
 
253
379
  // Child component refs
@@ -311,12 +437,6 @@ export class PktDatepicker extends PktInputElement<Props> {
311
437
  .strings=${this.strings}
312
438
  @toggle-calendar=${(e: CustomEvent) => this.toggleCalendar(e.detail)}
313
439
  @input-change=${() => this.onInput()}
314
- @input-focus=${() => this.onFocus()}
315
- @input-blur=${(e: CustomEvent) => {
316
- if (!this.calRef.value?.contains(e.detail.relatedTarget as Node)) {
317
- this.onBlur()
318
- }
319
- }}
320
440
  @manage-validity=${(e: CustomEvent) => this.manageValidity(e.detail)}
321
441
  @value-change=${(e: CustomEvent) => {
322
442
  this.value = e.detail
@@ -332,7 +452,7 @@ export class PktDatepicker extends PktInputElement<Props> {
332
452
  renderRangeInput() {
333
453
  return html`
334
454
  <pkt-datepicker-range
335
- .value=${this._value}
455
+ .value=${this.rangeFieldValues}
336
456
  .inputType=${this.inputType}
337
457
  .id=${this.id}
338
458
  .label=${this.label}
@@ -349,67 +469,12 @@ export class PktDatepicker extends PktInputElement<Props> {
349
469
  .hasHelptext=${Boolean(this.helptext || this.helptextDropdown || this.hasSlotContent('helptext'))}
350
470
  .strings=${this.strings}
351
471
  @toggle-calendar=${(e: CustomEvent) => this.toggleCalendar(e.detail)}
352
- @input-change=${() => this.onInput()}
353
- @input-focus=${() => this.onFocus()}
354
- @input-blur=${(e: CustomEvent) => {
355
- if (!this.calRef.value?.contains(e.detail.relatedTarget as Node)) {
356
- this.onBlur()
357
- }
358
- }}
359
- @range-blur=${(e: CustomEvent) => {
360
- const inputFrom = this.currentInputElement
361
- const inputTo = this.currentInputElementTo
362
-
363
- // Update _value with current input values to sync with calendar
364
- if (inputFrom && inputTo) {
365
- const fromValue = inputFrom.value
366
- const toValue = inputTo.value
367
-
368
- // If from date is after to date, clear the to date
369
- if (fromValue && toValue && fromValue > toValue) {
370
- inputTo.value = ''
371
- this._value = [fromValue]
372
- this.value = fromValue
373
- } else {
374
- const newValues = [fromValue, toValue].filter(Boolean)
375
- if (
376
- newValues.length > 0 &&
377
- (newValues[0] !== this._value[0] || newValues[1] !== this._value[1])
378
- ) {
379
- this._value = newValues
380
- this.value = newValues.join(',')
381
- }
382
- }
383
- }
384
-
385
- dateProcessingUtils.processRangeBlur(
386
- e.detail.event,
387
- e.detail.values,
388
- this.calRef,
389
- () => this.clearInputValue(),
390
- (input) => this.manageValidity(input),
391
- )
472
+ @input-change=${() => {
473
+ this.trackRangeFields()
474
+ this.onInput()
392
475
  }}
476
+ @range-blur=${(e: CustomEvent) => this.syncRangeFromFields(e.detail.event)}
393
477
  @manage-validity=${(e: CustomEvent) => this.manageValidity(e.detail)}
394
- @validate-date-input=${(e: CustomEvent) => {
395
- formUtils.validateDateInput(
396
- e.detail,
397
- this.internals,
398
- this.min,
399
- this.max,
400
- this.pktStrings.get('validation'),
401
- )
402
- }}
403
- @handle-date-select=${(e: CustomEvent) => {
404
- const date = fromISOToDate(e.detail)
405
- if (date) {
406
- const formattedDate = formatISODate(date)
407
- // Only update calendar if the date is different from current values
408
- if (this._value[0] !== formattedDate && this._value[1] !== formattedDate) {
409
- this.calRef?.value?.handleDateSelect(date)
410
- }
411
- }
412
- }}
413
478
  @input-changed=${() => {
414
479
  this.touched = true
415
480
  }}
@@ -438,12 +503,6 @@ export class PktDatepicker extends PktInputElement<Props> {
438
503
  .strings=${this.strings}
439
504
  @toggle-calendar=${(e: CustomEvent) => this.toggleCalendar(e.detail)}
440
505
  @input-change=${() => this.onInput()}
441
- @input-focus=${() => this.onFocus()}
442
- @input-blur=${(e: CustomEvent) => {
443
- if (!this.calRef.value?.contains(e.detail.relatedTarget as Node)) {
444
- this.onBlur()
445
- }
446
- }}
447
506
  @add-to-selected=${(e: CustomEvent) => this.addToSelected(e.detail)}
448
507
  @input-changed=${() => {
449
508
  this.touched = true
@@ -463,7 +522,7 @@ export class PktDatepicker extends PktInputElement<Props> {
463
522
  ?weeknumbers=${this.weeknumbers}
464
523
  ?withcontrols=${this.withcontrols}
465
524
  .maxMultiple=${this.maxlength}
466
- .selected=${this._value}
525
+ .selected=${this.calendarSelected}
467
526
  .earliest=${this.min}
468
527
  .latest=${this.max}
469
528
  .excludedates=${Array.isArray(this.excludedates)
@@ -473,8 +532,18 @@ export class PktDatepicker extends PktInputElement<Props> {
473
532
  .currentmonth=${this.currentmonth ? parseISODateString(this.currentmonth) : null}
474
533
  .today=${this.today}
475
534
  @date-selected=${(e: CustomEvent) => {
476
- this.value = dateProcessingUtils.processDateSelection(e.detail, this.multiple, this.range)
477
- this._value = e.detail
535
+ const selected: string[] = this.range
536
+ ? completeRangeWithTo(
537
+ e.detail,
538
+ this._value[0] ? null : this.toWithoutFrom,
539
+ valueToArray(this.excludedates),
540
+ this.excludeweekdays,
541
+ )
542
+ : e.detail
543
+ this.toWithoutFrom = null
544
+ this.editedRangeFields = null
545
+ this.value = dateProcessingUtils.processDateSelection(selected, this.multiple, this.range)
546
+ this._value = selected
478
547
  dateProcessingUtils.updateInputValues(
479
548
  this.inputRef,
480
549
  this.inputRefTo,
@@ -483,9 +552,11 @@ export class PktDatepicker extends PktInputElement<Props> {
483
552
  this.multiple,
484
553
  (input) => this.manageValidity(input),
485
554
  )
555
+ if (!this.multiple && (!this.range || this._value.length === 2)) this.focusField()
556
+ if (selected !== e.detail) this.hideCalendar()
486
557
  }}
487
558
  @close=${() => {
488
- this.onBlur()
559
+ if (this.focusIsInPopup) this.focusField()
489
560
  this.hideCalendar()
490
561
  }}
491
562
  ${ref(this.datepickerPopupRef)}
@@ -510,7 +581,7 @@ export class PktDatepicker extends PktInputElement<Props> {
510
581
  .strings=${this.strings}
511
582
  label="${this.label}"
512
583
  forId="${this.id}-input"
513
- ?counter=${this.multiple && !!this.maxlength}
584
+ ?counter=${this.hasCounter}
514
585
  .counterCurrent=${this.value ? this._value.length : 0}
515
586
  .counterMaxLength=${this.maxlength}
516
587
  ?disabled=${this.isDisabled}
@@ -538,13 +609,8 @@ export class PktDatepicker extends PktInputElement<Props> {
538
609
  strings=${this.strings}
539
610
  id-base=${this.id}
540
611
  @date-tag-removed=${(e: CustomEvent) => {
541
- const popup = this.datepickerPopupRef.value
542
612
  const date = fromISOToDate(e.detail)
543
- if (popup && date && typeof popup.handleDateSelect === 'function') {
544
- popup.handleDateSelect(date)
545
- } else {
546
- this.calRef.value?.handleDateSelect(date)
547
- }
613
+ if (date) this.datepickerPopupRef.value?.handleDateSelect(date)
548
614
  }}
549
615
  ></pkt-date-tags>`
550
616
  : nothing}
@@ -565,31 +631,29 @@ export class PktDatepicker extends PktInputElement<Props> {
565
631
  */
566
632
 
567
633
  handleCalendarPosition() {
568
- const hasCounter = this.multiple && !!this.maxlength
569
- calendarUtils.handleCalendarPosition(this.popupRef, this.inputRef, hasCounter)
634
+ positionCalendarPopup(
635
+ this.datepickerPopupRef.value?.popupRef.value ?? null,
636
+ this.currentInputElement ?? null,
637
+ this.hasCounter,
638
+ )
570
639
  }
571
640
 
572
- addToSelected = (e: Event | KeyboardEvent) => {
641
+ private async positionOpenCalendar() {
573
642
  const popup = this.datepickerPopupRef.value
574
- if (popup && typeof popup.addToSelected === 'function') {
575
- return popup.addToSelected(e, this.min, this.max)
576
- }
577
- return calendarUtils.addToSelected(e, this.calRef, this.min, this.max)
643
+ await popup?.updateComplete
644
+ await (popup?.calendarRef.value as PktCalendar | undefined)?.updateComplete
645
+ if (this.calendarOpen) this.handleCalendarPosition()
578
646
  }
579
647
 
648
+ addToSelected = (e: Event | KeyboardEvent) =>
649
+ this.datepickerPopupRef.value?.addToSelected(e, this.min, this.max)
650
+
580
651
  public async showCalendar() {
581
652
  const popup = this.datepickerPopupRef.value
582
653
  this.calendarOpen = true
583
- if (popup && typeof popup.show === 'function') {
584
- popup.show()
585
- if (isIOS()) popup.focusOnCurrentDate()
586
- return
587
- }
588
- await sleep(20)
589
- this.handleCalendarPosition()
590
- if (isIOS()) {
591
- this.calRef.value?.focusOnCurrentDate()
592
- }
654
+ if (!popup) return
655
+ popup.show()
656
+ if (isIOS()) popup.focusOnCurrentDate()
593
657
  }
594
658
 
595
659
  public hideCalendar() {
@@ -4,4 +4,5 @@ import { PktDatepickerRange } from './datepicker-range'
4
4
  import { PktDatepickerMultiple } from './datepicker-multiple'
5
5
 
6
6
  export { PktDatepicker, PktDatepickerSingle, PktDatepickerRange, PktDatepickerMultiple }
7
+ export type { IPktDatepicker } from './datepicker'
7
8
  export default PktDatepicker