@oslokommune/punkt-elements 14.0.2 → 14.0.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 (30) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/calendar-BtShW7ER.cjs +90 -0
  3. package/dist/{calendar-Bz27nuTP.js → calendar-yxjSI4wd.js} +766 -682
  4. package/dist/datepicker-D0q75U1Z.js +1463 -0
  5. package/dist/datepicker-DDV382Uu.cjs +271 -0
  6. package/dist/index.d.ts +118 -83
  7. package/dist/pkt-calendar.cjs +1 -1
  8. package/dist/pkt-calendar.js +1 -1
  9. package/dist/pkt-datepicker.cjs +1 -1
  10. package/dist/pkt-datepicker.js +2 -2
  11. package/dist/pkt-index.cjs +1 -1
  12. package/dist/pkt-index.js +3 -3
  13. package/package.json +2 -2
  14. package/src/components/calendar/calendar.ts +372 -414
  15. package/src/components/calendar/helpers/calendar-grid.ts +93 -0
  16. package/src/components/calendar/helpers/date-validation.ts +86 -0
  17. package/src/components/calendar/helpers/index.ts +49 -0
  18. package/src/components/calendar/helpers/keyboard-navigation.ts +54 -0
  19. package/src/components/calendar/helpers/selection-manager.ts +184 -0
  20. package/src/components/datepicker/datepicker-base.ts +151 -0
  21. package/src/components/datepicker/datepicker-multiple.ts +7 -114
  22. package/src/components/datepicker/datepicker-range.ts +21 -141
  23. package/src/components/datepicker/datepicker-single.ts +7 -115
  24. package/src/components/datepicker/datepicker-types.ts +56 -0
  25. package/src/components/datepicker/datepicker-utils.test.ts +730 -0
  26. package/src/components/datepicker/datepicker-utils.ts +338 -9
  27. package/src/components/datepicker/datepicker.ts +25 -1
  28. package/dist/calendar-Dz1Cnzx5.cjs +0 -115
  29. package/dist/datepicker-CnCOXI2x.cjs +0 -289
  30. package/dist/datepicker-DsqM01iU.js +0 -1355
@@ -10,7 +10,17 @@ import { Ref } from 'lit/directives/ref.js'
10
10
  import { PktCalendar } from '@/components/calendar/calendar'
11
11
 
12
12
  /**
13
- * Utility functions for PktDatepicker
13
+ * Utility functions for PktDatepicker component
14
+ *
15
+ * This module provides helper functions organized by concern:
16
+ * - valueUtils: Value parsing and validation
17
+ * - inputTypeUtils: Input type detection
18
+ * - formUtils: Form submission and validation
19
+ * - calendarUtils: Calendar interaction and positioning
20
+ * - eventUtils: Event listener creation
21
+ * - cssUtils: CSS class generation
22
+ * - dateProcessingUtils: Date value processing
23
+ * - keyboardUtils: Keyboard navigation handling
14
24
  */
15
25
 
16
26
  /**
@@ -19,6 +29,19 @@ import { PktCalendar } from '@/components/calendar/calendar'
19
29
  export const valueUtils = {
20
30
  /**
21
31
  * Ensures name attribute ends with [] for multiple/range inputs
32
+ *
33
+ * For form submission to work correctly with multiple values,
34
+ * the input name must end with [] to indicate it's an array.
35
+ *
36
+ * @param name - The original name attribute value
37
+ * @param isMultiple - Whether this is a multiple-date picker
38
+ * @param isRange - Whether this is a range date picker
39
+ * @returns The normalized name with [] suffix if needed, or null if name is null
40
+ *
41
+ * @example
42
+ * normalizeNameForMultiple('dates', true, false) // Returns 'dates[]'
43
+ * normalizeNameForMultiple('dates[]', true, false) // Returns 'dates[]' (no change)
44
+ * normalizeNameForMultiple('date', false, false) // Returns 'date' (no change)
22
45
  */
23
46
  normalizeNameForMultiple(
24
47
  name: string | null,
@@ -34,6 +57,14 @@ export const valueUtils = {
34
57
 
35
58
  /**
36
59
  * Validates that a range has valid order (start <= end)
60
+ *
61
+ * @param values - Array of date strings (ISO format YYYY-MM-DD)
62
+ * @returns True if range is valid or incomplete, false if end date is before start date
63
+ *
64
+ * @example
65
+ * validateRangeOrder(['2024-01-01', '2024-01-31']) // Returns true
66
+ * validateRangeOrder(['2024-01-31', '2024-01-01']) // Returns false
67
+ * validateRangeOrder(['2024-01-01']) // Returns true (incomplete range)
37
68
  */
38
69
  validateRangeOrder(values: string[]): boolean {
39
70
  if (!values || values.length !== 2) return true // Not a complete range
@@ -42,6 +73,13 @@ export const valueUtils = {
42
73
 
43
74
  /**
44
75
  * Sorts date strings chronologically
76
+ *
77
+ * @param dates - Array of date strings in ISO format (YYYY-MM-DD)
78
+ * @returns New array with dates sorted from earliest to latest
79
+ *
80
+ * @example
81
+ * sortDates(['2024-03-15', '2024-01-10', '2024-02-20'])
82
+ * // Returns ['2024-01-10', '2024-02-20', '2024-03-15']
45
83
  */
46
84
  sortDates(dates: string[]): string[] {
47
85
  return sortDateStrings(dates)
@@ -49,6 +87,28 @@ export const valueUtils = {
49
87
 
50
88
  /**
51
89
  * Filters dates to only include selectable ones based on constraints
90
+ *
91
+ * Removes dates that:
92
+ * - Fall outside the min/max range
93
+ * - Are in the excludedDates array
94
+ * - Fall on excluded weekdays (0=Sunday, 6=Saturday)
95
+ *
96
+ * @param dates - Array of date strings to filter
97
+ * @param min - Minimum allowed date (ISO format)
98
+ * @param max - Maximum allowed date (ISO format)
99
+ * @param excludedDates - Array of specific dates to exclude
100
+ * @param excludedWeekdays - Array of weekday numbers to exclude ('0'-'6')
101
+ * @returns Filtered array containing only selectable dates
102
+ *
103
+ * @example
104
+ * filterSelectableDates(
105
+ * ['2024-01-01', '2024-01-06', '2024-01-15'],
106
+ * '2024-01-05',
107
+ * '2024-01-20',
108
+ * ['2024-01-15'],
109
+ * ['0', '6'] // Exclude weekends
110
+ * )
111
+ * // Returns ['2024-01-06'] (after min, not excluded, not a weekend)
52
112
  */
53
113
  filterSelectableDates(
54
114
  dates: string[],
@@ -67,7 +127,16 @@ export const valueUtils = {
67
127
  export const inputTypeUtils = {
68
128
  /**
69
129
  * Determines the appropriate input type based on device
70
- * Mobile Safari does not play well with type="date" amd custom datepickers
130
+ *
131
+ * Mobile Safari does not play well with type="date" and custom datepickers,
132
+ * so we use type="text" on iOS devices to avoid the native date picker
133
+ * interfering with our custom calendar component.
134
+ *
135
+ * @returns 'text' for iOS devices, 'date' for all other devices
136
+ *
137
+ * @example
138
+ * const inputType = getInputType()
139
+ * // Returns 'text' on iPhone/iPad, 'date' on desktop/Android
71
140
  */
72
141
  getInputType(): string {
73
142
  return isIOS() ? 'text' : 'date'
@@ -80,6 +149,14 @@ export const inputTypeUtils = {
80
149
  export const formUtils = {
81
150
  /**
82
151
  * Submits the form that contains the given element
152
+ *
153
+ * Uses ElementInternals API to access the associated form and trigger submission.
154
+ * Requires the element to have attachInternals() called.
155
+ *
156
+ * @param element - The form-associated custom element
157
+ *
158
+ * @example
159
+ * submitForm(this) // From within a custom element
83
160
  */
84
161
  submitForm(element: HTMLElement): void {
85
162
  const form = (element as any).internals?.form as HTMLFormElement
@@ -90,6 +167,18 @@ export const formUtils = {
90
167
 
91
168
  /**
92
169
  * Submits form if available, otherwise executes fallback action
170
+ *
171
+ * Useful for Enter key handling where we want to submit the form,
172
+ * but provide a fallback behavior (like moving focus) if not in a form.
173
+ *
174
+ * @param internals - ElementInternals instance with optional form reference
175
+ * @param fallbackAction - Function to call if no form is available
176
+ *
177
+ * @example
178
+ * submitFormOrFallback(
179
+ * this.internals,
180
+ * () => this.inputRef.value?.blur()
181
+ * )
93
182
  */
94
183
  submitFormOrFallback(internals: any, fallbackAction: () => void): void {
95
184
  const form = internals?.form as HTMLFormElement
@@ -102,6 +191,24 @@ export const formUtils = {
102
191
 
103
192
  /**
104
193
  * Validates a date input and sets validity state
194
+ *
195
+ * Checks if the input value falls within the min/max range and
196
+ * sets appropriate validity flags using the ElementInternals API.
197
+ *
198
+ * @param input - The input element to validate
199
+ * @param internals - ElementInternals instance for setting validity
200
+ * @param min - Minimum allowed date (ISO format)
201
+ * @param max - Maximum allowed date (ISO format)
202
+ * @param strings - Optional localized error messages
203
+ *
204
+ * @example
205
+ * validateDateInput(
206
+ * inputElement,
207
+ * this.internals,
208
+ * '2024-01-01',
209
+ * '2024-12-31',
210
+ * { forms: { messages: { rangeUnderflow: 'Date too early' } } }
211
+ * )
105
212
  */
106
213
  validateDateInput(
107
214
  input: HTMLInputElement,
@@ -135,6 +242,23 @@ export const formUtils = {
135
242
  export const calendarUtils = {
136
243
  /**
137
244
  * Adds a date to selected dates if it's valid
245
+ *
246
+ * Used for multiple date selection - validates the input date against
247
+ * min/max constraints before adding it to the calendar's selected dates.
248
+ * Clears the input field after processing.
249
+ *
250
+ * @param event - Input or keyboard event containing the target input
251
+ * @param calendarRef - Lit ref to the calendar component
252
+ * @param min - Minimum allowed date (ISO format)
253
+ * @param max - Maximum allowed date (ISO format)
254
+ *
255
+ * @example
256
+ * addToSelected(
257
+ * blurEvent,
258
+ * this.calendarRef,
259
+ * '2024-01-01',
260
+ * '2024-12-31'
261
+ * )
138
262
  */
139
263
  addToSelected(
140
264
  event: Event | KeyboardEvent,
@@ -163,6 +287,25 @@ export const calendarUtils = {
163
287
 
164
288
  /**
165
289
  * Handles calendar positioning based on viewport and input position
290
+ *
291
+ * Intelligently positions the calendar popup either above or below the input
292
+ * depending on available viewport space. Accounts for optional counter elements.
293
+ *
294
+ * The positioning logic:
295
+ * 1. By default, positions calendar below the input (top: 100%)
296
+ * 2. If calendar would overflow viewport bottom AND there's space above, positions above
297
+ * 3. Accounts for counter height (30px) when hasCounter is true
298
+ *
299
+ * @param popupRef - Lit ref to the calendar popup element
300
+ * @param inputRef - Lit ref to the input element
301
+ * @param hasCounter - Whether the input has a character counter below it
302
+ *
303
+ * @example
304
+ * handleCalendarPosition(
305
+ * this.popupRef,
306
+ * this.inputRef,
307
+ * this.multiple && this.maxlength !== null
308
+ * )
166
309
  */
167
310
  handleCalendarPosition(
168
311
  popupRef: Ref<HTMLDivElement>,
@@ -194,10 +337,34 @@ export const calendarUtils = {
194
337
 
195
338
  /**
196
339
  * Event handling utilities
340
+ *
341
+ * Factory functions for creating event listeners with proper closure over component state.
197
342
  */
198
343
  export const eventUtils = {
199
344
  /**
200
- * Creates a document click listener for closing calendar
345
+ * Creates a document click listener for closing calendar on outside clicks
346
+ *
347
+ * Returns a function that closes the calendar when clicking outside the
348
+ * datepicker component (input, button, or calendar popup).
349
+ *
350
+ * @param inputRef - Lit ref to the primary input element
351
+ * @param inputRefTo - Lit ref to the secondary input (for range picker) or null
352
+ * @param btnRef - Lit ref to the calendar button
353
+ * @param getCalendarOpen - Function returning current calendar open state
354
+ * @param onBlur - Callback to execute on blur
355
+ * @param hideCalendar - Callback to hide the calendar
356
+ * @returns Event listener function to attach to document
357
+ *
358
+ * @example
359
+ * const listener = createDocumentClickListener(
360
+ * this.inputRef,
361
+ * null,
362
+ * this.btnRef,
363
+ * () => this.calendarOpen,
364
+ * () => this.handleBlur(),
365
+ * () => this.calendarOpen = false
366
+ * )
367
+ * document.addEventListener('click', listener)
201
368
  */
202
369
  createDocumentClickListener(
203
370
  inputRef: Ref<HTMLInputElement>,
@@ -225,6 +392,19 @@ export const eventUtils = {
225
392
 
226
393
  /**
227
394
  * Creates a document keydown listener for ESC key
395
+ *
396
+ * Returns a function that closes the calendar when Escape is pressed.
397
+ *
398
+ * @param getCalendarOpen - Function returning current calendar open state
399
+ * @param hideCalendar - Callback to hide the calendar
400
+ * @returns Event listener function to attach to document
401
+ *
402
+ * @example
403
+ * const listener = createDocumentKeydownListener(
404
+ * () => this.calendarOpen,
405
+ * () => this.calendarOpen = false
406
+ * )
407
+ * document.addEventListener('keydown', listener)
228
408
  */
229
409
  createDocumentKeydownListener(
230
410
  getCalendarOpen: () => boolean,
@@ -239,6 +419,22 @@ export const eventUtils = {
239
419
 
240
420
  /**
241
421
  * Handles focus out events for calendar popup
422
+ *
423
+ * Closes the calendar when focus leaves the datepicker component entirely.
424
+ * Used for keyboard navigation accessibility.
425
+ *
426
+ * @param event - The focusout event
427
+ * @param element - The container element to check focus against
428
+ * @param onBlur - Callback to execute on blur
429
+ * @param hideCalendar - Callback to hide the calendar
430
+ *
431
+ * @example
432
+ * handleFocusOut(
433
+ * event,
434
+ * this,
435
+ * () => this.handleBlur(),
436
+ * () => this.calendarOpen = false
437
+ * )
242
438
  */
243
439
  handleFocusOut(
244
440
  event: FocusEvent,
@@ -255,10 +451,24 @@ export const eventUtils = {
255
451
 
256
452
  /**
257
453
  * CSS class utilities
454
+ *
455
+ * Functions for generating CSS class objects for use with classMap directive.
258
456
  */
259
457
  export const cssUtils = {
260
458
  /**
261
459
  * Generates input classes for datepicker
460
+ *
461
+ * @param fullwidth - Whether input should take full width
462
+ * @param showRangeLabels - Whether range labels are visible
463
+ * @param multiple - Whether this is a multiple date picker
464
+ * @param range - Whether this is a range date picker
465
+ * @param readonly - Whether input is readonly
466
+ * @param inputType - The input type ('date' or 'text')
467
+ * @returns Object with class names as keys and boolean values
468
+ *
469
+ * @example
470
+ * const classes = getInputClasses(true, false, false, false, false, 'date')
471
+ * // Returns { 'pkt-input': true, 'pkt-input--fullwidth': true, ... }
262
472
  */
263
473
  getInputClasses(
264
474
  fullwidth: boolean,
@@ -280,7 +490,13 @@ export const cssUtils = {
280
490
  },
281
491
 
282
492
  /**
283
- * Generates button classes for datepicker
493
+ * Generates button classes for calendar button
494
+ *
495
+ * @returns Object with class names for the calendar toggle button
496
+ *
497
+ * @example
498
+ * const classes = getButtonClasses()
499
+ * // Returns { 'pkt-btn': true, 'pkt-btn--icon-only': true, ... }
284
500
  */
285
501
  getButtonClasses() {
286
502
  return {
@@ -294,6 +510,13 @@ export const cssUtils = {
294
510
 
295
511
  /**
296
512
  * Generates range label classes
513
+ *
514
+ * @param showRangeLabels - Whether range labels should be visible
515
+ * @returns Object with class names for range label elements
516
+ *
517
+ * @example
518
+ * const classes = getRangeLabelClasses(true)
519
+ * // Returns { 'pkt-input-prefix': true, 'pkt-hide': false }
297
520
  */
298
521
  getRangeLabelClasses(showRangeLabels: boolean) {
299
522
  return {
@@ -305,10 +528,24 @@ export const cssUtils = {
305
528
 
306
529
  /**
307
530
  * Date value processing utilities
531
+ *
532
+ * Functions for transforming and updating date values between calendar and input elements.
308
533
  */
309
534
  export const dateProcessingUtils = {
310
535
  /**
311
- * Handles date selection from calendar events
536
+ * Processes date selection from calendar events
537
+ *
538
+ * Converts the calendar's date selection into the appropriate string format
539
+ * for the input element's value.
540
+ *
541
+ * @param detail - Date selection detail from calendar event (string or array)
542
+ * @param multiple - Whether this is a multiple date picker
543
+ * @param range - Whether this is a range date picker
544
+ * @returns Formatted date string (single date or comma-separated dates)
545
+ *
546
+ * @example
547
+ * processDateSelection(['2024-01-15'], false, false) // Returns '2024-01-15'
548
+ * processDateSelection(['2024-01-15', '2024-01-20'], true, false) // Returns '2024-01-15,2024-01-20'
312
549
  */
313
550
  processDateSelection(detail: any, multiple: boolean, range: boolean): string {
314
551
  if (!multiple && !range) {
@@ -322,6 +559,30 @@ export const dateProcessingUtils = {
322
559
 
323
560
  /**
324
561
  * Updates input values after calendar selection
562
+ *
563
+ * Synchronizes input element values with the calendar's selected dates.
564
+ * Handles single, range, and multiple date scenarios differently.
565
+ *
566
+ * - Single: Updates single input with first date
567
+ * - Range: Updates two inputs with start and end dates
568
+ * - Multiple: Does nothing (dates shown as tags, not in input)
569
+ *
570
+ * @param inputRef - Lit ref to the primary input element
571
+ * @param inputRefTo - Lit ref to the secondary input (for range) or null
572
+ * @param values - Array of selected date strings
573
+ * @param range - Whether this is a range date picker
574
+ * @param multiple - Whether this is a multiple date picker
575
+ * @param manageValidity - Callback to validate the input
576
+ *
577
+ * @example
578
+ * updateInputValues(
579
+ * this.inputRef,
580
+ * this.inputRefTo,
581
+ * ['2024-01-15', '2024-01-20'],
582
+ * true, // range
583
+ * false,
584
+ * (input) => this.dispatchManageValidity(input)
585
+ * )
325
586
  */
326
587
  updateInputValues(
327
588
  inputRef: Ref<HTMLInputElement>,
@@ -346,6 +607,27 @@ export const dateProcessingUtils = {
346
607
 
347
608
  /**
348
609
  * Processes blur events for range inputs
610
+ *
611
+ * Handles the logic when a range input loses focus:
612
+ * - If input has value: validates it and updates the calendar to reflect the typed date
613
+ * - If input is empty but range has start date: clears the entire range
614
+ *
615
+ * This ensures the range picker calendar stays synchronized when users type dates directly.
616
+ *
617
+ * @param event - The blur event from the input
618
+ * @param values - Current array of selected dates [start, end]
619
+ * @param calendarRef - Lit ref to the calendar component
620
+ * @param clearInputValue - Callback to clear the date selection
621
+ * @param manageValidity - Callback to validate the input
622
+ *
623
+ * @example
624
+ * processRangeBlur(
625
+ * blurEvent,
626
+ * ['2024-01-15', '2024-01-20'],
627
+ * this.calendarRef,
628
+ * () => this.value = [],
629
+ * (input) => this.dispatchManageValidity(input)
630
+ * )
349
631
  */
350
632
  processRangeBlur(
351
633
  event: Event,
@@ -359,10 +641,8 @@ export const dateProcessingUtils = {
359
641
  manageValidity(target)
360
642
  const date = fromISOToDate(target.value)
361
643
  if (date) {
362
- if (values[0] !== target.value && values[1]) {
363
- clearInputValue()
364
- calendarRef?.value?.handleDateSelect(date)
365
- }
644
+ // Always update the calendar when a valid date is typed
645
+ calendarRef?.value?.handleDateSelect(date)
366
646
  }
367
647
  } else if (values[0]) {
368
648
  clearInputValue()
@@ -372,10 +652,47 @@ export const dateProcessingUtils = {
372
652
 
373
653
  /**
374
654
  * Keyboard navigation utilities
655
+ *
656
+ * Functions for handling keyboard interactions with proper accessibility support.
375
657
  */
376
658
  export const keyboardUtils = {
377
659
  /**
378
660
  * Handles common keyboard interactions for datepicker inputs
661
+ *
662
+ * Provides consistent keyboard navigation across all datepicker types:
663
+ * - Space: Opens calendar
664
+ * - Enter: Submits form, focuses next input, or blurs (priority order)
665
+ * - Comma: Adds date to selection (multiple) or blurs input
666
+ *
667
+ * The handler prioritizes callbacks in the order provided, allowing
668
+ * different behaviors for single, multiple, and range pickers.
669
+ *
670
+ * @param event - The keyboard event
671
+ * @param toggleCalendar - Callback to toggle calendar visibility
672
+ * @param submitForm - Optional callback to submit the form (highest priority on Enter)
673
+ * @param focusNextInput - Optional callback to focus next input (medium priority on Enter)
674
+ * @param blurInput - Optional callback to blur current input (lowest priority on Enter/Comma)
675
+ * @param commaHandler - Optional callback for comma key (overrides default blur behavior)
676
+ *
677
+ * @example
678
+ * // Single datepicker: Enter submits form or blurs
679
+ * handleInputKeydown(
680
+ * event,
681
+ * (e) => this.toggleCalendar(e),
682
+ * () => formUtils.submitFormOrFallback(this.internals, () => this.inputRef.value?.blur()),
683
+ * undefined,
684
+ * () => this.inputRef.value?.blur()
685
+ * )
686
+ *
687
+ * @example
688
+ * // Range datepicker: Enter moves to next input
689
+ * handleInputKeydown(
690
+ * event,
691
+ * (e) => this.toggleCalendar(e),
692
+ * () => formUtils.submitFormOrFallback(this.internals, () => this.inputRefTo.value?.focus()),
693
+ * () => this.inputRefTo.value?.focus(),
694
+ * () => this.inputRef.value?.blur()
695
+ * )
379
696
  */
380
697
  handleInputKeydown(
381
698
  event: KeyboardEvent,
@@ -415,6 +732,18 @@ export const keyboardUtils = {
415
732
 
416
733
  /**
417
734
  * Handles keyboard interactions for calendar button
735
+ *
736
+ * Ensures the calendar button is keyboard accessible by responding
737
+ * to Enter and Space keys (standard button activation keys).
738
+ *
739
+ * @param event - The keyboard event
740
+ * @param toggleCalendar - Callback to toggle calendar visibility
741
+ *
742
+ * @example
743
+ * handleButtonKeydown(
744
+ * event,
745
+ * (e) => this.dispatchToggleCalendar(e)
746
+ * )
418
747
  */
419
748
  handleButtonKeydown(event: KeyboardEvent, toggleCalendar: (e: Event) => void): void {
420
749
  const { key } = event
@@ -347,6 +347,28 @@ export class PktDatepicker extends PktInputElement<Props> {
347
347
  }
348
348
  }}
349
349
  @range-blur=${(e: CustomEvent) => {
350
+ const inputFrom = this.currentInputElement
351
+ const inputTo = this.currentInputElementTo
352
+
353
+ // Update _value with current input values to sync with calendar
354
+ if (inputFrom && inputTo) {
355
+ const fromValue = inputFrom.value
356
+ const toValue = inputTo.value
357
+
358
+ // If from date is after to date, clear the to date
359
+ if (fromValue && toValue && fromValue > toValue) {
360
+ inputTo.value = ''
361
+ this._value = [fromValue]
362
+ this.value = fromValue
363
+ } else {
364
+ const newValues = [fromValue, toValue].filter(Boolean)
365
+ if (newValues.length > 0 && (newValues[0] !== this._value[0] || newValues[1] !== this._value[1])) {
366
+ this._value = newValues
367
+ this.value = newValues.join(',')
368
+ }
369
+ }
370
+ }
371
+
350
372
  dateProcessingUtils.processRangeBlur(
351
373
  e.detail.event,
352
374
  e.detail.values,
@@ -362,7 +384,9 @@ export class PktDatepicker extends PktInputElement<Props> {
362
384
  @handle-date-select=${(e: CustomEvent) => {
363
385
  const date = fromISOToDate(e.detail)
364
386
  if (date) {
365
- if (this._value[1] !== formatISODate(date)) {
387
+ const formattedDate = formatISODate(date)
388
+ // Only update calendar if the date is different from current values
389
+ if (this._value[0] !== formattedDate && this._value[1] !== formattedDate) {
366
390
  this.calRef?.value?.handleDateSelect(date)
367
391
  }
368
392
  }