@descope/web-components-ui 1.92.0 → 1.94.0

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.
@@ -11,9 +11,7 @@ import {
11
11
  newDate,
12
12
  isValidTimestamp,
13
13
  formatTimestamp,
14
- isSupportedKey,
15
14
  isNumber,
16
- getKeyMap,
17
15
  getCurrentTime,
18
16
  overrideConstructedStylesheet,
19
17
  } from './helpers';
@@ -70,12 +68,10 @@ class RawDateFieldClass extends BaseInputClass {
70
68
  }
71
69
  }
72
70
 
73
- #yearDateCounter = new DateCounter(counterConfig.YEAR, this.onDateCounterChange.bind(this));
74
-
75
71
  dateCounters = [
76
72
  new DateCounter(counterConfig.MONTH, this.onDateCounterChange.bind(this)),
77
73
  new DateCounter(counterConfig.DAY, this.onDateCounterChange.bind(this)),
78
- this.#yearDateCounter,
74
+ new DateCounter(counterConfig.YEAR, this.onDateCounterChange.bind(this)),
79
75
  ];
80
76
 
81
77
  static get observedAttributes() {
@@ -190,6 +186,9 @@ class RawDateFieldClass extends BaseInputClass {
190
186
  }
191
187
 
192
188
  get value() {
189
+ if (this.isInvalidDate()) {
190
+ return '';
191
+ }
193
192
  return this.timestamp;
194
193
  }
195
194
 
@@ -248,8 +247,8 @@ class RawDateFieldClass extends BaseInputClass {
248
247
  this.inputElement.addEventListener('focus', this.onFocus.bind(this));
249
248
  this.inputElement.addEventListener('blur', this.onBlur.bind(this));
250
249
  this.inputElement.addEventListener('click', this.handleMouseCaretPositionChange.bind(this));
251
- this.inputElement.addEventListener('keydown', this.handleNavKeys.bind(this));
252
- this.inputElement.addEventListener('keydown', this.handleDigitKeys.bind(this));
250
+ this.inputElement.addEventListener('keydown', this.handleArrowKeys.bind(this));
251
+ this.inputElement.addEventListener('beforeinput', this.handleInput.bind(this));
253
252
 
254
253
  forwardAttrs(this, this.inputElement, {
255
254
  includeAttrs: [
@@ -275,6 +274,17 @@ class RawDateFieldClass extends BaseInputClass {
275
274
  });
276
275
  }
277
276
 
277
+ handleInput(e) {
278
+ e.preventDefault();
279
+
280
+ if (e.data && isNumber(e.data)) {
281
+ this.parseDigits(e.data);
282
+ this.updateCountersDisplay();
283
+ } else if (e.inputType) {
284
+ this.handleNavKeys(e);
285
+ }
286
+ }
287
+
278
288
  initPopover() {
279
289
  this.baseElement.trigger = ['click'];
280
290
  this.baseElement.withBackdrop = true;
@@ -443,6 +453,8 @@ class RawDateFieldClass extends BaseInputClass {
443
453
 
444
454
  if (!this.inputElement.value) {
445
455
  this.inputElement.value = this.format;
456
+ // If no value, on focus select the first part of the format placeholder
457
+ this.selectedCounterIdx = 0;
446
458
  this.setInputSelectionRange();
447
459
  }
448
460
  }
@@ -470,18 +482,14 @@ class RawDateFieldClass extends BaseInputClass {
470
482
  this.setAttribute('pattern', formats[format].pattern);
471
483
  }
472
484
 
473
- handleDigitKeys(e) {
474
- if (isNumber(e.key)) {
475
- e.preventDefault();
476
-
477
- this.activeCounter.add(e.key);
478
-
479
- if (this.activeCounter.isFull) {
480
- this.selectNextCounter();
481
- }
485
+ parseDigits(value) {
486
+ this.activeCounter.add(value);
482
487
 
483
- this.setInputSelectionRange();
488
+ if (this.activeCounter.isFull) {
489
+ this.selectNextCounter();
484
490
  }
491
+
492
+ this.setInputSelectionRange();
485
493
  }
486
494
 
487
495
  getCounterIdx(caretPos) {
@@ -553,52 +561,47 @@ class RawDateFieldClass extends BaseInputClass {
553
561
  });
554
562
  }
555
563
 
564
+ handleArrowKeys(e) {
565
+ if (e.key === 'ArrowUp') {
566
+ this.activeCounter.inc();
567
+ } else if (e.key === 'ArrowDown') {
568
+ this.activeCounter.dec();
569
+ } else if (e.key === 'ArrowRight') {
570
+ this.selectNextCounter();
571
+ } else if (e.key === 'ArrowLeft') {
572
+ this.selectPrevCounter();
573
+ }
574
+
575
+ setTimeout(() => {
576
+ this.setInputSelectionRange();
577
+ });
578
+ }
579
+
556
580
  handleNavKeys(e) {
557
581
  if (this.isReadOnly) {
558
582
  return;
559
583
  }
560
584
 
561
- const { key, shiftKey, metaKey } = e;
562
- const keys = getKeyMap(key, shiftKey, metaKey);
563
-
564
585
  if (this.opened) {
565
586
  this.closePopover();
566
587
  }
567
588
 
568
- e.preventDefault();
569
-
570
- if (isSupportedKey(key)) {
571
- const counter = this.activeCounter;
572
-
573
- if (!counter) return;
589
+ if (!this.activeCounter) return;
574
590
 
575
- const counterData = Object.values(counterConfig).find((config) => config.id === counter.id);
576
- const { count, shiftCount } = counterData;
577
-
578
- if (keys.backspace) this.handleBackspace();
579
- else if (keys.arrowUp) counter.inc();
580
- else if (keys.arrowDown) counter.dec();
581
- else if (keys.shiftArrowUp) counter.inc(count);
582
- else if (keys.shiftArrowDown) counter.dec(count);
583
- else if (keys.pageUp) counter.inc(count);
584
- else if (keys.pageDown) counter.dec(count);
585
- else if (keys.shiftPageUp) counter.inc(shiftCount);
586
- else if (keys.shiftPageDown) counter.dec(shiftCount);
587
- else if (keys.arrowRight) this.selectNextCounter();
588
- else if (keys.arrowLeft) this.selectPrevCounter();
589
-
590
- this.setInputSelectionRange();
591
+ if (e.inputType === 'deleteContentBackward') {
592
+ this.handleBackspace();
591
593
  }
594
+
595
+ this.setInputSelectionRange();
592
596
  }
593
597
 
594
598
  handleBackspace() {
595
- const counter = this.activeCounter;
596
-
597
- if (counter.isEmpty) {
599
+ if (this.activeCounter.isEmpty) {
600
+ this.activeCounter.clear();
598
601
  this.selectPrevCounter();
599
602
  this.setInputSelectionRange();
600
603
  } else {
601
- counter.del();
604
+ this.activeCounter.del();
602
605
  }
603
606
  }
604
607
 
@@ -625,8 +628,9 @@ class RawDateFieldClass extends BaseInputClass {
625
628
  if (!val) return;
626
629
  const [min, max] = val.split?.('-');
627
630
  if (min && max) {
628
- this.#yearDateCounter.setMin(min);
629
- this.#yearDateCounter.setMax(max);
631
+ const counter = this.getCounterById('year');
632
+ counter.setMin(min);
633
+ counter.setMax(max);
630
634
  }
631
635
  }
632
636
 
@@ -665,17 +669,45 @@ class RawDateFieldClass extends BaseInputClass {
665
669
  }
666
670
  }
667
671
 
672
+ // we want to validate the date supplied actually exists. For example: Feb 31 is not an actual date,
673
+ // but in JS when create a `new Date('1999-02-31') we get March 2nd 1999.
674
+ // To prevent this error from being submitted, we evaluate the
675
+ // date parts against their generated Date value.
676
+ isInvalidDate() {
677
+ return Object.entries(this.getDateVals()).some(
678
+ ([key, val]) => !val || this.getCounterById(key).numberValue !== val
679
+ );
680
+ }
681
+
668
682
  getValidity() {
669
683
  if (this.isRequired && this.isCountersEmpty) {
670
684
  return { valueMissing: true };
671
685
  }
672
686
 
673
- if (this.isCountersOutOfRange) {
687
+ if (this.isCountersOutOfRange || this.isInvalidDate()) {
674
688
  return { patternMismatch: true };
675
689
  }
676
690
 
677
691
  return {};
678
692
  }
693
+
694
+ getDateVals() {
695
+ const ret = {
696
+ day: '',
697
+ month: '',
698
+ year: '',
699
+ };
700
+
701
+ try {
702
+ const date = newDate(this.timestamp);
703
+
704
+ ret.month = date.getMonth() + 1;
705
+ ret.day = date.getDate();
706
+ ret.year = date.getFullYear();
707
+ } catch (e) {}
708
+
709
+ return ret;
710
+ }
679
711
  }
680
712
 
681
713
  const textVars = TextFieldClass.cssVarList;
@@ -8,18 +8,6 @@ export const YEARS_RANGE = 100;
8
8
 
9
9
  export const DIVIDER = '/';
10
10
 
11
- export const COUNTER_SUPPORTED_KEYS = [
12
- 'Backspace',
13
- 'ArrowLeft',
14
- 'ArrowRight',
15
- 'ArrowDown',
16
- 'ArrowUp',
17
- 'PageUp',
18
- 'PageDown',
19
- 'Meta',
20
- 'Enter',
21
- ];
22
-
23
11
  export const months = [
24
12
  'January',
25
13
  'February',
@@ -1,8 +1,6 @@
1
- import { COUNTER_SUPPORTED_KEYS } from './consts';
2
-
3
1
  export const isValidTimestamp = (val) => !Number.isNaN(Number(val));
4
2
 
5
- export const isNumber = (val) => !Number.isNaN(Number(val));
3
+ export const isNumber = (val) => !!String(val || '').trim() && !Number.isNaN(Number(val));
6
4
 
7
5
  export const getTimestampParts = (timestamp) => {
8
6
  const date = newDate(timestamp);
@@ -35,28 +33,6 @@ export const newDate = (date) => {
35
33
  return new Date();
36
34
  };
37
35
 
38
- export const isSupportedKey = (key) => COUNTER_SUPPORTED_KEYS.includes(key);
39
-
40
- export const getKeyMap = (key, shiftKey, metaKey) => {
41
- return {
42
- refresh: metaKey && key.toLowerCase() === 'r',
43
- tab: key === 'Tab',
44
- shiftTab: shiftKey && key === 'Tab',
45
- backspace: key === 'Backspace',
46
- arrowUp: !shiftKey && key === 'ArrowUp',
47
- arrowDown: !shiftKey && key === 'ArrowDown',
48
- arrowLeft: !shiftKey && key === 'ArrowLeft',
49
- arrowRight: !shiftKey && key === 'ArrowRight',
50
- pageUp: !shiftKey && key === 'PageUp',
51
- pageDown: !shiftKey && key === 'PageDown',
52
- shiftArrowUp: shiftKey && key === 'ArrowUp',
53
- shiftArrowDown: shiftKey && key === 'ArrowDown',
54
- shiftPageUp: shiftKey && key === 'PageUp',
55
- shiftPageDown: shiftKey && key === 'PageDown',
56
- enter: key === 'Enter',
57
- };
58
- };
59
-
60
36
  export const getCurrentTime = () => newDate().getTime();
61
37
  export const getFullYear = (timestamp) => newDate(timestamp).getFullYear().toString();
62
38
  export const getMonth = (timestamp) => (newDate(timestamp).getMonth() + 1).toString();