@descope/web-components-ui 1.104.0 → 1.106.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.
- package/dist/cjs/index.cjs.js +242 -151
- package/dist/cjs/index.cjs.js.map +1 -1
- package/dist/index.esm.js +242 -151
- package/dist/index.esm.js.map +1 -1
- package/dist/umd/DescopeDev.js +1 -1
- package/dist/umd/DescopeDev.js.map +1 -1
- package/dist/umd/descope-date-field-descope-calendar-index-js.js +1 -1
- package/dist/umd/descope-date-field-descope-calendar-index-js.js.map +1 -1
- package/dist/umd/descope-date-field-index-js.js +1 -1
- package/dist/umd/descope-date-field-index-js.js.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/package.json +9 -9
- package/src/components/descope-date-field/DateFieldClass.js +147 -56
- package/src/components/descope-date-field/consts.js +2 -0
- package/src/components/descope-date-field/helpers.js +8 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@descope/web-components-ui",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.106.0",
|
4
4
|
"description": "",
|
5
5
|
"main": "dist/cjs/index.cjs.js",
|
6
6
|
"module": "dist/index.esm.js",
|
@@ -82,14 +82,14 @@
|
|
82
82
|
"@descope-ui/descope-icon": "0.0.14",
|
83
83
|
"@descope-ui/descope-text": "0.0.17",
|
84
84
|
"@descope-ui/descope-avatar": "0.0.17",
|
85
|
-
"@descope-ui/descope-combo-box": "0.1.
|
86
|
-
"@descope-ui/descope-autocomplete-field": "0.0.
|
87
|
-
"@descope-ui/descope-address-field": "0.0.
|
88
|
-
"@descope-ui/descope-timer": "0.0.
|
89
|
-
"@descope-ui/descope-timer-button": "0.0.
|
90
|
-
"@descope-ui/descope-password-strength": "0.0.
|
91
|
-
"@descope-ui/descope-collapsible-container": "0.0.
|
92
|
-
"@descope-ui/descope-recovery-codes": "0.0.
|
85
|
+
"@descope-ui/descope-combo-box": "0.1.7",
|
86
|
+
"@descope-ui/descope-autocomplete-field": "0.0.20",
|
87
|
+
"@descope-ui/descope-address-field": "0.0.19",
|
88
|
+
"@descope-ui/descope-timer": "0.0.14",
|
89
|
+
"@descope-ui/descope-timer-button": "0.0.16",
|
90
|
+
"@descope-ui/descope-password-strength": "0.0.11",
|
91
|
+
"@descope-ui/descope-collapsible-container": "0.0.15",
|
92
|
+
"@descope-ui/descope-recovery-codes": "0.0.4"
|
93
93
|
},
|
94
94
|
"overrides": {
|
95
95
|
"@vaadin/avatar": "24.3.4",
|
@@ -17,10 +17,18 @@ import {
|
|
17
17
|
} from './helpers';
|
18
18
|
import { formats } from './formats';
|
19
19
|
import { calendarIcon } from './icons';
|
20
|
-
import {
|
20
|
+
import {
|
21
|
+
counterConfig,
|
22
|
+
DEFAULT_FORMAT,
|
23
|
+
DIVIDER,
|
24
|
+
MOBILE_DEVICE_INTERACTION_TIMEOUT_MS,
|
25
|
+
NATIVE_FORMAT,
|
26
|
+
valRange,
|
27
|
+
} from './consts';
|
21
28
|
import { DateCounter } from './DateCounterClass';
|
22
29
|
import { TextFieldClass } from '../descope-text-field/TextFieldClass';
|
23
30
|
import { injectStyle } from '@descope-ui/common/components-helpers';
|
31
|
+
import { parseDateString } from './helpers';
|
24
32
|
|
25
33
|
export const componentName = getComponentName('date-field');
|
26
34
|
|
@@ -40,8 +48,6 @@ class RawDateFieldClass extends BaseInputClass {
|
|
40
48
|
|
41
49
|
selectedCounterIdx = 0;
|
42
50
|
|
43
|
-
#focused = false;
|
44
|
-
|
45
51
|
updateCountersDisplay() {
|
46
52
|
this.inputElement.value = this.countersValue;
|
47
53
|
}
|
@@ -215,6 +221,11 @@ class RawDateFieldClass extends BaseInputClass {
|
|
215
221
|
return this.getAttribute('disable-calendar') === 'true';
|
216
222
|
}
|
217
223
|
|
224
|
+
get isSelectAll() {
|
225
|
+
const inputEle = this.inputElement.baseElement.inputElement;
|
226
|
+
return inputEle.value.length === inputEle.selectionStart + inputEle.selectionEnd;
|
227
|
+
}
|
228
|
+
|
218
229
|
reportValidity() {
|
219
230
|
this.inputElement.reportValidity();
|
220
231
|
}
|
@@ -249,24 +260,10 @@ class RawDateFieldClass extends BaseInputClass {
|
|
249
260
|
this.inputElement.addEventListener('focus', this.onFocus.bind(this));
|
250
261
|
this.inputElement.addEventListener('blur', this.onBlur.bind(this));
|
251
262
|
this.inputElement.addEventListener('click', this.handleMouseCaretPositionChange.bind(this));
|
252
|
-
this.inputElement.addEventListener('keydown', this.
|
263
|
+
this.inputElement.addEventListener('keydown', this.handleKeyboard.bind(this));
|
253
264
|
this.inputElement.addEventListener('beforeinput', this.handleInput.bind(this));
|
254
|
-
|
255
|
-
|
256
|
-
// Since we can't seem to block touch events (`touch-action: none` or preventing default on `touchstart`
|
257
|
-
// or `touchend`, we listen to `pointerdown` and in case it's of type `touch` we execute
|
258
|
-
// the component's logic for range selection.
|
259
|
-
this.inputElement.addEventListener('pointerdown', (e) => {
|
260
|
-
if (e.pointerType === 'touch') {
|
261
|
-
e.preventDefault();
|
262
|
-
if (!this.#focused) {
|
263
|
-
this.inputElement.focus();
|
264
|
-
}
|
265
|
-
setTimeout(() => {
|
266
|
-
this.handleMouseCaretPositionChange(e);
|
267
|
-
}, 250);
|
268
|
-
}
|
269
|
-
});
|
265
|
+
this.inputElement.addEventListener('pointerdown', this.onPointerDown.bind(this));
|
266
|
+
this.inputElement.addEventListener('paste', this.onPaste.bind(this));
|
270
267
|
|
271
268
|
forwardAttrs(this, this.inputElement, {
|
272
269
|
includeAttrs: [
|
@@ -295,6 +292,8 @@ class RawDateFieldClass extends BaseInputClass {
|
|
295
292
|
handleInput(e) {
|
296
293
|
e.preventDefault();
|
297
294
|
|
295
|
+
this.handleSelectAll();
|
296
|
+
|
298
297
|
if (e.data && isNumber(e.data)) {
|
299
298
|
this.parseDigits(e.data);
|
300
299
|
this.updateCountersDisplay();
|
@@ -315,6 +314,12 @@ class RawDateFieldClass extends BaseInputClass {
|
|
315
314
|
});
|
316
315
|
}
|
317
316
|
|
317
|
+
handleSelectAll() {
|
318
|
+
if (this.isSelectAll) {
|
319
|
+
this.selectFirstCounter();
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
318
323
|
#popoverPosStylesheet;
|
319
324
|
|
320
325
|
#popoverRenderer(root) {
|
@@ -464,16 +469,19 @@ class RawDateFieldClass extends BaseInputClass {
|
|
464
469
|
});
|
465
470
|
}
|
466
471
|
|
472
|
+
// In mobile devices, there are cases were `pointerdown` is triggered
|
473
|
+
// instead of `click`.
|
474
|
+
onPointerDown(e) {
|
475
|
+
setTimeout(() => this.handleMouseCaretPositionChange(e), MOBILE_DEVICE_INTERACTION_TIMEOUT_MS);
|
476
|
+
}
|
477
|
+
|
467
478
|
onFocus() {
|
468
|
-
if (this.isReadOnly
|
479
|
+
if (this.isReadOnly) {
|
469
480
|
return;
|
470
481
|
}
|
471
482
|
|
472
|
-
// We
|
473
|
-
|
474
|
-
this.#focused = true;
|
475
|
-
|
476
|
-
this.resetDisplay();
|
483
|
+
// We need to wait for focus to end before we set selection
|
484
|
+
setTimeout(() => this.resetDisplay());
|
477
485
|
}
|
478
486
|
|
479
487
|
resetDisplay() {
|
@@ -481,18 +489,13 @@ class RawDateFieldClass extends BaseInputClass {
|
|
481
489
|
this.inputElement.value = this.format;
|
482
490
|
}
|
483
491
|
|
484
|
-
// On focus select the first
|
485
|
-
this.
|
486
|
-
|
487
|
-
|
488
|
-
// set selection on first counter
|
489
|
-
this.inputElement.setSelectionRange(0, this.sortedCounters[0].length);
|
490
|
-
});
|
492
|
+
// On focus select the first counter
|
493
|
+
this.selectFirstCounter();
|
494
|
+
// set selection on first counter
|
495
|
+
this.inputElement.setSelectionRange(0, this.sortedCounters[0].length);
|
491
496
|
}
|
492
497
|
|
493
498
|
onBlur() {
|
494
|
-
this.#focused = false;
|
495
|
-
|
496
499
|
if (this.opened) {
|
497
500
|
return;
|
498
501
|
}
|
@@ -522,6 +525,8 @@ class RawDateFieldClass extends BaseInputClass {
|
|
522
525
|
this.selectNextCounter();
|
523
526
|
}
|
524
527
|
|
528
|
+
// We wait for the digit to be parsed, and only then set the selection.
|
529
|
+
// Failing to do so results in unexpected "jump" of the screen in mobile devices.
|
525
530
|
this.setInputSelectionRange();
|
526
531
|
}
|
527
532
|
|
@@ -537,10 +542,18 @@ class RawDateFieldClass extends BaseInputClass {
|
|
537
542
|
|
538
543
|
setSelectedCounterByCaretPosition(e) {
|
539
544
|
this.selectedCounterIdx = this.getCounterIdx(
|
540
|
-
|
545
|
+
// if triggered by touch event, target might not include `selectionStart`
|
546
|
+
// in that case we fall back to the inputElement's `selectionStart` value.
|
547
|
+
// Therefore, it is recommended to run this function with setTimeout,
|
548
|
+
// at least for mobile events.
|
549
|
+
e.target?.selectionStart || this.inputElement.selectionStart
|
541
550
|
);
|
542
551
|
}
|
543
552
|
|
553
|
+
selectFirstCounter() {
|
554
|
+
this.selectedCounterIdx = 0;
|
555
|
+
}
|
556
|
+
|
544
557
|
selectNextCounter() {
|
545
558
|
if (this.selectedCounterIdx < this.dateCounters.length) {
|
546
559
|
this.selectedCounterIdx = Math.min(this.selectedCounterIdx + 1, 2);
|
@@ -563,14 +576,18 @@ class RawDateFieldClass extends BaseInputClass {
|
|
563
576
|
return;
|
564
577
|
}
|
565
578
|
|
566
|
-
|
567
|
-
|
568
|
-
|
579
|
+
// We wait for before setting the selection, otherwise there's an
|
580
|
+
// unexpected "jump" of the screen in mobile devices.
|
581
|
+
setTimeout(() => {
|
582
|
+
const caretStart = this.sortedCounters
|
583
|
+
.slice(0, this.selectedCounterIdx)
|
584
|
+
.reduce((acc, counter) => acc + counter.length, this.selectedCounterIdx);
|
569
585
|
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
586
|
+
this.inputElement.setSelectionRange(
|
587
|
+
caretStart,
|
588
|
+
caretStart + this.sortedCounters[this.selectedCounterIdx].length
|
589
|
+
);
|
590
|
+
});
|
574
591
|
}
|
575
592
|
|
576
593
|
resetDateCounters() {
|
@@ -596,7 +613,17 @@ class RawDateFieldClass extends BaseInputClass {
|
|
596
613
|
});
|
597
614
|
}
|
598
615
|
|
599
|
-
|
616
|
+
handleKeyboard(e) {
|
617
|
+
if (e.metaKey || e.ctrlKey) {
|
618
|
+
if (e.key.toLowerCase() === 'x') {
|
619
|
+
this.onCut(e);
|
620
|
+
}
|
621
|
+
|
622
|
+
return;
|
623
|
+
}
|
624
|
+
|
625
|
+
this.handleSelectAll();
|
626
|
+
|
600
627
|
if (e.key === 'ArrowUp') {
|
601
628
|
this.activeCounter.inc();
|
602
629
|
} else if (e.key === 'ArrowDown') {
|
@@ -607,9 +634,7 @@ class RawDateFieldClass extends BaseInputClass {
|
|
607
634
|
this.selectPrevCounter();
|
608
635
|
}
|
609
636
|
|
610
|
-
|
611
|
-
this.setInputSelectionRange();
|
612
|
-
});
|
637
|
+
this.setInputSelectionRange();
|
613
638
|
}
|
614
639
|
|
615
640
|
handleNavKeys(e) {
|
@@ -631,27 +656,37 @@ class RawDateFieldClass extends BaseInputClass {
|
|
631
656
|
}
|
632
657
|
|
633
658
|
handleBackspace() {
|
634
|
-
if (this.
|
635
|
-
this.
|
659
|
+
if (this.isSelectAll) {
|
660
|
+
this.resetToInitialState();
|
661
|
+
return;
|
662
|
+
}
|
663
|
+
|
664
|
+
const counter = this.activeCounter;
|
665
|
+
|
666
|
+
if (counter.isEmpty) {
|
636
667
|
this.selectPrevCounter();
|
637
668
|
this.setInputSelectionRange();
|
638
669
|
} else {
|
639
|
-
|
670
|
+
counter.set('');
|
640
671
|
}
|
672
|
+
|
673
|
+
// To support keyboards like SwiftKey, we need to re-render the counters display and selection,
|
674
|
+
// otherwise we get an unexpected behavior, where the format is deleted.
|
675
|
+
setTimeout(() => {
|
676
|
+
this.updateCountersDisplay();
|
677
|
+
this.setInputSelectionRange();
|
678
|
+
});
|
641
679
|
}
|
642
680
|
|
643
681
|
handleMouseCaretPositionChange(e) {
|
644
682
|
if (this.opened) {
|
645
683
|
return;
|
646
684
|
}
|
685
|
+
|
647
686
|
e.preventDefault();
|
648
|
-
this.setSelectedCounterByCaretPosition(e);
|
649
687
|
|
650
|
-
|
651
|
-
|
652
|
-
setTimeout(() => {
|
653
|
-
this.setInputSelectionRange();
|
654
|
-
});
|
688
|
+
this.setSelectedCounterByCaretPosition(e);
|
689
|
+
this.setInputSelectionRange();
|
655
690
|
}
|
656
691
|
|
657
692
|
onInitialValueChange(val) {
|
@@ -743,6 +778,62 @@ class RawDateFieldClass extends BaseInputClass {
|
|
743
778
|
|
744
779
|
return ret;
|
745
780
|
}
|
781
|
+
|
782
|
+
resetToInitialState() {
|
783
|
+
this.resetDateCounters();
|
784
|
+
this.selectFirstCounter();
|
785
|
+
this.resetDisplay();
|
786
|
+
}
|
787
|
+
|
788
|
+
onCut(e) {
|
789
|
+
e.preventDefault();
|
790
|
+
|
791
|
+
if (this.isSelectAll) {
|
792
|
+
this.#copyToClipboard(this.countersValue);
|
793
|
+
this.resetToInitialState();
|
794
|
+
} else {
|
795
|
+
this.#copyToClipboard(this.activeCounter.stringValue);
|
796
|
+
this.activeCounter.set('');
|
797
|
+
}
|
798
|
+
|
799
|
+
this.setInputSelectionRange();
|
800
|
+
}
|
801
|
+
|
802
|
+
#copyToClipboard(value) {
|
803
|
+
try {
|
804
|
+
navigator.clipboard.writeText(value);
|
805
|
+
} catch (err) {
|
806
|
+
console.error('Failed to copy date value:', err);
|
807
|
+
}
|
808
|
+
}
|
809
|
+
|
810
|
+
onPaste(e) {
|
811
|
+
e.preventDefault();
|
812
|
+
|
813
|
+
const clipboardData = e.clipboardData || window.clipboardData;
|
814
|
+
const pastedData = clipboardData.getData('Text');
|
815
|
+
|
816
|
+
// try paste entire date if valid
|
817
|
+
const validDate = parseDateString(pastedData, this.format);
|
818
|
+
|
819
|
+
if (validDate) {
|
820
|
+
this.value = validDate.getTime();
|
821
|
+
this.onDateCounterChange();
|
822
|
+
|
823
|
+
// select all
|
824
|
+
setTimeout(() => this.inputElement.setSelectionRange(0, this.inputElement.value.length));
|
825
|
+
} else {
|
826
|
+
const value = Number(pastedData);
|
827
|
+
|
828
|
+
// try paste in counter if possible
|
829
|
+
if (value && this.activeCounter.min <= value && this.activeCounter.max >= value) {
|
830
|
+
// use String to get rid of any zero padding
|
831
|
+
this.activeCounter.set(String(value));
|
832
|
+
|
833
|
+
setTimeout(() => this.setInputSelectionRange());
|
834
|
+
}
|
835
|
+
}
|
836
|
+
}
|
746
837
|
}
|
747
838
|
|
748
839
|
const textVars = TextFieldClass.cssVarList;
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import { formats } from './formats';
|
2
|
+
|
1
3
|
export const isValidTimestamp = (val) => !Number.isNaN(Number(val));
|
2
4
|
|
3
5
|
export const isNumber = (val) => !!String(val || '').trim() && !Number.isNaN(Number(val));
|
@@ -47,3 +49,9 @@ export const overrideConstructedStylesheet = (ele) => {
|
|
47
49
|
cs.insertRule(':host{display:block!important;}');
|
48
50
|
ele?.shadowRoot?.adoptedStyleSheets?.push(cs);
|
49
51
|
};
|
52
|
+
|
53
|
+
export const parseDateString = (val, format) => {
|
54
|
+
const trimmed = val.trim?.();
|
55
|
+
if (!trimmed) return null;
|
56
|
+
return formats[format].getDate(trimmed);
|
57
|
+
};
|