@aurodesignsystem-dev/auro-formkit 0.0.0-pr1516.0 → 0.0.0-pr1519.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/components/checkbox/demo/customize.min.js +18 -10
- package/components/checkbox/demo/getting-started.min.js +18 -10
- package/components/checkbox/demo/index.min.js +18 -10
- package/components/checkbox/dist/index.js +18 -10
- package/components/checkbox/dist/registered.js +18 -10
- package/components/combobox/README.md +1 -1
- package/components/combobox/demo/customize.md +2 -10
- package/components/combobox/demo/customize.min.js +454 -467
- package/components/combobox/demo/getting-started.min.js +454 -463
- package/components/combobox/demo/index.md +1 -1
- package/components/combobox/demo/index.min.js +454 -463
- package/components/combobox/demo/keyboard-behavior.md +2 -142
- package/components/combobox/demo/readme.md +1 -1
- package/components/combobox/demo/why-combobox.md +2 -2
- package/components/combobox/dist/auro-combobox.d.ts +30 -14
- package/components/combobox/dist/index.js +454 -463
- package/components/combobox/dist/registered.js +454 -463
- package/components/counter/demo/customize.min.js +32 -12
- package/components/counter/demo/index.min.js +32 -12
- package/components/counter/dist/index.js +32 -12
- package/components/counter/dist/registered.js +32 -12
- package/components/datepicker/demo/customize.min.js +183 -167
- package/components/datepicker/demo/index.min.js +183 -167
- package/components/datepicker/dist/index.js +183 -167
- package/components/datepicker/dist/registered.js +183 -167
- package/components/dropdown/demo/customize.min.js +14 -2
- package/components/dropdown/demo/getting-started.min.js +14 -2
- package/components/dropdown/demo/index.min.js +14 -2
- package/components/dropdown/dist/index.js +14 -2
- package/components/dropdown/dist/registered.js +14 -2
- package/components/form/demo/customize.min.js +851 -829
- package/components/form/demo/getting-started.min.js +851 -829
- package/components/form/demo/index.min.js +851 -829
- package/components/form/demo/registerDemoDeps.min.js +851 -829
- package/components/input/demo/api.md +58 -57
- package/components/input/demo/customize.min.js +114 -155
- package/components/input/demo/getting-started.min.js +114 -155
- package/components/input/demo/index.min.js +114 -155
- package/components/input/dist/base-input.d.ts +9 -51
- package/components/input/dist/index.js +114 -155
- package/components/input/dist/registered.js +114 -155
- package/components/input/dist/utilities.d.ts +9 -0
- package/components/radio/demo/customize.min.js +18 -10
- package/components/radio/demo/getting-started.min.js +18 -10
- package/components/radio/demo/index.min.js +18 -10
- package/components/radio/dist/index.js +18 -10
- package/components/radio/dist/registered.js +18 -10
- package/components/select/demo/customize.min.js +32 -12
- package/components/select/demo/getting-started.min.js +32 -12
- package/components/select/demo/index.min.js +32 -12
- package/components/select/dist/index.js +32 -12
- package/components/select/dist/registered.js +32 -12
- package/custom-elements.json +142 -278
- package/package.json +1 -1
|
@@ -402,11 +402,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
|
|
|
402
402
|
return;
|
|
403
403
|
}
|
|
404
404
|
|
|
405
|
-
// Validate that the date passed was the correct format and is a valid date
|
|
405
|
+
// Validate that the date passed was the correct format and is a valid date.
|
|
406
|
+
// For partial date formats, valueObject is never populated; validate them directly.
|
|
406
407
|
if (elem.value && !elem.valueObject) {
|
|
407
|
-
elem.
|
|
408
|
-
|
|
409
|
-
|
|
408
|
+
const isPartialDateFormat = elem.util && !elem.util.isFullDateFormat(elem.type, elem.format);
|
|
409
|
+
const isValidPartial = isPartialDateFormat && elem.util.isValidPartialDate(elem.value, elem.format);
|
|
410
|
+
|
|
411
|
+
if (!isValidPartial) {
|
|
412
|
+
elem.validity = 'patternMismatch';
|
|
413
|
+
elem.errorMessage = elem.setCustomValidityPatternMismatch || elem.setCustomValidity || 'Invalid Date Format Entered';
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
410
416
|
}
|
|
411
417
|
|
|
412
418
|
// Perform the rest of the validation
|
|
@@ -500,15 +506,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
|
|
|
500
506
|
);
|
|
501
507
|
}
|
|
502
508
|
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
509
|
+
const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
|
|
510
|
+
|
|
511
|
+
// If there is a second input in the elem and that value is undefined or an empty string set hasValue to false.
|
|
512
|
+
// Skip for combobox: its second auro-input is the fullscreen-bib mirror of the same value, not an independent
|
|
513
|
+
// field (datepicker is the intended consumer — start/end are independently required).
|
|
514
|
+
if (this.auroInputElements?.length === 2 && !isCombobox) {
|
|
515
|
+
if (!this.auroInputElements[1].value || this.auroInputElements[1].value.length === 0) {
|
|
506
516
|
hasValue = false;
|
|
507
517
|
}
|
|
508
518
|
}
|
|
509
519
|
|
|
510
|
-
const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
|
|
511
|
-
|
|
512
520
|
if (isCombobox) {
|
|
513
521
|
|
|
514
522
|
if (!elem.persistInput || elem.behavior === "filter") {
|
|
@@ -825,6 +833,8 @@ function navigateArrow(component, direction, options = {}) {
|
|
|
825
833
|
}
|
|
826
834
|
}
|
|
827
835
|
|
|
836
|
+
/* eslint-disable no-underscore-dangle */
|
|
837
|
+
|
|
828
838
|
/**
|
|
829
839
|
* Returns the clear button element from the active input's shadow
|
|
830
840
|
* DOM, if available.
|
|
@@ -860,11 +870,9 @@ function isClearBtnFocused(ctx, clearBtn = getClearBtn(ctx)) {
|
|
|
860
870
|
* @param {Object} menu - The menu component.
|
|
861
871
|
*/
|
|
862
872
|
function reconcileMenuIndex(menu) {
|
|
863
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
864
873
|
if (menu._index < 0 && menu.optionActive && menu.items) {
|
|
865
874
|
const idx = menu.items.indexOf(menu.optionActive);
|
|
866
875
|
if (idx >= 0) {
|
|
867
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
868
876
|
menu._index = idx;
|
|
869
877
|
}
|
|
870
878
|
}
|
|
@@ -883,11 +891,7 @@ const comboboxKeyboardStrategy = {
|
|
|
883
891
|
|
|
884
892
|
// navigate if bib is open otherwise open it
|
|
885
893
|
if (component.dropdown.isPopoverVisible) {
|
|
886
|
-
|
|
887
|
-
component.activateLastEnabledAvailableOption();
|
|
888
|
-
} else {
|
|
889
|
-
navigateArrow(component, 'down');
|
|
890
|
-
}
|
|
894
|
+
navigateArrow(component, 'down');
|
|
891
895
|
} else {
|
|
892
896
|
component.showBib();
|
|
893
897
|
}
|
|
@@ -906,11 +910,7 @@ const comboboxKeyboardStrategy = {
|
|
|
906
910
|
|
|
907
911
|
// navigate if bib is open otherwise open it
|
|
908
912
|
if (component.dropdown.isPopoverVisible) {
|
|
909
|
-
|
|
910
|
-
component.activateFirstEnabledAvailableOption();
|
|
911
|
-
} else {
|
|
912
|
-
navigateArrow(component, 'up');
|
|
913
|
-
}
|
|
913
|
+
navigateArrow(component, 'up');
|
|
914
914
|
} else {
|
|
915
915
|
component.showBib();
|
|
916
916
|
}
|
|
@@ -926,13 +926,18 @@ const comboboxKeyboardStrategy = {
|
|
|
926
926
|
},
|
|
927
927
|
|
|
928
928
|
Enter(component, evt, ctx) {
|
|
929
|
+
// Forms should not submit on Enter from a combobox, regardless of which
|
|
930
|
+
// child element (input, clear button, menu) is focused.
|
|
931
|
+
evt.stopPropagation();
|
|
932
|
+
|
|
929
933
|
if (isClearBtnFocused(ctx)) {
|
|
930
|
-
//
|
|
931
|
-
//
|
|
932
|
-
//
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
934
|
+
// Let the browser dispatch Enter to the focused clear button so its
|
|
935
|
+
// built-in activation fires and clears the selection. Do NOT call
|
|
936
|
+
// preventDefault — that would block the activation.
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
if (ctx.isExpanded && component.menu.optionActive) {
|
|
936
941
|
reconcileMenuIndex(component.menu);
|
|
937
942
|
component.menu.makeSelection();
|
|
938
943
|
|
|
@@ -941,14 +946,8 @@ const comboboxKeyboardStrategy = {
|
|
|
941
946
|
}
|
|
942
947
|
|
|
943
948
|
evt.preventDefault();
|
|
944
|
-
evt.stopPropagation();
|
|
945
949
|
} else {
|
|
946
|
-
// Prevent the keypress from bubbling to parent containers (e.g., forms)
|
|
947
|
-
// which could interpret Enter as a submit or trigger other unintended behavior.
|
|
948
|
-
// This is safe because showBib() opens the dialog programmatically,
|
|
949
|
-
// not via event propagation.
|
|
950
950
|
evt.preventDefault();
|
|
951
|
-
evt.stopPropagation();
|
|
952
951
|
component.showBib();
|
|
953
952
|
}
|
|
954
953
|
},
|
|
@@ -993,7 +992,7 @@ const comboboxKeyboardStrategy = {
|
|
|
993
992
|
component.setClearBtnFocus();
|
|
994
993
|
}
|
|
995
994
|
}
|
|
996
|
-
}
|
|
995
|
+
}
|
|
997
996
|
};
|
|
998
997
|
|
|
999
998
|
/**
|
|
@@ -4888,7 +4887,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$3 {
|
|
|
4888
4887
|
}
|
|
4889
4888
|
};
|
|
4890
4889
|
|
|
4891
|
-
var formkitVersion$2 = '
|
|
4890
|
+
var formkitVersion$2 = '202607011722';
|
|
4892
4891
|
|
|
4893
4892
|
let AuroElement$2 = class AuroElement extends i$3 {
|
|
4894
4893
|
static get properties() {
|
|
@@ -5640,7 +5639,19 @@ class AuroDropdown extends AuroElement$2 {
|
|
|
5640
5639
|
if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown") {
|
|
5641
5640
|
this.trigger.focus();
|
|
5642
5641
|
}
|
|
5643
|
-
|
|
5642
|
+
|
|
5643
|
+
|
|
5644
|
+
if (!this.isPopoverVisible) {
|
|
5645
|
+
// wait til the bib gets fully closed and rendered
|
|
5646
|
+
setTimeout(() => {
|
|
5647
|
+
// check if it's still closed and the focus is still within the dropdown, but not in the bib. If so, move focus to the trigger.
|
|
5648
|
+
if (!this.isPopoverVisible || !this.bib.matches(':focus-within')) {
|
|
5649
|
+
return;
|
|
5650
|
+
}
|
|
5651
|
+
// Move focus out of bib into trigger.
|
|
5652
|
+
this.trigger.focus();
|
|
5653
|
+
});
|
|
5654
|
+
} }
|
|
5644
5655
|
|
|
5645
5656
|
firstUpdated() {
|
|
5646
5657
|
// Configure the floater to, this will generate the ID for the bib
|
|
@@ -10572,11 +10583,17 @@ class AuroFormValidation {
|
|
|
10572
10583
|
return;
|
|
10573
10584
|
}
|
|
10574
10585
|
|
|
10575
|
-
// Validate that the date passed was the correct format and is a valid date
|
|
10586
|
+
// Validate that the date passed was the correct format and is a valid date.
|
|
10587
|
+
// For partial date formats, valueObject is never populated; validate them directly.
|
|
10576
10588
|
if (elem.value && !elem.valueObject) {
|
|
10577
|
-
elem.
|
|
10578
|
-
|
|
10579
|
-
|
|
10589
|
+
const isPartialDateFormat = elem.util && !elem.util.isFullDateFormat(elem.type, elem.format);
|
|
10590
|
+
const isValidPartial = isPartialDateFormat && elem.util.isValidPartialDate(elem.value, elem.format);
|
|
10591
|
+
|
|
10592
|
+
if (!isValidPartial) {
|
|
10593
|
+
elem.validity = 'patternMismatch';
|
|
10594
|
+
elem.errorMessage = elem.setCustomValidityPatternMismatch || elem.setCustomValidity || 'Invalid Date Format Entered';
|
|
10595
|
+
return;
|
|
10596
|
+
}
|
|
10580
10597
|
}
|
|
10581
10598
|
|
|
10582
10599
|
// Perform the rest of the validation
|
|
@@ -10670,15 +10687,17 @@ class AuroFormValidation {
|
|
|
10670
10687
|
);
|
|
10671
10688
|
}
|
|
10672
10689
|
|
|
10673
|
-
|
|
10674
|
-
|
|
10675
|
-
|
|
10690
|
+
const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
|
|
10691
|
+
|
|
10692
|
+
// If there is a second input in the elem and that value is undefined or an empty string set hasValue to false.
|
|
10693
|
+
// Skip for combobox: its second auro-input is the fullscreen-bib mirror of the same value, not an independent
|
|
10694
|
+
// field (datepicker is the intended consumer — start/end are independently required).
|
|
10695
|
+
if (this.auroInputElements?.length === 2 && !isCombobox) {
|
|
10696
|
+
if (!this.auroInputElements[1].value || this.auroInputElements[1].value.length === 0) {
|
|
10676
10697
|
hasValue = false;
|
|
10677
10698
|
}
|
|
10678
10699
|
}
|
|
10679
10700
|
|
|
10680
|
-
const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
|
|
10681
|
-
|
|
10682
10701
|
if (isCombobox) {
|
|
10683
10702
|
|
|
10684
10703
|
if (!elem.persistInput || elem.behavior === "filter") {
|
|
@@ -16578,6 +16597,42 @@ class AuroInputUtilities {
|
|
|
16578
16597
|
return type === 'date' && normalizedFormat.includes('yy') && normalizedFormat.includes('mm') && normalizedFormat.includes('dd');
|
|
16579
16598
|
}
|
|
16580
16599
|
|
|
16600
|
+
/**
|
|
16601
|
+
* Validates a value against a partial date format (one that lacks yy/mm/dd all three).
|
|
16602
|
+
* Single-component formats are checked as integer ranges; multi-component formats use
|
|
16603
|
+
* a date-fns parse + round-trip to confirm both validity and exact formatting.
|
|
16604
|
+
* @param {string} value - The user-facing display value.
|
|
16605
|
+
* @param {string} format - The partial date format string (e.g. "mm/yyyy", "yyyy", "dd").
|
|
16606
|
+
* @returns {boolean}
|
|
16607
|
+
*/
|
|
16608
|
+
isValidPartialDate(value, format$1) {
|
|
16609
|
+
if (!value || !format$1) {
|
|
16610
|
+
return false;
|
|
16611
|
+
}
|
|
16612
|
+
const normalizedFormat = format$1.toLowerCase();
|
|
16613
|
+
|
|
16614
|
+
if (normalizedFormat === 'dd') {
|
|
16615
|
+
const num = Number(value);
|
|
16616
|
+
return (/^\d{2}$/u).test(value) && num >= 1 && num <= 31;
|
|
16617
|
+
}
|
|
16618
|
+
if (normalizedFormat === 'yy') {
|
|
16619
|
+
const num = Number(value);
|
|
16620
|
+
return (/^\d{2}$/u).test(value) && num >= 0 && num <= 99;
|
|
16621
|
+
}
|
|
16622
|
+
if (normalizedFormat === 'yyyy') {
|
|
16623
|
+
const num = Number(value);
|
|
16624
|
+
return (/^\d{4}$/u).test(value) && num >= 1 && num <= 9999;
|
|
16625
|
+
}
|
|
16626
|
+
|
|
16627
|
+
const dateFnsMask = this.toDateFnsMask(normalizedFormat);
|
|
16628
|
+
// Use the 1st of the current month as the reference so that formats
|
|
16629
|
+
// omitting a day (e.g. MM/yyyy) never roll over on days 29–31.
|
|
16630
|
+
const referenceDate = new Date();
|
|
16631
|
+
referenceDate.setDate(1);
|
|
16632
|
+
const parsed = parse(value, dateFnsMask, referenceDate);
|
|
16633
|
+
return isValid(parsed) && format(parsed, dateFnsMask) === value;
|
|
16634
|
+
}
|
|
16635
|
+
|
|
16581
16636
|
/**
|
|
16582
16637
|
* Converts a display string to its model value.
|
|
16583
16638
|
* For full date formats, converts the display string to an ISO date string.
|
|
@@ -16625,6 +16680,7 @@ class AuroInputUtilities {
|
|
|
16625
16680
|
const maskOptions = this.getMaskOptions('date', normalizedFormat);
|
|
16626
16681
|
|
|
16627
16682
|
if (!(valueObject instanceof Date) || Number.isNaN(valueObject.getTime()) || !maskOptions || typeof maskOptions.format !== 'function') {
|
|
16683
|
+
console.debug('Invalid date object or mask options for formatting', { valueObject, maskOptions }); // eslint-disable-line no-console
|
|
16628
16684
|
return undefined;
|
|
16629
16685
|
}
|
|
16630
16686
|
|
|
@@ -16782,6 +16838,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
16782
16838
|
// so a parent (datepicker/combobox) calling `validate()` synchronously
|
|
16783
16839
|
// during its own update cycle sees a populated util instance.
|
|
16784
16840
|
this.activeLabel = false;
|
|
16841
|
+
|
|
16785
16842
|
/** @private */
|
|
16786
16843
|
this.allowedInputTypes = [
|
|
16787
16844
|
"text",
|
|
@@ -16792,6 +16849,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
16792
16849
|
"tel"
|
|
16793
16850
|
];
|
|
16794
16851
|
this.appearance = "default";
|
|
16852
|
+
|
|
16795
16853
|
/** @private */
|
|
16796
16854
|
this.dateFormatMap = {
|
|
16797
16855
|
'mm/dd/yyyy': 'dateMMDDYYYY',
|
|
@@ -16810,23 +16868,24 @@ class BaseInput extends AuroElement$1 {
|
|
|
16810
16868
|
'mm/dd': 'dateMMDD'
|
|
16811
16869
|
};
|
|
16812
16870
|
this.disabled = false;
|
|
16871
|
+
|
|
16813
16872
|
/** @private */
|
|
16814
16873
|
this.domHandler = new DomHandler();
|
|
16815
16874
|
this.dvInputOnly = false;
|
|
16816
16875
|
this.hasValue = false;
|
|
16817
16876
|
this.hideLabelVisually = false;
|
|
16818
16877
|
this.icon = false;
|
|
16878
|
+
|
|
16819
16879
|
/** @private */
|
|
16820
16880
|
this.inputIconName = undefined;
|
|
16881
|
+
|
|
16821
16882
|
/** @private */
|
|
16822
16883
|
this.label = 'Input label is undefined';
|
|
16823
16884
|
this.layout = 'classic';
|
|
16824
16885
|
this.locale = 'en-US';
|
|
16825
16886
|
this.max = undefined;
|
|
16826
|
-
this._maxObject = undefined;
|
|
16827
16887
|
this.maxLength = undefined;
|
|
16828
16888
|
this.min = undefined;
|
|
16829
|
-
this._minObject = undefined;
|
|
16830
16889
|
this.minLength = undefined;
|
|
16831
16890
|
this.noValidate = false;
|
|
16832
16891
|
this.onDark = false;
|
|
@@ -16843,23 +16902,27 @@ class BaseInput extends AuroElement$1 {
|
|
|
16843
16902
|
"email"
|
|
16844
16903
|
];
|
|
16845
16904
|
this.shape = 'classic';
|
|
16905
|
+
|
|
16846
16906
|
/** @private */
|
|
16847
16907
|
this.showPassword = false;
|
|
16848
16908
|
this.size = 'lg';
|
|
16849
16909
|
this.touched = false;
|
|
16910
|
+
|
|
16850
16911
|
/** @private */
|
|
16851
16912
|
this.uniqueId = new UniqueId().create();
|
|
16913
|
+
|
|
16852
16914
|
/** @private */
|
|
16853
16915
|
this.util = new AuroInputUtilities({
|
|
16854
16916
|
locale: this.locale,
|
|
16855
16917
|
format: this.format
|
|
16856
16918
|
});
|
|
16919
|
+
|
|
16857
16920
|
/** @private */
|
|
16858
16921
|
this.validation = new AuroFormValidation();
|
|
16922
|
+
|
|
16859
16923
|
/** @private */
|
|
16860
16924
|
this.validationCCLength = undefined;
|
|
16861
16925
|
this.value = undefined;
|
|
16862
|
-
this._valueObject = undefined;
|
|
16863
16926
|
}
|
|
16864
16927
|
|
|
16865
16928
|
// function to define props used within the scope of this component
|
|
@@ -17189,6 +17252,13 @@ class BaseInput extends AuroElement$1 {
|
|
|
17189
17252
|
type: String
|
|
17190
17253
|
},
|
|
17191
17254
|
|
|
17255
|
+
/**
|
|
17256
|
+
* Custom help text message to display when validity = `patternMismatch`.
|
|
17257
|
+
*/
|
|
17258
|
+
setCustomValidityPatternMismatch: {
|
|
17259
|
+
type: String
|
|
17260
|
+
},
|
|
17261
|
+
|
|
17192
17262
|
/**
|
|
17193
17263
|
* Custom help text message to display when validity = `rangeOverflow`.
|
|
17194
17264
|
*/
|
|
@@ -17259,7 +17329,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17259
17329
|
|
|
17260
17330
|
/**
|
|
17261
17331
|
* Populates the `type` attribute on the input.
|
|
17262
|
-
* @type {'text' | 'password' | 'email' | 'credit-card' | 'tel' | 'number'}
|
|
17332
|
+
* @type {'text' | 'password' | 'email' | 'credit-card' | 'tel' | 'number' | 'date'}
|
|
17263
17333
|
* @default 'text'
|
|
17264
17334
|
*/
|
|
17265
17335
|
type: {
|
|
@@ -17297,7 +17367,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17297
17367
|
* @returns {Date|undefined}
|
|
17298
17368
|
*/
|
|
17299
17369
|
get valueObject() {
|
|
17300
|
-
return this.
|
|
17370
|
+
return this.value && dateFormatter.isValidDate(this.value) ? dateFormatter.stringToDateInstance(this.value) : undefined;
|
|
17301
17371
|
}
|
|
17302
17372
|
|
|
17303
17373
|
/**
|
|
@@ -17305,7 +17375,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17305
17375
|
* @returns {Date|undefined}
|
|
17306
17376
|
*/
|
|
17307
17377
|
get minObject() {
|
|
17308
|
-
return this.
|
|
17378
|
+
return this.min && dateFormatter.isValidDate(this.min) ? dateFormatter.stringToDateInstance(this.min) : undefined;
|
|
17309
17379
|
}
|
|
17310
17380
|
|
|
17311
17381
|
/**
|
|
@@ -17313,50 +17383,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17313
17383
|
* @returns {Date|undefined}
|
|
17314
17384
|
*/
|
|
17315
17385
|
get maxObject() {
|
|
17316
|
-
return this.
|
|
17317
|
-
}
|
|
17318
|
-
|
|
17319
|
-
/**
|
|
17320
|
-
* Parses a date string into a Date object when the corresponding `_*Object`
|
|
17321
|
-
* field hasn't been synced yet by `updated()`. Returns undefined when the
|
|
17322
|
-
* input type/format isn't a full date or the string is not a valid date.
|
|
17323
|
-
*
|
|
17324
|
-
* Why this exists: a parent (datepicker) can call `inputN.validate()` from
|
|
17325
|
-
* inside its own `updated()` before this input's `updated()` has run
|
|
17326
|
-
* `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
|
|
17327
|
-
* and range checks would otherwise silently no-op (flipping the result to
|
|
17328
|
-
* `valid` or `patternMismatch`).
|
|
17329
|
-
* @private
|
|
17330
|
-
* @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
|
|
17331
|
-
* @returns {Date|undefined}
|
|
17332
|
-
*/
|
|
17333
|
-
_computeDateObjectFallback(dateStr) {
|
|
17334
|
-
if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
|
|
17335
|
-
return undefined;
|
|
17336
|
-
}
|
|
17337
|
-
if (!dateFormatter.isValidDate(dateStr)) {
|
|
17338
|
-
return undefined;
|
|
17339
|
-
}
|
|
17340
|
-
return dateFormatter.stringToDateInstance(dateStr);
|
|
17341
|
-
}
|
|
17342
|
-
|
|
17343
|
-
/**
|
|
17344
|
-
* Internal setter for readonly date object properties.
|
|
17345
|
-
* @private
|
|
17346
|
-
* @param {'valueObject'|'minObject'|'maxObject'} propertyName - Public object property name.
|
|
17347
|
-
* @param {Date|undefined} propertyValue - Value to assign.
|
|
17348
|
-
* @returns {void}
|
|
17349
|
-
*/
|
|
17350
|
-
setDateObjectProperty(propertyName, propertyValue) {
|
|
17351
|
-
const internalPropertyName = `_${propertyName}`;
|
|
17352
|
-
const previousValue = this[internalPropertyName];
|
|
17353
|
-
|
|
17354
|
-
if (previousValue === propertyValue) {
|
|
17355
|
-
return;
|
|
17356
|
-
}
|
|
17357
|
-
|
|
17358
|
-
this[internalPropertyName] = propertyValue;
|
|
17359
|
-
this.requestUpdate(propertyName, previousValue);
|
|
17386
|
+
return this.max && dateFormatter.isValidDate(this.max) ? dateFormatter.stringToDateInstance(this.max) : undefined;
|
|
17360
17387
|
}
|
|
17361
17388
|
|
|
17362
17389
|
connectedCallback() {
|
|
@@ -17385,7 +17412,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17385
17412
|
format: this.format
|
|
17386
17413
|
});
|
|
17387
17414
|
this.configureDataForType();
|
|
17388
|
-
this.syncDateValues();
|
|
17389
17415
|
}
|
|
17390
17416
|
|
|
17391
17417
|
disconnectedCallback() {
|
|
@@ -17424,7 +17450,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17424
17450
|
this.setCustomHelpTextMessage();
|
|
17425
17451
|
this.configureAutoFormatting();
|
|
17426
17452
|
this.configureDataForType();
|
|
17427
|
-
this.syncDateValues();
|
|
17428
17453
|
}
|
|
17429
17454
|
|
|
17430
17455
|
/**
|
|
@@ -17568,8 +17593,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17568
17593
|
this.configureDataForType();
|
|
17569
17594
|
}
|
|
17570
17595
|
|
|
17571
|
-
this.syncDateValues(changedProperties);
|
|
17572
|
-
|
|
17573
17596
|
if (changedProperties.has('value')) {
|
|
17574
17597
|
if (this.value && this.value.length > 0) {
|
|
17575
17598
|
this.hasValue = true;
|
|
@@ -17591,14 +17614,14 @@ class BaseInput extends AuroElement$1 {
|
|
|
17591
17614
|
|
|
17592
17615
|
if (formattedValue !== this.inputElement.value) {
|
|
17593
17616
|
this.skipNextProgrammaticInputEvent = true;
|
|
17594
|
-
if (this.maskInstance && this.type
|
|
17617
|
+
if (this.maskInstance && this.type !== 'date') {
|
|
17595
17618
|
// Route through the mask so its _value and el.value stay in lock-step
|
|
17596
17619
|
// (set value calls updateControl which writes el.value = displayValue).
|
|
17597
17620
|
// Writing el.value directly leaves the mask thinking displayValue is
|
|
17598
|
-
// stale; _saveSelection on the next focus/click then warns.
|
|
17599
|
-
//
|
|
17600
|
-
//
|
|
17601
|
-
//
|
|
17621
|
+
// stale; _saveSelection on the next focus/click then warns. Date is
|
|
17622
|
+
// excluded because its formattedValue can be raw ISO when the calendar
|
|
17623
|
+
// is invalid, and re-masking through mm/dd/yyyy would truncate it and
|
|
17624
|
+
// flip validity from patternMismatch to tooShort.
|
|
17602
17625
|
this.maskInstance.value = formattedValue || '';
|
|
17603
17626
|
} else if (formattedValue) {
|
|
17604
17627
|
this.inputElement.value = formattedValue;
|
|
@@ -17640,120 +17663,65 @@ class BaseInput extends AuroElement$1 {
|
|
|
17640
17663
|
}));
|
|
17641
17664
|
}
|
|
17642
17665
|
|
|
17643
|
-
|
|
17644
|
-
/**
|
|
17645
|
-
* Synchronizes the ISO string values and Date object representations for date-related properties.
|
|
17646
|
-
* This keeps the model and display values aligned when either side changes.
|
|
17647
|
-
*
|
|
17648
|
-
* When a full date format is in use, this method updates `value`, `min`, and `max` from their corresponding
|
|
17649
|
-
* Date objects or vice versa, based on which properties have changed. It only runs when the current configuration
|
|
17650
|
-
* represents a full year/month/day date format.
|
|
17651
|
-
*
|
|
17652
|
-
* @param {Map<string, unknown>|undefined} [changedProperties=undefined] - Optional map of changed properties used to limit which values are synchronized.
|
|
17653
|
-
* @returns {void}
|
|
17654
|
-
* @private
|
|
17655
|
-
*/
|
|
17656
|
-
syncDateValues(changedProperties = undefined) {
|
|
17657
|
-
if (!this.util.isFullDateFormat(this.type, this.format)) {
|
|
17658
|
-
return;
|
|
17659
|
-
}
|
|
17660
|
-
|
|
17661
|
-
this.syncSingleDateValue(changedProperties, 'valueObject', 'value');
|
|
17662
|
-
this.syncSingleDateValue(changedProperties, 'minObject', 'min');
|
|
17663
|
-
this.syncSingleDateValue(changedProperties, 'maxObject', 'max');
|
|
17664
|
-
}
|
|
17665
|
-
|
|
17666
|
-
/**
|
|
17667
|
-
* Synchronizes one date object/string property pair.
|
|
17668
|
-
* @private
|
|
17669
|
-
* @param {Map<string, unknown>|undefined} changedProperties - Map of changed properties from Lit.
|
|
17670
|
-
* @param {string} objectProperty - Date object property name.
|
|
17671
|
-
* @param {string} valueProperty - ISO string property name.
|
|
17672
|
-
* @returns {void}
|
|
17673
|
-
*/
|
|
17674
|
-
syncSingleDateValue(changedProperties, objectProperty, valueProperty) {
|
|
17675
|
-
const objectPropertyChanged = !changedProperties || changedProperties.has(objectProperty);
|
|
17676
|
-
|
|
17677
|
-
// objectProperty wins over valueProperty when both changed
|
|
17678
|
-
if (objectPropertyChanged && this[objectProperty]) {
|
|
17679
|
-
this[valueProperty] = dateFormatter.toISOFormatString(this[objectProperty]);
|
|
17680
|
-
return;
|
|
17681
|
-
}
|
|
17682
|
-
|
|
17683
|
-
const valuePropertyChanged = !changedProperties || changedProperties.has(valueProperty);
|
|
17684
|
-
if (!valuePropertyChanged) {
|
|
17685
|
-
return;
|
|
17686
|
-
}
|
|
17687
|
-
|
|
17688
|
-
// when value is newly set to the same ISO string that corresponds to the existing Date object, do not clear the Date object (avoid unnecessary updates)
|
|
17689
|
-
if (
|
|
17690
|
-
changedProperties &&
|
|
17691
|
-
valueProperty === 'value' &&
|
|
17692
|
-
changedProperties.get('value') === undefined &&
|
|
17693
|
-
this[objectProperty] instanceof Date &&
|
|
17694
|
-
this[valueProperty] === dateFormatter.toISOFormatString(this[objectProperty])
|
|
17695
|
-
) {
|
|
17696
|
-
return;
|
|
17697
|
-
}
|
|
17698
|
-
|
|
17699
|
-
if (dateFormatter.isValidDate(this[valueProperty])) {
|
|
17700
|
-
this.setDateObjectProperty(objectProperty, dateFormatter.stringToDateInstance(this[valueProperty]));
|
|
17701
|
-
} else {
|
|
17702
|
-
this.setDateObjectProperty(objectProperty, undefined);
|
|
17703
|
-
}
|
|
17704
|
-
}
|
|
17705
|
-
|
|
17706
17666
|
/**
|
|
17707
17667
|
* Sets up IMasks and logic based on auto-formatting requirements.
|
|
17708
17668
|
* @private
|
|
17709
17669
|
* @returns {void}
|
|
17710
17670
|
*/
|
|
17711
17671
|
configureAutoFormatting() {
|
|
17712
|
-
//
|
|
17713
|
-
//
|
|
17714
|
-
//
|
|
17715
|
-
//
|
|
17672
|
+
// _configuringMask gates two things: external re-entry into this method
|
|
17673
|
+
// while setup is mid-flight (from property changes that call back here),
|
|
17674
|
+
// and the accept/complete listeners below — both need to ignore the mask
|
|
17675
|
+
// events fired by our own value-restore step.
|
|
17716
17676
|
if (this._configuringMask) return;
|
|
17717
17677
|
this._configuringMask = true;
|
|
17718
17678
|
try {
|
|
17679
|
+
// Destroy any prior mask so IMask can attach fresh under the new format.
|
|
17680
|
+
// Null the reference too — if the new maskOptions.mask is falsy (e.g.
|
|
17681
|
+
// type switched from credit-card to text) the IMask() reassignment
|
|
17682
|
+
// below is skipped, and downstream writes at line ~823 would otherwise
|
|
17683
|
+
// route through the destroyed instance.
|
|
17719
17684
|
if (this.maskInstance) {
|
|
17720
17685
|
this.maskInstance.destroy();
|
|
17686
|
+
this.maskInstance = null;
|
|
17721
17687
|
}
|
|
17722
17688
|
|
|
17723
|
-
// Pass new format to util
|
|
17724
17689
|
this.util.updateFormat(this.format);
|
|
17725
17690
|
|
|
17726
17691
|
const maskOptions = this.util.getMaskOptions(this.type, this.format);
|
|
17727
17692
|
|
|
17728
17693
|
if (this.inputElement && maskOptions.mask) {
|
|
17729
|
-
|
|
17730
|
-
//
|
|
17731
|
-
//
|
|
17732
|
-
//
|
|
17733
|
-
// When the format changes (e.g. locale switch) and we have a valid ISO
|
|
17734
|
-
// model value, compute the display string for the NEW format instead of
|
|
17735
|
-
// re-using the old display string, which may be invalid in the new mask.
|
|
17694
|
+
// Capture the current display so it can be re-applied after IMask
|
|
17695
|
+
// attaches. The restore at the bottom goes through maskInstance.value
|
|
17696
|
+
// (not inputElement.value directly) so the mask's internal state and
|
|
17697
|
+
// the input's displayed text stay in lock-step.
|
|
17736
17698
|
let existingValue = this.inputElement.value;
|
|
17699
|
+
|
|
17700
|
+
// Format-change case (e.g. locale switch): existingValue is the OLD
|
|
17701
|
+
// mask's display string and may not parse under the new mask. When
|
|
17702
|
+
// we have a valid date model, rebuild the display from valueObject
|
|
17703
|
+
// (the canonical source) using the new mask's format function.
|
|
17737
17704
|
if (
|
|
17738
17705
|
this.util.isFullDateFormat(this.type, this.format) &&
|
|
17739
17706
|
this.value &&
|
|
17740
|
-
|
|
17741
|
-
this.valueObject instanceof Date &&
|
|
17742
|
-
!Number.isNaN(this.valueObject.getTime()) &&
|
|
17707
|
+
this.valueObject &&
|
|
17743
17708
|
typeof maskOptions.format === 'function'
|
|
17744
17709
|
) {
|
|
17745
17710
|
existingValue = maskOptions.format(this.valueObject);
|
|
17746
17711
|
}
|
|
17747
17712
|
|
|
17713
|
+
// Clear before IMask attaches so the constructor seeds an empty
|
|
17714
|
+
// internal value. Otherwise IMask reads the stale unmasked string
|
|
17715
|
+
// and emits a spurious 'accept' before the restore below runs.
|
|
17748
17716
|
this.skipNextProgrammaticInputEvent = true;
|
|
17749
17717
|
this.inputElement.value = '';
|
|
17750
17718
|
|
|
17751
17719
|
this.maskInstance = IMask(this.inputElement, maskOptions);
|
|
17752
17720
|
|
|
17721
|
+
// Mask fires 'accept' on every value change, including the restore
|
|
17722
|
+
// step below. Skip events fired during configureAutoFormatting so
|
|
17723
|
+
// we don't overwrite a value the parent just pushed.
|
|
17753
17724
|
this.maskInstance.on('accept', () => {
|
|
17754
|
-
// Suppress propagation during configureAutoFormatting's own value-restoration
|
|
17755
|
-
// (line below) — the mask emits 'accept' on every value-set, including ours,
|
|
17756
|
-
// and we don't want to overwrite a value the parent just pushed.
|
|
17757
17725
|
if (this._configuringMask) return;
|
|
17758
17726
|
this.value = this.util.toModelValue(this.maskInstance.value, this.format);
|
|
17759
17727
|
if (this.type === "date") {
|
|
@@ -17761,6 +17729,8 @@ class BaseInput extends AuroElement$1 {
|
|
|
17761
17729
|
}
|
|
17762
17730
|
});
|
|
17763
17731
|
|
|
17732
|
+
// Mask fires 'complete' on the restore step below for any value that
|
|
17733
|
+
// happens to be a complete match. Same setup-suppression as 'accept'.
|
|
17764
17734
|
this.maskInstance.on('complete', () => {
|
|
17765
17735
|
if (this._configuringMask) return;
|
|
17766
17736
|
this.value = this.util.toModelValue(this.maskInstance.value, this.format);
|
|
@@ -17769,7 +17739,9 @@ class BaseInput extends AuroElement$1 {
|
|
|
17769
17739
|
}
|
|
17770
17740
|
});
|
|
17771
17741
|
|
|
17772
|
-
//
|
|
17742
|
+
// Write existingValue through the mask (not the input directly) so
|
|
17743
|
+
// the mask reformats it under the new rules and keeps its internal
|
|
17744
|
+
// _value aligned with the input's displayed text.
|
|
17773
17745
|
if (existingValue) {
|
|
17774
17746
|
this.maskInstance.value = existingValue;
|
|
17775
17747
|
}
|
|
@@ -17863,9 +17835,9 @@ class BaseInput extends AuroElement$1 {
|
|
|
17863
17835
|
// the gated types currently support it, since the list is a public
|
|
17864
17836
|
// property a consumer could mutate.
|
|
17865
17837
|
if (this.setSelectionInputTypes.includes(this.type)) {
|
|
17866
|
-
let selectionStart;
|
|
17838
|
+
let selectionStart = null;
|
|
17867
17839
|
try {
|
|
17868
|
-
selectionStart = this.inputElement
|
|
17840
|
+
({ selectionStart } = this.inputElement);
|
|
17869
17841
|
} catch (error) { // eslint-disable-line no-unused-vars
|
|
17870
17842
|
return;
|
|
17871
17843
|
}
|
|
@@ -17969,7 +17941,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17969
17941
|
*/
|
|
17970
17942
|
reset() {
|
|
17971
17943
|
this.value = undefined;
|
|
17972
|
-
this.setDateObjectProperty('valueObject', undefined);
|
|
17973
17944
|
this.validation.reset(this);
|
|
17974
17945
|
}
|
|
17975
17946
|
|
|
@@ -17978,7 +17949,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17978
17949
|
*/
|
|
17979
17950
|
clear() {
|
|
17980
17951
|
this.value = undefined;
|
|
17981
|
-
this.setDateObjectProperty('valueObject', undefined);
|
|
17982
17952
|
}
|
|
17983
17953
|
|
|
17984
17954
|
/**
|
|
@@ -18507,7 +18477,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$3 {
|
|
|
18507
18477
|
}
|
|
18508
18478
|
};
|
|
18509
18479
|
|
|
18510
|
-
var formkitVersion$1 = '
|
|
18480
|
+
var formkitVersion$1 = '202607011722';
|
|
18511
18481
|
|
|
18512
18482
|
// Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
|
|
18513
18483
|
// See LICENSE in the project root for license information.
|
|
@@ -19633,7 +19603,7 @@ class AuroBibtemplate extends i$3 {
|
|
|
19633
19603
|
}
|
|
19634
19604
|
}
|
|
19635
19605
|
|
|
19636
|
-
var formkitVersion = '
|
|
19606
|
+
var formkitVersion = '202607011722';
|
|
19637
19607
|
|
|
19638
19608
|
var styleCss$3 = i$6`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}:host{display:block;text-align:left}:host [auro-dropdown]{--ds-auro-dropdown-trigger-background-color: transparent}:host #inputInBib::part(wrapper){box-shadow:none}:host #inputInBib::part(accent-left){display:none}:host([layout*=classic]) [auro-input]{width:100%}:host([layout*=classic]) [auro-input]::part(helpText){display:none}:host([layout*=classic]) #slotHolder{display:none}`;
|
|
19639
19609
|
|
|
@@ -20001,12 +19971,23 @@ function getOptionLabel(option) {
|
|
|
20001
19971
|
if (!option) {
|
|
20002
19972
|
return '';
|
|
20003
19973
|
}
|
|
20004
|
-
|
|
20005
|
-
|
|
20006
|
-
if (
|
|
20007
|
-
|
|
19974
|
+
|
|
19975
|
+
// Consumer-provided override: short-circuit the DOM walk entirely.
|
|
19976
|
+
if (option.dataset && option.dataset.label) {
|
|
19977
|
+
return option.dataset.label;
|
|
19978
|
+
}
|
|
19979
|
+
|
|
19980
|
+
// Walk direct children — the `slot` attribute only applies to direct children
|
|
19981
|
+
// of the slot host, so a shallow filter is sufficient. Avoids the cloneNode +
|
|
19982
|
+
// querySelector allocation that ran on every keystroke via syncValuesAndStates.
|
|
19983
|
+
let text = '';
|
|
19984
|
+
for (const node of option.childNodes) {
|
|
19985
|
+
const isDisplayValueSlot = node.nodeType === Node.ELEMENT_NODE && node.getAttribute('slot') === 'displayValue';
|
|
19986
|
+
if (!isDisplayValueSlot) {
|
|
19987
|
+
text += node.textContent || '';
|
|
19988
|
+
}
|
|
20008
19989
|
}
|
|
20009
|
-
return
|
|
19990
|
+
return text.replace(/\s+/gu, ' ').trim();
|
|
20010
19991
|
}
|
|
20011
19992
|
|
|
20012
19993
|
// See https://git.io/JJ6SJ for "How to document your components using JSDoc"
|
|
@@ -20081,8 +20062,6 @@ class AuroCombobox extends AuroElement {
|
|
|
20081
20062
|
this.availableOptions = [];
|
|
20082
20063
|
this.dropdownId = undefined;
|
|
20083
20064
|
this.dropdownOpen = false;
|
|
20084
|
-
this.triggerExpandedState = false;
|
|
20085
|
-
this._expandedTimeout = null;
|
|
20086
20065
|
this._inFullscreenTransition = false;
|
|
20087
20066
|
this.errorMessage = null;
|
|
20088
20067
|
this.isHiddenWhileLoading = false;
|
|
@@ -20093,6 +20072,30 @@ class AuroCombobox extends AuroElement {
|
|
|
20093
20072
|
this.touched = false;
|
|
20094
20073
|
this.validation = new AuroFormValidation$1();
|
|
20095
20074
|
this.validity = undefined;
|
|
20075
|
+
this._userTyped = false;
|
|
20076
|
+
// Tracks every setTimeout scheduled via _scheduleTimer so
|
|
20077
|
+
// disconnectedCallback can cancel them. Without this, a detached
|
|
20078
|
+
// combobox's pending timers still fire — most are no-ops, but
|
|
20079
|
+
// configureMenu's racing-condition retry would otherwise loop.
|
|
20080
|
+
this._pendingTimers = new Set();
|
|
20081
|
+
}
|
|
20082
|
+
|
|
20083
|
+
/**
|
|
20084
|
+
* setTimeout wrapper that records the timer id so disconnectedCallback
|
|
20085
|
+
* can cancel any outstanding callbacks. The id is removed from the set
|
|
20086
|
+
* once the callback fires so the set doesn't grow unbounded.
|
|
20087
|
+
* @param {Function} fn - Callback to run.
|
|
20088
|
+
* @param {number} ms - Delay in milliseconds.
|
|
20089
|
+
* @returns {number} The timer id.
|
|
20090
|
+
* @private
|
|
20091
|
+
*/
|
|
20092
|
+
_scheduleTimer(fn, ms) {
|
|
20093
|
+
const id = setTimeout(() => {
|
|
20094
|
+
this._pendingTimers.delete(id);
|
|
20095
|
+
fn();
|
|
20096
|
+
}, ms);
|
|
20097
|
+
this._pendingTimers.add(id);
|
|
20098
|
+
return id;
|
|
20096
20099
|
}
|
|
20097
20100
|
|
|
20098
20101
|
// This function is to define props used within the scope of this component
|
|
@@ -20454,17 +20457,6 @@ class AuroCombobox extends AuroElement {
|
|
|
20454
20457
|
attribute: false
|
|
20455
20458
|
},
|
|
20456
20459
|
|
|
20457
|
-
/**
|
|
20458
|
-
* Deferred aria-expanded state for the trigger input.
|
|
20459
|
-
* Delays the "true" transition so VoiceOver finishes its character echo
|
|
20460
|
-
* before announcing "expanded".
|
|
20461
|
-
* @private
|
|
20462
|
-
*/
|
|
20463
|
-
triggerExpandedState: {
|
|
20464
|
-
type: Boolean,
|
|
20465
|
-
reflect: false,
|
|
20466
|
-
attribute: false
|
|
20467
|
-
},
|
|
20468
20460
|
};
|
|
20469
20461
|
}
|
|
20470
20462
|
|
|
@@ -20487,13 +20479,6 @@ class AuroCombobox extends AuroElement {
|
|
|
20487
20479
|
return this.input.value;
|
|
20488
20480
|
}
|
|
20489
20481
|
|
|
20490
|
-
// /**
|
|
20491
|
-
// * Sets the value of the input element within the combobox.
|
|
20492
|
-
// */
|
|
20493
|
-
// set inputValue(value) {
|
|
20494
|
-
// this.input.value = value;
|
|
20495
|
-
// }
|
|
20496
|
-
|
|
20497
20482
|
/**
|
|
20498
20483
|
* Checks if the element is valid.
|
|
20499
20484
|
* @returns {boolean} - Returns true if the element is valid, false otherwise.
|
|
@@ -20620,7 +20605,7 @@ class AuroCombobox extends AuroElement {
|
|
|
20620
20605
|
if (this.menu) {
|
|
20621
20606
|
this.menu.matchWord = normalizeFilterValue(this.input.value);
|
|
20622
20607
|
}
|
|
20623
|
-
const label = getOptionLabel(this.menu.optionSelected)
|
|
20608
|
+
const label = getOptionLabel(this.menu.optionSelected);
|
|
20624
20609
|
this.updateTriggerTextDisplay(label || this.value);
|
|
20625
20610
|
}
|
|
20626
20611
|
|
|
@@ -20634,44 +20619,16 @@ class AuroCombobox extends AuroElement {
|
|
|
20634
20619
|
// in suggestion mode, do not override input value if no selection has been made and the input currently has focus
|
|
20635
20620
|
const isInputFocusedWithNoSelection = !this.menu.value && (this.input.matches(':focus-within') || (this.inputInBib && this.inputInBib.matches(':focus-within')));
|
|
20636
20621
|
|
|
20637
|
-
|
|
20638
|
-
|
|
20639
|
-
|
|
20640
|
-
// syncValuesAndStates re-enters here during typing when both inputs
|
|
20641
|
-
// already match, and a no-op flag flip would make the bib branch's
|
|
20642
|
-
// bail eat legitimate user-input events.
|
|
20643
|
-
const triggerNeedsSync = this.input.value !== nextValue;
|
|
20644
|
-
const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
|
|
20645
|
-
if (triggerNeedsSync || bibNeedsSync) {
|
|
20646
|
-
this._syncingDisplayValue = true;
|
|
20647
|
-
if (triggerNeedsSync) {
|
|
20648
|
-
this.input.value = nextValue;
|
|
20649
|
-
}
|
|
20650
|
-
if (bibNeedsSync) {
|
|
20651
|
-
this.inputInBib.value = nextValue;
|
|
20652
|
-
}
|
|
20653
|
-
const pending = [];
|
|
20654
|
-
if (triggerNeedsSync) {
|
|
20655
|
-
pending.push(this.input.updateComplete);
|
|
20656
|
-
}
|
|
20657
|
-
if (bibNeedsSync) {
|
|
20658
|
-
pending.push(this.inputInBib.updateComplete);
|
|
20659
|
-
}
|
|
20660
|
-
Promise.all(pending).then(() => {
|
|
20661
|
-
// imask reasserts otherwise, and handleBlur reverts to its stale value.
|
|
20662
|
-
if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
|
|
20663
|
-
this.input.maskInstance.updateValue();
|
|
20664
|
-
}
|
|
20665
|
-
if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
|
|
20666
|
-
this.inputInBib.maskInstance.updateValue();
|
|
20667
|
-
}
|
|
20668
|
-
this._syncingDisplayValue = false;
|
|
20669
|
-
});
|
|
20670
|
-
}
|
|
20622
|
+
const suppressed = this.persistInput || (this.behavior === 'suggestion' && isInputFocusedWithNoSelection);
|
|
20623
|
+
if (!suppressed) {
|
|
20624
|
+
this.syncInputValuesAcrossTriggerAndBib(label || this.value);
|
|
20671
20625
|
}
|
|
20672
20626
|
|
|
20673
|
-
//
|
|
20674
|
-
|
|
20627
|
+
// Replace any previously appended displayValue clone in the trigger.
|
|
20628
|
+
// :not(slot) excludes the template's <slot name="displayValue"
|
|
20629
|
+
// slot="displayValue"> forwarder, which also has slot="displayValue"
|
|
20630
|
+
// and would otherwise be matched first and removed.
|
|
20631
|
+
const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]:not(slot)');
|
|
20675
20632
|
|
|
20676
20633
|
if (displayValueInTrigger) {
|
|
20677
20634
|
displayValueInTrigger.remove();
|
|
@@ -20695,6 +20652,53 @@ class AuroCombobox extends AuroElement {
|
|
|
20695
20652
|
this.requestUpdate();
|
|
20696
20653
|
}
|
|
20697
20654
|
|
|
20655
|
+
/**
|
|
20656
|
+
* Writes nextValue to the trigger input and the bib input when their current
|
|
20657
|
+
* value differs, then re-asserts imask after Lit's update flushes.
|
|
20658
|
+
* @param {string} nextValue - The value to write to both inputs.
|
|
20659
|
+
* @returns {Promise<void>} Resolves after both inputs flush and imask
|
|
20660
|
+
* re-asserts; resolves immediately when no sync is needed.
|
|
20661
|
+
* @private
|
|
20662
|
+
*/
|
|
20663
|
+
async syncInputValuesAcrossTriggerAndBib(nextValue) {
|
|
20664
|
+
// Only set the flag when there's an actual write to suppress —
|
|
20665
|
+
// syncValuesAndStates re-enters here during typing when both inputs
|
|
20666
|
+
// already match, and a no-op flag flip would make the bib branch's
|
|
20667
|
+
// bail eat legitimate user-input events.
|
|
20668
|
+
const triggerNeedsSync = this.input.value !== nextValue;
|
|
20669
|
+
const bibNeedsSync = Boolean(this.inputInBib) && this.inputInBib.value !== nextValue;
|
|
20670
|
+
if (!triggerNeedsSync && !bibNeedsSync) {
|
|
20671
|
+
return;
|
|
20672
|
+
}
|
|
20673
|
+
|
|
20674
|
+
this._syncingDisplayValue = true;
|
|
20675
|
+
|
|
20676
|
+
const pending = [];
|
|
20677
|
+
if (triggerNeedsSync) {
|
|
20678
|
+
this.input.value = nextValue;
|
|
20679
|
+
pending.push(this.input.updateComplete);
|
|
20680
|
+
}
|
|
20681
|
+
if (bibNeedsSync) {
|
|
20682
|
+
this.inputInBib.value = nextValue;
|
|
20683
|
+
pending.push(this.inputInBib.updateComplete);
|
|
20684
|
+
}
|
|
20685
|
+
// finally — not a bare .then — so that an imask throw (see commit
|
|
20686
|
+
// d1857401c: imask can throw on credit-card format change) doesn't strand
|
|
20687
|
+
// _syncingDisplayValue=true and silently swallow every subsequent input.
|
|
20688
|
+
try {
|
|
20689
|
+
await Promise.all(pending);
|
|
20690
|
+
// imask reasserts otherwise, and handleBlur reverts to its stale value.
|
|
20691
|
+
if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
|
|
20692
|
+
this.input.maskInstance.updateValue();
|
|
20693
|
+
}
|
|
20694
|
+
if (bibNeedsSync && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
|
|
20695
|
+
this.inputInBib.maskInstance.updateValue();
|
|
20696
|
+
}
|
|
20697
|
+
} finally {
|
|
20698
|
+
this._syncingDisplayValue = false;
|
|
20699
|
+
}
|
|
20700
|
+
}
|
|
20701
|
+
|
|
20698
20702
|
/**
|
|
20699
20703
|
* Processes hidden state of all menu options and determines if there are any available options not hidden.
|
|
20700
20704
|
* @private
|
|
@@ -20703,6 +20707,13 @@ class AuroCombobox extends AuroElement {
|
|
|
20703
20707
|
handleMenuOptions() {
|
|
20704
20708
|
this.generateOptionsArray();
|
|
20705
20709
|
this.availableOptions = [];
|
|
20710
|
+
// Single source of truth for the menu's filter/highlight token per call.
|
|
20711
|
+
// syncValuesAndStates re-writes the same value on exact-match keystrokes —
|
|
20712
|
+
// Lit's hasChanged makes that a no-op — and the prior duplicate set inside
|
|
20713
|
+
// handleTriggerInputValueChange is gone.
|
|
20714
|
+
if (this.menu) {
|
|
20715
|
+
this.menu.matchWord = normalizeFilterValue(this.input.value);
|
|
20716
|
+
}
|
|
20706
20717
|
this.updateFilter();
|
|
20707
20718
|
|
|
20708
20719
|
// Set aria-setsize/aria-posinset on each visible option so screen readers
|
|
@@ -20714,17 +20725,14 @@ class AuroCombobox extends AuroElement {
|
|
|
20714
20725
|
option.setAttribute('aria-posinset', index + 1);
|
|
20715
20726
|
});
|
|
20716
20727
|
|
|
20717
|
-
if (this.value && this.
|
|
20718
|
-
|
|
20719
|
-
this.setMenuValue(this.value);
|
|
20720
|
-
}
|
|
20721
|
-
|
|
20728
|
+
if (this.input.value && this.menu.options && this.menu.options.some((opt) => opt.value === this.input.value)) {
|
|
20729
|
+
this.setMenuValue(this.input.value);
|
|
20722
20730
|
this.syncValuesAndStates();
|
|
20723
20731
|
}
|
|
20724
20732
|
|
|
20725
|
-
// Re-activate when optionActive is
|
|
20726
|
-
// been reset (e.g. by clearSelection in an async update)
|
|
20727
|
-
// makeSelection() can find the correct option.
|
|
20733
|
+
// Re-activate when optionActive is not in the current scrollable viewport,
|
|
20734
|
+
// or when _index has been reset (e.g. by clearSelection in an async update)
|
|
20735
|
+
// so that makeSelection() can find the correct option.
|
|
20728
20736
|
if (!this.availableOptions.includes(this.menu.optionActive) || this.menu._index < 0) {
|
|
20729
20737
|
this.activateFirstEnabledAvailableOption();
|
|
20730
20738
|
}
|
|
@@ -20797,18 +20805,6 @@ class AuroCombobox extends AuroElement {
|
|
|
20797
20805
|
this.dropdownOpen = ev.detail.expanded;
|
|
20798
20806
|
this.updateMenuShapeSize();
|
|
20799
20807
|
|
|
20800
|
-
// Defer aria-expanded "true" so VoiceOver finishes character echo
|
|
20801
|
-
// before announcing "expanded". Set "false" immediately on close.
|
|
20802
|
-
clearTimeout(this._expandedTimeout);
|
|
20803
|
-
if (this.dropdownOpen) {
|
|
20804
|
-
const expandedDelay = 150;
|
|
20805
|
-
this._expandedTimeout = setTimeout(() => {
|
|
20806
|
-
this.triggerExpandedState = true;
|
|
20807
|
-
}, expandedDelay);
|
|
20808
|
-
} else {
|
|
20809
|
-
this.triggerExpandedState = false;
|
|
20810
|
-
}
|
|
20811
|
-
|
|
20812
20808
|
// Clear aria-activedescendant when dropdown closes
|
|
20813
20809
|
if (!this.dropdownOpen && this.input) {
|
|
20814
20810
|
this.input.setActiveDescendant(null);
|
|
@@ -20858,28 +20854,25 @@ class AuroCombobox extends AuroElement {
|
|
|
20858
20854
|
|
|
20859
20855
|
guardTouchPassthrough(this.menu);
|
|
20860
20856
|
|
|
20861
|
-
//
|
|
20862
|
-
//
|
|
20863
|
-
//
|
|
20857
|
+
// showModal() takes focus away from the trigger. Early focus once
|
|
20858
|
+
// the dialog has painted; the shared doubleRaf below is the fallback
|
|
20859
|
+
// for when the dialog needs an extra frame and also clears the
|
|
20860
|
+
// validation guard.
|
|
20864
20861
|
requestAnimationFrame(() => {
|
|
20865
20862
|
this.setInputFocus();
|
|
20866
20863
|
});
|
|
20867
|
-
|
|
20868
|
-
doubleRaf(() => {
|
|
20869
|
-
this.setInputFocus();
|
|
20870
|
-
this._inFullscreenTransition = false;
|
|
20871
|
-
});
|
|
20872
|
-
} else {
|
|
20873
|
-
// Desktop popover-open: restore the trigger caret to end-of-text.
|
|
20874
|
-
// Clicking the trigger lands on auro-input's floating <label for="…">
|
|
20875
|
-
// overlay; Chrome resets the native input's selection to [0, 0] on
|
|
20876
|
-
// label-focus before any JS runs. setInputFocus()'s non-fullscreen
|
|
20877
|
-
// branch parks the caret at end. doubleRaf lets the dropdown layout
|
|
20878
|
-
// settle first, matching the fullscreen branch timing.
|
|
20879
|
-
doubleRaf(() => {
|
|
20880
|
-
this.setInputFocus();
|
|
20881
|
-
});
|
|
20882
20864
|
}
|
|
20865
|
+
// else (desktop popover-open): Chrome resets the trigger caret to
|
|
20866
|
+
// [0, 0] when its floating <label for="…"> overlay receives focus.
|
|
20867
|
+
// The shared doubleRaf below parks the caret back at end-of-text
|
|
20868
|
+
// after the dropdown layout settles.
|
|
20869
|
+
|
|
20870
|
+
doubleRaf(() => {
|
|
20871
|
+
this.setInputFocus();
|
|
20872
|
+
if (this._inFullscreenTransition) {
|
|
20873
|
+
this._inFullscreenTransition = false;
|
|
20874
|
+
}
|
|
20875
|
+
});
|
|
20883
20876
|
}
|
|
20884
20877
|
});
|
|
20885
20878
|
|
|
@@ -20916,7 +20909,7 @@ class AuroCombobox extends AuroElement {
|
|
|
20916
20909
|
this.dropdown.trigger.inert = false;
|
|
20917
20910
|
}
|
|
20918
20911
|
|
|
20919
|
-
|
|
20912
|
+
this._scheduleTimer(() => {
|
|
20920
20913
|
this.setInputFocus();
|
|
20921
20914
|
}, 0);
|
|
20922
20915
|
});
|
|
@@ -21060,7 +21053,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21060
21053
|
|
|
21061
21054
|
// racing condition on custom-combobox with custom-menu
|
|
21062
21055
|
if (!this.menu) {
|
|
21063
|
-
|
|
21056
|
+
this._scheduleTimer(() => {
|
|
21064
21057
|
this.configureMenu();
|
|
21065
21058
|
}, 0);
|
|
21066
21059
|
return;
|
|
@@ -21090,7 +21083,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21090
21083
|
if (this.menu.optionSelected) {
|
|
21091
21084
|
const selected = this.menu.optionSelected;
|
|
21092
21085
|
|
|
21093
|
-
if (
|
|
21086
|
+
if (this.optionSelected !== selected) {
|
|
21094
21087
|
this.optionSelected = selected;
|
|
21095
21088
|
}
|
|
21096
21089
|
|
|
@@ -21103,7 +21096,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21103
21096
|
}
|
|
21104
21097
|
|
|
21105
21098
|
// Update display
|
|
21106
|
-
this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected)
|
|
21099
|
+
this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected));
|
|
21107
21100
|
|
|
21108
21101
|
// Update match word for filtering
|
|
21109
21102
|
const trimmedInput = normalizeFilterValue(this.input.value);
|
|
@@ -21115,17 +21108,24 @@ class AuroCombobox extends AuroElement {
|
|
|
21115
21108
|
// Hide dropdown on selection (except during slot changes)
|
|
21116
21109
|
if (evt.detail && evt.detail.source !== 'slotchange') {
|
|
21117
21110
|
// do not close while typing in suggestion mode with no value selected, to allow freeform input
|
|
21118
|
-
|
|
21119
|
-
|
|
21111
|
+
this.hideBib();
|
|
21112
|
+
|
|
21113
|
+
// Move focus to the clear button when the user makes a selection.
|
|
21114
|
+
if (!isEcho && this.menu.value !== undefined) {
|
|
21115
|
+
this.setClearBtnFocus();
|
|
21120
21116
|
}
|
|
21121
21117
|
|
|
21122
21118
|
// Announce the selection after the dropdown closes so it isn't
|
|
21123
21119
|
// overridden by VoiceOver's "collapsed" announcement from aria-expanded.
|
|
21120
|
+
// Skip when there's no selected value (e.g. menu.clearSelection() from
|
|
21121
|
+
// the unmatched-value path), otherwise VoiceOver reads "undefined".
|
|
21124
21122
|
const selectedValue = this.menu.value;
|
|
21125
|
-
|
|
21126
|
-
|
|
21127
|
-
|
|
21128
|
-
|
|
21123
|
+
if (selectedValue) {
|
|
21124
|
+
const announcementDelay = 300;
|
|
21125
|
+
this._scheduleTimer(() => {
|
|
21126
|
+
announceToScreenReader(this._getAnnouncementRoot(), `${selectedValue}, selected`);
|
|
21127
|
+
}, announcementDelay);
|
|
21128
|
+
}
|
|
21129
21129
|
}
|
|
21130
21130
|
|
|
21131
21131
|
// Programmatic value syncs leave availableOptions stale because
|
|
@@ -21134,22 +21134,6 @@ class AuroCombobox extends AuroElement {
|
|
|
21134
21134
|
// only — fresh user selections take the existing hideBib path.
|
|
21135
21135
|
if (isEcho && this.menu.optionSelected) {
|
|
21136
21136
|
this._programmaticFilterRefresh = true;
|
|
21137
|
-
this.handleMenuOptions();
|
|
21138
|
-
setTimeout(() => {
|
|
21139
|
-
this._programmaticFilterRefresh = false;
|
|
21140
|
-
}, 0);
|
|
21141
|
-
}
|
|
21142
|
-
|
|
21143
|
-
// base-input skips auto-validate when focus is inside its own shadow,
|
|
21144
|
-
// which it is after setTriggerInputFocus — re-run so prior tooShort
|
|
21145
|
-
// clears. processCreditCard reasserts errorMessage on every render.
|
|
21146
|
-
if (!isEcho && this.menu.optionSelected && this.input.validate) {
|
|
21147
|
-
this.input.updateComplete.then(() => {
|
|
21148
|
-
this.input.validate(true);
|
|
21149
|
-
if (this.input.validity === 'valid') {
|
|
21150
|
-
this.input.errorMessage = '';
|
|
21151
|
-
}
|
|
21152
|
-
});
|
|
21153
21137
|
}
|
|
21154
21138
|
});
|
|
21155
21139
|
|
|
@@ -21162,6 +21146,14 @@ class AuroCombobox extends AuroElement {
|
|
|
21162
21146
|
// stale option. Safe from re-entrancy because any resulting
|
|
21163
21147
|
// input.value changes dispatch isProgrammatic events.
|
|
21164
21148
|
this.menu.addEventListener('auroMenu-selectValueFailure', () => {
|
|
21149
|
+
// Announce the rejection BEFORE we clear `this.value` so the live
|
|
21150
|
+
// region carries the attempted value — without this the bib closes
|
|
21151
|
+
// silently and screen-reader users get no signal that their request
|
|
21152
|
+
// (e.g. a direct setMenuValue() call) was dropped.
|
|
21153
|
+
const attemptedValue = this.value;
|
|
21154
|
+
if (attemptedValue) {
|
|
21155
|
+
announceToScreenReader(this._getAnnouncementRoot(), `No matching option for ${attemptedValue}`);
|
|
21156
|
+
}
|
|
21165
21157
|
this.value = undefined;
|
|
21166
21158
|
this.optionSelected = undefined;
|
|
21167
21159
|
});
|
|
@@ -21173,10 +21165,13 @@ class AuroCombobox extends AuroElement {
|
|
|
21173
21165
|
this.input.setActiveDescendant(this.optionActive);
|
|
21174
21166
|
}
|
|
21175
21167
|
|
|
21176
|
-
//
|
|
21177
|
-
//
|
|
21178
|
-
//
|
|
21179
|
-
|
|
21168
|
+
// In fullscreen mode the menu sits inside a nested <dialog> shadow root,
|
|
21169
|
+
// and aria-activedescendant references across that boundary are lost —
|
|
21170
|
+
// VoiceOver/NVDA don't read the active option natively, so we mirror it
|
|
21171
|
+
// into the polite live region. In popover mode aria-activedescendant on
|
|
21172
|
+
// the trigger input is read natively; double-announcing would flood the
|
|
21173
|
+
// queue on arrow-key repeat.
|
|
21174
|
+
if (this.optionActive && this.dropdown.isBibFullscreen) {
|
|
21180
21175
|
const optionText = this.optionActive.textContent.trim();
|
|
21181
21176
|
const selectedState = this.optionActive.hasAttribute('selected') ? ', selected' : ', not selected';
|
|
21182
21177
|
const optionIndex = this.availableOptions.indexOf(this.optionActive) + 1;
|
|
@@ -21209,7 +21204,12 @@ class AuroCombobox extends AuroElement {
|
|
|
21209
21204
|
* Validate every time we remove focus from the combo box.
|
|
21210
21205
|
*/
|
|
21211
21206
|
this.addEventListener('focusout', () => {
|
|
21212
|
-
|
|
21207
|
+
// Skip while the dropdown is open — focus transits out briefly on
|
|
21208
|
+
// mousedown of a menu option (popover top-layer breaks :focus-within),
|
|
21209
|
+
// and validating against the pre-selection value flashes a stale error
|
|
21210
|
+
// between mousedown and mouseup. The next focusout fires after the
|
|
21211
|
+
// dropdown closes and validates against the post-selection value.
|
|
21212
|
+
if (!this.componentHasFocus && !this._inFullscreenTransition && !this.dropdownOpen) {
|
|
21213
21213
|
this.validate();
|
|
21214
21214
|
}
|
|
21215
21215
|
});
|
|
@@ -21272,49 +21272,40 @@ class AuroCombobox extends AuroElement {
|
|
|
21272
21272
|
if (this._syncingDisplayValue) {
|
|
21273
21273
|
return;
|
|
21274
21274
|
}
|
|
21275
|
-
this._syncingBibValue = true;
|
|
21276
|
-
this.input.value = this.inputInBib.value;
|
|
21277
|
-
this.input.updateComplete.then(() => {
|
|
21278
|
-
this._syncingBibValue = false;
|
|
21279
|
-
});
|
|
21280
21275
|
|
|
21281
|
-
//
|
|
21282
|
-
|
|
21283
|
-
|
|
21284
|
-
|
|
21285
|
-
|
|
21286
|
-
|
|
21287
|
-
|
|
21288
|
-
|
|
21289
|
-
|
|
21276
|
+
// Filtering runs via re-entrance: writing this.input.value below
|
|
21277
|
+
// dispatches an 'input' event that the trigger's listener routes to
|
|
21278
|
+
// handleTriggerInputValueChange, which refreshes the menu filter.
|
|
21279
|
+
if (this.input.value !== this.inputInBib.value) {
|
|
21280
|
+
this._syncingBibValue = true;
|
|
21281
|
+
this.input.value = this.inputInBib.value;
|
|
21282
|
+
this.input.updateComplete.then(() => {
|
|
21283
|
+
this._syncingBibValue = false;
|
|
21284
|
+
});
|
|
21290
21285
|
}
|
|
21291
21286
|
|
|
21292
|
-
this.handleMenuOptions();
|
|
21293
21287
|
this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
|
|
21294
|
-
|
|
21288
|
+
} else if (event.target === this.input) {
|
|
21289
|
+
|
|
21290
|
+
// Also sync the native input immediately so keystrokes arriving
|
|
21291
|
+
// before Lit's async update cycle (e.g. rapid Backspaces during a
|
|
21292
|
+
// fullscreen transition) operate on the correct content.
|
|
21293
|
+
// Skip the next programmatic input event to prevent the patched setter
|
|
21294
|
+
// from re-entering handleInputValueChange via the bib path.
|
|
21295
|
+
const bibNativeInput = this.inputInBib.inputElement;
|
|
21296
|
+
if (bibNativeInput && bibNativeInput.value !== this.input.value) {
|
|
21297
|
+
this.inputInBib.skipNextProgrammaticInputEvent = true;
|
|
21298
|
+
bibNativeInput.value = this.input.value || '';
|
|
21299
|
+
}
|
|
21295
21300
|
}
|
|
21296
21301
|
|
|
21302
|
+
this.value = this.input.value;
|
|
21303
|
+
|
|
21297
21304
|
// Ignore re-entrant input events caused by programmatic value sets.
|
|
21298
21305
|
if (this._syncingBibValue || this._syncingDisplayValue) {
|
|
21299
21306
|
return;
|
|
21300
21307
|
}
|
|
21301
21308
|
|
|
21302
|
-
this.inputInBib.value = this.input.value;
|
|
21303
|
-
|
|
21304
|
-
// Also sync the native input immediately so keystrokes arriving
|
|
21305
|
-
// before Lit's async update cycle (e.g. rapid Backspaces during a
|
|
21306
|
-
// fullscreen transition) operate on the correct content.
|
|
21307
|
-
// Skip the next programmatic input event to prevent the patched setter
|
|
21308
|
-
// from re-entering handleInputValueChange via the bib path.
|
|
21309
|
-
const bibNativeInput = this.inputInBib.inputElement;
|
|
21310
|
-
if (bibNativeInput && bibNativeInput.value !== this.input.value) {
|
|
21311
|
-
this.inputInBib.skipNextProgrammaticInputEvent = true;
|
|
21312
|
-
bibNativeInput.value = this.input.value || '';
|
|
21313
|
-
}
|
|
21314
|
-
|
|
21315
|
-
this.menu.matchWord = normalizeFilterValue(this.input.value);
|
|
21316
|
-
this.optionActive = null;
|
|
21317
|
-
|
|
21318
21309
|
if (this.behavior === 'suggestion') {
|
|
21319
21310
|
this.value = this.input.value;
|
|
21320
21311
|
}
|
|
@@ -21322,7 +21313,6 @@ class AuroCombobox extends AuroElement {
|
|
|
21322
21313
|
if (!this.input.value && !this._clearing) {
|
|
21323
21314
|
this.clear();
|
|
21324
21315
|
}
|
|
21325
|
-
this.handleMenuOptions();
|
|
21326
21316
|
|
|
21327
21317
|
// Validate only if the value was set programmatically (not during user
|
|
21328
21318
|
// interaction). In fullscreen dialog mode, componentHasFocus returns false
|
|
@@ -21332,34 +21322,44 @@ class AuroCombobox extends AuroElement {
|
|
|
21332
21322
|
this.validate();
|
|
21333
21323
|
}
|
|
21334
21324
|
|
|
21335
|
-
|
|
21336
|
-
|
|
21337
|
-
|
|
21338
|
-
|
|
21339
|
-
|
|
21340
|
-
|
|
21341
|
-
|
|
21342
|
-
|
|
21343
|
-
|
|
21325
|
+
this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
|
|
21326
|
+
}
|
|
21327
|
+
|
|
21328
|
+
/**
|
|
21329
|
+
* Handles input value changes originating from the trigger input.
|
|
21330
|
+
* Refreshes menu options and filtering, delegates to handleInputValueChange
|
|
21331
|
+
* for value synchronization, and manages fullscreen bib focus.
|
|
21332
|
+
* @private
|
|
21333
|
+
* @param {Event} event - The input event from the trigger input element.
|
|
21334
|
+
* @returns {void}
|
|
21335
|
+
*/
|
|
21336
|
+
handleTriggerInputValueChange(event) {
|
|
21337
|
+
// 'input' fires for every user-initiated value change — typing, paste,
|
|
21338
|
+
// IME composition end, dead-key composition, drag-drop. Flip _userTyped
|
|
21339
|
+
// here so updated('availableOptions') auto-opens the bib for sources
|
|
21340
|
+
// that keydown alone misses: paste fires no keydown, IME uses
|
|
21341
|
+
// key='Process', and dead keys produce multi-char keys (all bypass the
|
|
21342
|
+
// prior keydown.key.length===1 gate). Skip programmatic syncs.
|
|
21343
|
+
if (!this._syncingDisplayValue && !this._syncingBibValue && !this._programmaticFilterRefresh) {
|
|
21344
|
+
this._userTyped = true;
|
|
21344
21345
|
}
|
|
21345
21346
|
|
|
21346
|
-
|
|
21347
|
-
|
|
21348
|
-
|
|
21349
|
-
|
|
21350
|
-
|
|
21351
|
-
// dialog opens — the user then has to tap the input again to resume
|
|
21352
|
-
// typing.
|
|
21353
|
-
if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0) {
|
|
21354
|
-
if (!this.dropdown.isPopoverVisible) {
|
|
21355
|
-
this.showBib();
|
|
21356
|
-
}
|
|
21357
|
-
if (this.dropdown.isPopoverVisible) {
|
|
21358
|
-
this.setInputFocus();
|
|
21359
|
-
}
|
|
21347
|
+
this.handleMenuOptions();
|
|
21348
|
+
this.optionActive = null;
|
|
21349
|
+
|
|
21350
|
+
if (this.value === this.input.value) {
|
|
21351
|
+
return;
|
|
21360
21352
|
}
|
|
21361
21353
|
|
|
21362
|
-
this.
|
|
21354
|
+
this.handleInputValueChange(event);
|
|
21355
|
+
|
|
21356
|
+
if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0 && this.dropdown.isPopoverVisible) {
|
|
21357
|
+
this.setInputFocus();
|
|
21358
|
+
}
|
|
21359
|
+
|
|
21360
|
+
if (this._programmaticFilterRefresh) {
|
|
21361
|
+
this._programmaticFilterRefresh = false;
|
|
21362
|
+
}
|
|
21363
21363
|
}
|
|
21364
21364
|
|
|
21365
21365
|
/**
|
|
@@ -21427,6 +21427,11 @@ class AuroCombobox extends AuroElement {
|
|
|
21427
21427
|
disconnectedCallback() {
|
|
21428
21428
|
super.disconnectedCallback();
|
|
21429
21429
|
this._inFullscreenTransition = false;
|
|
21430
|
+
// Cancel any outstanding timers so detached callbacks don't fire on
|
|
21431
|
+
// disposed DOM — most are no-ops, but configureMenu's racing-condition
|
|
21432
|
+
// retry would otherwise keep rescheduling itself indefinitely.
|
|
21433
|
+
this._pendingTimers.forEach((id) => clearTimeout(id));
|
|
21434
|
+
this._pendingTimers.clear();
|
|
21430
21435
|
}
|
|
21431
21436
|
|
|
21432
21437
|
firstUpdated() {
|
|
@@ -21468,7 +21473,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21468
21473
|
this.menu.value = value;
|
|
21469
21474
|
// Backup clear: if menu.value === menu.optionSelected.value already, the
|
|
21470
21475
|
// listener won't fire and the flag would swallow the next user click.
|
|
21471
|
-
|
|
21476
|
+
this._scheduleTimer(() => {
|
|
21472
21477
|
this._pendingMenuValueSync = false;
|
|
21473
21478
|
}, 0);
|
|
21474
21479
|
}
|
|
@@ -21485,15 +21490,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21485
21490
|
this.menu.value = undefined;
|
|
21486
21491
|
this.validation.reset(this);
|
|
21487
21492
|
this.touched = false;
|
|
21488
|
-
// Force validity back to the cleared state. validation.reset() calls
|
|
21489
|
-
// validate() at the end, and (post-credit-card-fix) the trigger input's
|
|
21490
|
-
// residual validity may still be copied into the combobox by the
|
|
21491
|
-
// auroInputElements loop. Explicitly clear here so reset() actually
|
|
21492
|
-
// leaves the component in a "no validity" state.
|
|
21493
21493
|
this.validity = undefined;
|
|
21494
|
-
this.errorMessage = '';
|
|
21495
|
-
this.input.validity = undefined;
|
|
21496
|
-
this.input.errorMessage = '';
|
|
21497
21494
|
}
|
|
21498
21495
|
|
|
21499
21496
|
/**
|
|
@@ -21511,6 +21508,17 @@ class AuroCombobox extends AuroElement {
|
|
|
21511
21508
|
this.optionSelected = undefined;
|
|
21512
21509
|
this.value = undefined;
|
|
21513
21510
|
|
|
21511
|
+
// Clear the appended displayValue clone in the trigger if present.
|
|
21512
|
+
// :not(slot) excludes the template's <slot name="displayValue"
|
|
21513
|
+
// slot="displayValue"> forwarder (line 1816), which also has
|
|
21514
|
+
// slot="displayValue" and would otherwise be matched first and removed,
|
|
21515
|
+
// permanently breaking the consumer-provided displayValue slot.
|
|
21516
|
+
const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]:not(slot)');
|
|
21517
|
+
|
|
21518
|
+
if (displayValueInTrigger) {
|
|
21519
|
+
displayValueInTrigger.remove();
|
|
21520
|
+
}
|
|
21521
|
+
|
|
21514
21522
|
if (this.input.value) {
|
|
21515
21523
|
this.input.clear();
|
|
21516
21524
|
}
|
|
@@ -21534,81 +21542,50 @@ class AuroCombobox extends AuroElement {
|
|
|
21534
21542
|
}
|
|
21535
21543
|
|
|
21536
21544
|
updated(changedProperties) {
|
|
21537
|
-
// After the component is ready,
|
|
21545
|
+
// After the component is ready, propagate direct changes down to child components.
|
|
21538
21546
|
if (changedProperties.has('value')) {
|
|
21539
|
-
|
|
21540
|
-
|
|
21541
|
-
|
|
21542
|
-
|
|
21547
|
+
// Only flag programmatic refreshes — user-typed value changes must not
|
|
21548
|
+
// suppress the availableOptions branch's showBib(). Firefox batches
|
|
21549
|
+
// 'value' and 'availableOptions' into the same updated() call, so
|
|
21550
|
+
// setting the flag unconditionally here masks the user-typed open path.
|
|
21551
|
+
if (!this._userTyped) {
|
|
21552
|
+
this._programmaticFilterRefresh = true;
|
|
21543
21553
|
}
|
|
21544
21554
|
|
|
21545
|
-
if (this.
|
|
21546
|
-
|
|
21547
|
-
|
|
21555
|
+
if (this.input.value !== this.value) {
|
|
21556
|
+
// Clear menu.value AND menu.optionSelected together. Clearing only
|
|
21557
|
+
// menu.value leaves the previously-selected option element pinned
|
|
21558
|
+
// as menu.optionSelected; a later auroMenu-selectedOption event
|
|
21559
|
+
// would then write its stale .value back into combobox.value
|
|
21560
|
+
// (e.g. Tab-after-Backspace re-selecting the prior option).
|
|
21561
|
+
if (this.menu.value || this.menu.optionSelected) {
|
|
21562
|
+
this.menu.clearSelection();
|
|
21563
|
+
}
|
|
21548
21564
|
|
|
21549
|
-
|
|
21550
|
-
|
|
21551
|
-
|
|
21552
|
-
|
|
21553
|
-
|
|
21554
|
-
|
|
21555
|
-
|
|
21556
|
-
|
|
21557
|
-
|
|
21558
|
-
|
|
21559
|
-
|
|
21560
|
-
|
|
21561
|
-
|
|
21562
|
-
|
|
21563
|
-
|
|
21564
|
-
|
|
21565
|
-
|
|
21566
|
-
|
|
21567
|
-
// matches to avoid spurious events during the normal typing path.
|
|
21568
|
-
const nextValue = this.value || '';
|
|
21569
|
-
const triggerNeedsSync = this.input.value !== nextValue;
|
|
21570
|
-
const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
|
|
21571
|
-
if (triggerNeedsSync || bibNeedsSync) {
|
|
21572
|
-
this._syncingDisplayValue = true;
|
|
21573
|
-
if (triggerNeedsSync) {
|
|
21574
|
-
this.input.value = nextValue;
|
|
21575
|
-
}
|
|
21576
|
-
if (bibNeedsSync) {
|
|
21577
|
-
this.inputInBib.value = nextValue;
|
|
21578
|
-
}
|
|
21579
|
-
const pending = [];
|
|
21580
|
-
if (triggerNeedsSync) {
|
|
21581
|
-
pending.push(this.input.updateComplete);
|
|
21582
|
-
}
|
|
21583
|
-
if (bibNeedsSync) {
|
|
21584
|
-
pending.push(this.inputInBib.updateComplete);
|
|
21585
|
-
}
|
|
21586
|
-
Promise.all(pending).then(() => {
|
|
21587
|
-
if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
|
|
21588
|
-
this.input.maskInstance.updateValue();
|
|
21589
|
-
}
|
|
21590
|
-
if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
|
|
21591
|
-
this.inputInBib.maskInstance.updateValue();
|
|
21592
|
-
}
|
|
21593
|
-
this._syncingDisplayValue = false;
|
|
21594
|
-
// handleInputValueChange bailed on the flag — refresh the filter.
|
|
21595
|
-
this._programmaticFilterRefresh = true;
|
|
21596
|
-
this.handleMenuOptions();
|
|
21597
|
-
setTimeout(() => {
|
|
21598
|
-
this._programmaticFilterRefresh = false;
|
|
21599
|
-
}, 0);
|
|
21600
|
-
});
|
|
21601
|
-
}
|
|
21565
|
+
if (!this.persistInput) {
|
|
21566
|
+
this.syncInputValuesAcrossTriggerAndBib(this.value || '');
|
|
21567
|
+
}
|
|
21568
|
+
|
|
21569
|
+
// Programmatic value with no matching option: updateFilter will close
|
|
21570
|
+
// the bib silently (see line 648 — no noMatchOption + 0 results
|
|
21571
|
+
// hides). Announce so screen-reader users hear the request was
|
|
21572
|
+
// dropped. Gated on `input.value !== this.value` so this never fires
|
|
21573
|
+
// for user typing — that path always reconciles input.value to
|
|
21574
|
+
// this.value before updated() runs.
|
|
21575
|
+
if (
|
|
21576
|
+
this.value &&
|
|
21577
|
+
this.menu &&
|
|
21578
|
+
this.menu.options &&
|
|
21579
|
+
this.menu.options.length > 0 &&
|
|
21580
|
+
!this.menu.options.some((opt) => opt.value === this.value)
|
|
21581
|
+
) {
|
|
21582
|
+
announceToScreenReader(this._getAnnouncementRoot(), `No matching option for ${this.value}`);
|
|
21602
21583
|
}
|
|
21603
21584
|
}
|
|
21585
|
+
|
|
21604
21586
|
if (!this.value) {
|
|
21605
21587
|
this.clear();
|
|
21606
21588
|
}
|
|
21607
|
-
if (this.value && !this.componentHasFocus) {
|
|
21608
|
-
// If the value got set programmatically make sure we hide the bib
|
|
21609
|
-
// when input is not taking the focus (input can be in dropdown.trigger or in bibtemplate)
|
|
21610
|
-
this.hideBib();
|
|
21611
|
-
}
|
|
21612
21589
|
|
|
21613
21590
|
// Sync the input and match word, but don't directly set menu.value again
|
|
21614
21591
|
if (this.menu) {
|
|
@@ -21621,7 +21598,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21621
21598
|
composed: true,
|
|
21622
21599
|
detail: {
|
|
21623
21600
|
optionSelected: this.menu.optionSelected,
|
|
21624
|
-
value: this.
|
|
21601
|
+
value: this.value
|
|
21625
21602
|
}
|
|
21626
21603
|
}));
|
|
21627
21604
|
|
|
@@ -21644,16 +21621,30 @@ class AuroCombobox extends AuroElement {
|
|
|
21644
21621
|
// from a programmatic filter refresh after a value swap — the user
|
|
21645
21622
|
// didn't interact, so we shouldn't auto-open the bib (especially for
|
|
21646
21623
|
// the no-match path where the existing condition unconditionally fires).
|
|
21647
|
-
if (this._programmaticFilterRefresh) {
|
|
21648
|
-
|
|
21649
|
-
|
|
21650
|
-
|
|
21624
|
+
if ((this.menu && !this._programmaticFilterRefresh)) {
|
|
21625
|
+
if (
|
|
21626
|
+
this.availableOptions.length > 0 ||
|
|
21627
|
+
this.menu.loading ||
|
|
21628
|
+
this.noMatchOption
|
|
21629
|
+
) {
|
|
21630
|
+
if (this._userTyped) {
|
|
21631
|
+
if (!this.dropdownOpen) {
|
|
21632
|
+
this.showBib();
|
|
21633
|
+
}
|
|
21634
|
+
this._userTyped = false;
|
|
21635
|
+
}
|
|
21636
|
+
}
|
|
21637
|
+
|
|
21651
21638
|
if (!this.availableOptions.includes(this.menu.optionActive)) {
|
|
21652
21639
|
this.activateFirstEnabledAvailableOption();
|
|
21653
21640
|
}
|
|
21654
|
-
} else if (this.dropdown
|
|
21641
|
+
} else if (!this.dropdown.isBibFullscreen) {
|
|
21655
21642
|
this.hideBib();
|
|
21656
21643
|
}
|
|
21644
|
+
|
|
21645
|
+
if (this._programmaticFilterRefresh) {
|
|
21646
|
+
this._programmaticFilterRefresh = false;
|
|
21647
|
+
}
|
|
21657
21648
|
}
|
|
21658
21649
|
|
|
21659
21650
|
if (changedProperties.has('error')) {
|
|
@@ -21795,10 +21786,10 @@ class AuroCombobox extends AuroElement {
|
|
|
21795
21786
|
shape="${this.shape}"
|
|
21796
21787
|
size="${this.size}">
|
|
21797
21788
|
<${this.inputTag}
|
|
21798
|
-
@input="${this.
|
|
21789
|
+
@input="${this.handleTriggerInputValueChange}"
|
|
21799
21790
|
appearance="${this.onDark ? 'inverse' : this.appearance}"
|
|
21800
21791
|
.a11yActivedescendant="${this.dropdownOpen && this.optionActive ? this.optionActive.id : undefined}"
|
|
21801
|
-
.a11yExpanded="${this.
|
|
21792
|
+
.a11yExpanded="${this.dropdownOpen}"
|
|
21802
21793
|
.a11yControls="${this.dropdownId}"
|
|
21803
21794
|
.autocomplete="${this.autocomplete}"
|
|
21804
21795
|
.inputmode="${this.inputmode}"
|