@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
|
@@ -387,11 +387,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
|
|
|
387
387
|
return;
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
-
// Validate that the date passed was the correct format and is a valid date
|
|
390
|
+
// Validate that the date passed was the correct format and is a valid date.
|
|
391
|
+
// For partial date formats, valueObject is never populated; validate them directly.
|
|
391
392
|
if (elem.value && !elem.valueObject) {
|
|
392
|
-
elem.
|
|
393
|
-
|
|
394
|
-
|
|
393
|
+
const isPartialDateFormat = elem.util && !elem.util.isFullDateFormat(elem.type, elem.format);
|
|
394
|
+
const isValidPartial = isPartialDateFormat && elem.util.isValidPartialDate(elem.value, elem.format);
|
|
395
|
+
|
|
396
|
+
if (!isValidPartial) {
|
|
397
|
+
elem.validity = 'patternMismatch';
|
|
398
|
+
elem.errorMessage = elem.setCustomValidityPatternMismatch || elem.setCustomValidity || 'Invalid Date Format Entered';
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
395
401
|
}
|
|
396
402
|
|
|
397
403
|
// Perform the rest of the validation
|
|
@@ -485,15 +491,17 @@ let AuroFormValidation$1 = class AuroFormValidation {
|
|
|
485
491
|
);
|
|
486
492
|
}
|
|
487
493
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
494
|
+
const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
|
|
495
|
+
|
|
496
|
+
// If there is a second input in the elem and that value is undefined or an empty string set hasValue to false.
|
|
497
|
+
// Skip for combobox: its second auro-input is the fullscreen-bib mirror of the same value, not an independent
|
|
498
|
+
// field (datepicker is the intended consumer — start/end are independently required).
|
|
499
|
+
if (this.auroInputElements?.length === 2 && !isCombobox) {
|
|
500
|
+
if (!this.auroInputElements[1].value || this.auroInputElements[1].value.length === 0) {
|
|
491
501
|
hasValue = false;
|
|
492
502
|
}
|
|
493
503
|
}
|
|
494
504
|
|
|
495
|
-
const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
|
|
496
|
-
|
|
497
505
|
if (isCombobox) {
|
|
498
506
|
|
|
499
507
|
if (!elem.persistInput || elem.behavior === "filter") {
|
|
@@ -810,6 +818,8 @@ function navigateArrow(component, direction, options = {}) {
|
|
|
810
818
|
}
|
|
811
819
|
}
|
|
812
820
|
|
|
821
|
+
/* eslint-disable no-underscore-dangle */
|
|
822
|
+
|
|
813
823
|
/**
|
|
814
824
|
* Returns the clear button element from the active input's shadow
|
|
815
825
|
* DOM, if available.
|
|
@@ -845,11 +855,9 @@ function isClearBtnFocused(ctx, clearBtn = getClearBtn(ctx)) {
|
|
|
845
855
|
* @param {Object} menu - The menu component.
|
|
846
856
|
*/
|
|
847
857
|
function reconcileMenuIndex(menu) {
|
|
848
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
849
858
|
if (menu._index < 0 && menu.optionActive && menu.items) {
|
|
850
859
|
const idx = menu.items.indexOf(menu.optionActive);
|
|
851
860
|
if (idx >= 0) {
|
|
852
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
853
861
|
menu._index = idx;
|
|
854
862
|
}
|
|
855
863
|
}
|
|
@@ -868,11 +876,7 @@ const comboboxKeyboardStrategy = {
|
|
|
868
876
|
|
|
869
877
|
// navigate if bib is open otherwise open it
|
|
870
878
|
if (component.dropdown.isPopoverVisible) {
|
|
871
|
-
|
|
872
|
-
component.activateLastEnabledAvailableOption();
|
|
873
|
-
} else {
|
|
874
|
-
navigateArrow(component, 'down');
|
|
875
|
-
}
|
|
879
|
+
navigateArrow(component, 'down');
|
|
876
880
|
} else {
|
|
877
881
|
component.showBib();
|
|
878
882
|
}
|
|
@@ -891,11 +895,7 @@ const comboboxKeyboardStrategy = {
|
|
|
891
895
|
|
|
892
896
|
// navigate if bib is open otherwise open it
|
|
893
897
|
if (component.dropdown.isPopoverVisible) {
|
|
894
|
-
|
|
895
|
-
component.activateFirstEnabledAvailableOption();
|
|
896
|
-
} else {
|
|
897
|
-
navigateArrow(component, 'up');
|
|
898
|
-
}
|
|
898
|
+
navigateArrow(component, 'up');
|
|
899
899
|
} else {
|
|
900
900
|
component.showBib();
|
|
901
901
|
}
|
|
@@ -911,13 +911,18 @@ const comboboxKeyboardStrategy = {
|
|
|
911
911
|
},
|
|
912
912
|
|
|
913
913
|
Enter(component, evt, ctx) {
|
|
914
|
+
// Forms should not submit on Enter from a combobox, regardless of which
|
|
915
|
+
// child element (input, clear button, menu) is focused.
|
|
916
|
+
evt.stopPropagation();
|
|
917
|
+
|
|
914
918
|
if (isClearBtnFocused(ctx)) {
|
|
915
|
-
//
|
|
916
|
-
//
|
|
917
|
-
//
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
919
|
+
// Let the browser dispatch Enter to the focused clear button so its
|
|
920
|
+
// built-in activation fires and clears the selection. Do NOT call
|
|
921
|
+
// preventDefault — that would block the activation.
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
if (ctx.isExpanded && component.menu.optionActive) {
|
|
921
926
|
reconcileMenuIndex(component.menu);
|
|
922
927
|
component.menu.makeSelection();
|
|
923
928
|
|
|
@@ -926,14 +931,8 @@ const comboboxKeyboardStrategy = {
|
|
|
926
931
|
}
|
|
927
932
|
|
|
928
933
|
evt.preventDefault();
|
|
929
|
-
evt.stopPropagation();
|
|
930
934
|
} else {
|
|
931
|
-
// Prevent the keypress from bubbling to parent containers (e.g., forms)
|
|
932
|
-
// which could interpret Enter as a submit or trigger other unintended behavior.
|
|
933
|
-
// This is safe because showBib() opens the dialog programmatically,
|
|
934
|
-
// not via event propagation.
|
|
935
935
|
evt.preventDefault();
|
|
936
|
-
evt.stopPropagation();
|
|
937
936
|
component.showBib();
|
|
938
937
|
}
|
|
939
938
|
},
|
|
@@ -978,7 +977,7 @@ const comboboxKeyboardStrategy = {
|
|
|
978
977
|
component.setClearBtnFocus();
|
|
979
978
|
}
|
|
980
979
|
}
|
|
981
|
-
}
|
|
980
|
+
}
|
|
982
981
|
};
|
|
983
982
|
|
|
984
983
|
/**
|
|
@@ -4873,7 +4872,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$3 {
|
|
|
4873
4872
|
}
|
|
4874
4873
|
};
|
|
4875
4874
|
|
|
4876
|
-
var formkitVersion$2 = '
|
|
4875
|
+
var formkitVersion$2 = '202607011722';
|
|
4877
4876
|
|
|
4878
4877
|
let AuroElement$2 = class AuroElement extends i$3 {
|
|
4879
4878
|
static get properties() {
|
|
@@ -5625,7 +5624,19 @@ class AuroDropdown extends AuroElement$2 {
|
|
|
5625
5624
|
if (!this.isPopoverVisible && this.hasFocus && eventType === "keydown") {
|
|
5626
5625
|
this.trigger.focus();
|
|
5627
5626
|
}
|
|
5628
|
-
|
|
5627
|
+
|
|
5628
|
+
|
|
5629
|
+
if (!this.isPopoverVisible) {
|
|
5630
|
+
// wait til the bib gets fully closed and rendered
|
|
5631
|
+
setTimeout(() => {
|
|
5632
|
+
// 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.
|
|
5633
|
+
if (!this.isPopoverVisible || !this.bib.matches(':focus-within')) {
|
|
5634
|
+
return;
|
|
5635
|
+
}
|
|
5636
|
+
// Move focus out of bib into trigger.
|
|
5637
|
+
this.trigger.focus();
|
|
5638
|
+
});
|
|
5639
|
+
} }
|
|
5629
5640
|
|
|
5630
5641
|
firstUpdated() {
|
|
5631
5642
|
// Configure the floater to, this will generate the ID for the bib
|
|
@@ -10557,11 +10568,17 @@ class AuroFormValidation {
|
|
|
10557
10568
|
return;
|
|
10558
10569
|
}
|
|
10559
10570
|
|
|
10560
|
-
// Validate that the date passed was the correct format and is a valid date
|
|
10571
|
+
// Validate that the date passed was the correct format and is a valid date.
|
|
10572
|
+
// For partial date formats, valueObject is never populated; validate them directly.
|
|
10561
10573
|
if (elem.value && !elem.valueObject) {
|
|
10562
|
-
elem.
|
|
10563
|
-
|
|
10564
|
-
|
|
10574
|
+
const isPartialDateFormat = elem.util && !elem.util.isFullDateFormat(elem.type, elem.format);
|
|
10575
|
+
const isValidPartial = isPartialDateFormat && elem.util.isValidPartialDate(elem.value, elem.format);
|
|
10576
|
+
|
|
10577
|
+
if (!isValidPartial) {
|
|
10578
|
+
elem.validity = 'patternMismatch';
|
|
10579
|
+
elem.errorMessage = elem.setCustomValidityPatternMismatch || elem.setCustomValidity || 'Invalid Date Format Entered';
|
|
10580
|
+
return;
|
|
10581
|
+
}
|
|
10565
10582
|
}
|
|
10566
10583
|
|
|
10567
10584
|
// Perform the rest of the validation
|
|
@@ -10655,15 +10672,17 @@ class AuroFormValidation {
|
|
|
10655
10672
|
);
|
|
10656
10673
|
}
|
|
10657
10674
|
|
|
10658
|
-
|
|
10659
|
-
|
|
10660
|
-
|
|
10675
|
+
const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
|
|
10676
|
+
|
|
10677
|
+
// If there is a second input in the elem and that value is undefined or an empty string set hasValue to false.
|
|
10678
|
+
// Skip for combobox: its second auro-input is the fullscreen-bib mirror of the same value, not an independent
|
|
10679
|
+
// field (datepicker is the intended consumer — start/end are independently required).
|
|
10680
|
+
if (this.auroInputElements?.length === 2 && !isCombobox) {
|
|
10681
|
+
if (!this.auroInputElements[1].value || this.auroInputElements[1].value.length === 0) {
|
|
10661
10682
|
hasValue = false;
|
|
10662
10683
|
}
|
|
10663
10684
|
}
|
|
10664
10685
|
|
|
10665
|
-
const isCombobox = this.runtimeUtils.elementMatch(elem, 'auro-combobox');
|
|
10666
|
-
|
|
10667
10686
|
if (isCombobox) {
|
|
10668
10687
|
|
|
10669
10688
|
if (!elem.persistInput || elem.behavior === "filter") {
|
|
@@ -16563,6 +16582,42 @@ class AuroInputUtilities {
|
|
|
16563
16582
|
return type === 'date' && normalizedFormat.includes('yy') && normalizedFormat.includes('mm') && normalizedFormat.includes('dd');
|
|
16564
16583
|
}
|
|
16565
16584
|
|
|
16585
|
+
/**
|
|
16586
|
+
* Validates a value against a partial date format (one that lacks yy/mm/dd all three).
|
|
16587
|
+
* Single-component formats are checked as integer ranges; multi-component formats use
|
|
16588
|
+
* a date-fns parse + round-trip to confirm both validity and exact formatting.
|
|
16589
|
+
* @param {string} value - The user-facing display value.
|
|
16590
|
+
* @param {string} format - The partial date format string (e.g. "mm/yyyy", "yyyy", "dd").
|
|
16591
|
+
* @returns {boolean}
|
|
16592
|
+
*/
|
|
16593
|
+
isValidPartialDate(value, format$1) {
|
|
16594
|
+
if (!value || !format$1) {
|
|
16595
|
+
return false;
|
|
16596
|
+
}
|
|
16597
|
+
const normalizedFormat = format$1.toLowerCase();
|
|
16598
|
+
|
|
16599
|
+
if (normalizedFormat === 'dd') {
|
|
16600
|
+
const num = Number(value);
|
|
16601
|
+
return (/^\d{2}$/u).test(value) && num >= 1 && num <= 31;
|
|
16602
|
+
}
|
|
16603
|
+
if (normalizedFormat === 'yy') {
|
|
16604
|
+
const num = Number(value);
|
|
16605
|
+
return (/^\d{2}$/u).test(value) && num >= 0 && num <= 99;
|
|
16606
|
+
}
|
|
16607
|
+
if (normalizedFormat === 'yyyy') {
|
|
16608
|
+
const num = Number(value);
|
|
16609
|
+
return (/^\d{4}$/u).test(value) && num >= 1 && num <= 9999;
|
|
16610
|
+
}
|
|
16611
|
+
|
|
16612
|
+
const dateFnsMask = this.toDateFnsMask(normalizedFormat);
|
|
16613
|
+
// Use the 1st of the current month as the reference so that formats
|
|
16614
|
+
// omitting a day (e.g. MM/yyyy) never roll over on days 29–31.
|
|
16615
|
+
const referenceDate = new Date();
|
|
16616
|
+
referenceDate.setDate(1);
|
|
16617
|
+
const parsed = parse(value, dateFnsMask, referenceDate);
|
|
16618
|
+
return isValid(parsed) && format(parsed, dateFnsMask) === value;
|
|
16619
|
+
}
|
|
16620
|
+
|
|
16566
16621
|
/**
|
|
16567
16622
|
* Converts a display string to its model value.
|
|
16568
16623
|
* For full date formats, converts the display string to an ISO date string.
|
|
@@ -16610,6 +16665,7 @@ class AuroInputUtilities {
|
|
|
16610
16665
|
const maskOptions = this.getMaskOptions('date', normalizedFormat);
|
|
16611
16666
|
|
|
16612
16667
|
if (!(valueObject instanceof Date) || Number.isNaN(valueObject.getTime()) || !maskOptions || typeof maskOptions.format !== 'function') {
|
|
16668
|
+
console.debug('Invalid date object or mask options for formatting', { valueObject, maskOptions }); // eslint-disable-line no-console
|
|
16613
16669
|
return undefined;
|
|
16614
16670
|
}
|
|
16615
16671
|
|
|
@@ -16767,6 +16823,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
16767
16823
|
// so a parent (datepicker/combobox) calling `validate()` synchronously
|
|
16768
16824
|
// during its own update cycle sees a populated util instance.
|
|
16769
16825
|
this.activeLabel = false;
|
|
16826
|
+
|
|
16770
16827
|
/** @private */
|
|
16771
16828
|
this.allowedInputTypes = [
|
|
16772
16829
|
"text",
|
|
@@ -16777,6 +16834,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
16777
16834
|
"tel"
|
|
16778
16835
|
];
|
|
16779
16836
|
this.appearance = "default";
|
|
16837
|
+
|
|
16780
16838
|
/** @private */
|
|
16781
16839
|
this.dateFormatMap = {
|
|
16782
16840
|
'mm/dd/yyyy': 'dateMMDDYYYY',
|
|
@@ -16795,23 +16853,24 @@ class BaseInput extends AuroElement$1 {
|
|
|
16795
16853
|
'mm/dd': 'dateMMDD'
|
|
16796
16854
|
};
|
|
16797
16855
|
this.disabled = false;
|
|
16856
|
+
|
|
16798
16857
|
/** @private */
|
|
16799
16858
|
this.domHandler = new DomHandler();
|
|
16800
16859
|
this.dvInputOnly = false;
|
|
16801
16860
|
this.hasValue = false;
|
|
16802
16861
|
this.hideLabelVisually = false;
|
|
16803
16862
|
this.icon = false;
|
|
16863
|
+
|
|
16804
16864
|
/** @private */
|
|
16805
16865
|
this.inputIconName = undefined;
|
|
16866
|
+
|
|
16806
16867
|
/** @private */
|
|
16807
16868
|
this.label = 'Input label is undefined';
|
|
16808
16869
|
this.layout = 'classic';
|
|
16809
16870
|
this.locale = 'en-US';
|
|
16810
16871
|
this.max = undefined;
|
|
16811
|
-
this._maxObject = undefined;
|
|
16812
16872
|
this.maxLength = undefined;
|
|
16813
16873
|
this.min = undefined;
|
|
16814
|
-
this._minObject = undefined;
|
|
16815
16874
|
this.minLength = undefined;
|
|
16816
16875
|
this.noValidate = false;
|
|
16817
16876
|
this.onDark = false;
|
|
@@ -16828,23 +16887,27 @@ class BaseInput extends AuroElement$1 {
|
|
|
16828
16887
|
"email"
|
|
16829
16888
|
];
|
|
16830
16889
|
this.shape = 'classic';
|
|
16890
|
+
|
|
16831
16891
|
/** @private */
|
|
16832
16892
|
this.showPassword = false;
|
|
16833
16893
|
this.size = 'lg';
|
|
16834
16894
|
this.touched = false;
|
|
16895
|
+
|
|
16835
16896
|
/** @private */
|
|
16836
16897
|
this.uniqueId = new UniqueId().create();
|
|
16898
|
+
|
|
16837
16899
|
/** @private */
|
|
16838
16900
|
this.util = new AuroInputUtilities({
|
|
16839
16901
|
locale: this.locale,
|
|
16840
16902
|
format: this.format
|
|
16841
16903
|
});
|
|
16904
|
+
|
|
16842
16905
|
/** @private */
|
|
16843
16906
|
this.validation = new AuroFormValidation();
|
|
16907
|
+
|
|
16844
16908
|
/** @private */
|
|
16845
16909
|
this.validationCCLength = undefined;
|
|
16846
16910
|
this.value = undefined;
|
|
16847
|
-
this._valueObject = undefined;
|
|
16848
16911
|
}
|
|
16849
16912
|
|
|
16850
16913
|
// function to define props used within the scope of this component
|
|
@@ -17174,6 +17237,13 @@ class BaseInput extends AuroElement$1 {
|
|
|
17174
17237
|
type: String
|
|
17175
17238
|
},
|
|
17176
17239
|
|
|
17240
|
+
/**
|
|
17241
|
+
* Custom help text message to display when validity = `patternMismatch`.
|
|
17242
|
+
*/
|
|
17243
|
+
setCustomValidityPatternMismatch: {
|
|
17244
|
+
type: String
|
|
17245
|
+
},
|
|
17246
|
+
|
|
17177
17247
|
/**
|
|
17178
17248
|
* Custom help text message to display when validity = `rangeOverflow`.
|
|
17179
17249
|
*/
|
|
@@ -17244,7 +17314,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17244
17314
|
|
|
17245
17315
|
/**
|
|
17246
17316
|
* Populates the `type` attribute on the input.
|
|
17247
|
-
* @type {'text' | 'password' | 'email' | 'credit-card' | 'tel' | 'number'}
|
|
17317
|
+
* @type {'text' | 'password' | 'email' | 'credit-card' | 'tel' | 'number' | 'date'}
|
|
17248
17318
|
* @default 'text'
|
|
17249
17319
|
*/
|
|
17250
17320
|
type: {
|
|
@@ -17282,7 +17352,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17282
17352
|
* @returns {Date|undefined}
|
|
17283
17353
|
*/
|
|
17284
17354
|
get valueObject() {
|
|
17285
|
-
return this.
|
|
17355
|
+
return this.value && dateFormatter.isValidDate(this.value) ? dateFormatter.stringToDateInstance(this.value) : undefined;
|
|
17286
17356
|
}
|
|
17287
17357
|
|
|
17288
17358
|
/**
|
|
@@ -17290,7 +17360,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17290
17360
|
* @returns {Date|undefined}
|
|
17291
17361
|
*/
|
|
17292
17362
|
get minObject() {
|
|
17293
|
-
return this.
|
|
17363
|
+
return this.min && dateFormatter.isValidDate(this.min) ? dateFormatter.stringToDateInstance(this.min) : undefined;
|
|
17294
17364
|
}
|
|
17295
17365
|
|
|
17296
17366
|
/**
|
|
@@ -17298,50 +17368,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17298
17368
|
* @returns {Date|undefined}
|
|
17299
17369
|
*/
|
|
17300
17370
|
get maxObject() {
|
|
17301
|
-
return this.
|
|
17302
|
-
}
|
|
17303
|
-
|
|
17304
|
-
/**
|
|
17305
|
-
* Parses a date string into a Date object when the corresponding `_*Object`
|
|
17306
|
-
* field hasn't been synced yet by `updated()`. Returns undefined when the
|
|
17307
|
-
* input type/format isn't a full date or the string is not a valid date.
|
|
17308
|
-
*
|
|
17309
|
-
* Why this exists: a parent (datepicker) can call `inputN.validate()` from
|
|
17310
|
-
* inside its own `updated()` before this input's `updated()` has run
|
|
17311
|
-
* `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
|
|
17312
|
-
* and range checks would otherwise silently no-op (flipping the result to
|
|
17313
|
-
* `valid` or `patternMismatch`).
|
|
17314
|
-
* @private
|
|
17315
|
-
* @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
|
|
17316
|
-
* @returns {Date|undefined}
|
|
17317
|
-
*/
|
|
17318
|
-
_computeDateObjectFallback(dateStr) {
|
|
17319
|
-
if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
|
|
17320
|
-
return undefined;
|
|
17321
|
-
}
|
|
17322
|
-
if (!dateFormatter.isValidDate(dateStr)) {
|
|
17323
|
-
return undefined;
|
|
17324
|
-
}
|
|
17325
|
-
return dateFormatter.stringToDateInstance(dateStr);
|
|
17326
|
-
}
|
|
17327
|
-
|
|
17328
|
-
/**
|
|
17329
|
-
* Internal setter for readonly date object properties.
|
|
17330
|
-
* @private
|
|
17331
|
-
* @param {'valueObject'|'minObject'|'maxObject'} propertyName - Public object property name.
|
|
17332
|
-
* @param {Date|undefined} propertyValue - Value to assign.
|
|
17333
|
-
* @returns {void}
|
|
17334
|
-
*/
|
|
17335
|
-
setDateObjectProperty(propertyName, propertyValue) {
|
|
17336
|
-
const internalPropertyName = `_${propertyName}`;
|
|
17337
|
-
const previousValue = this[internalPropertyName];
|
|
17338
|
-
|
|
17339
|
-
if (previousValue === propertyValue) {
|
|
17340
|
-
return;
|
|
17341
|
-
}
|
|
17342
|
-
|
|
17343
|
-
this[internalPropertyName] = propertyValue;
|
|
17344
|
-
this.requestUpdate(propertyName, previousValue);
|
|
17371
|
+
return this.max && dateFormatter.isValidDate(this.max) ? dateFormatter.stringToDateInstance(this.max) : undefined;
|
|
17345
17372
|
}
|
|
17346
17373
|
|
|
17347
17374
|
connectedCallback() {
|
|
@@ -17370,7 +17397,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17370
17397
|
format: this.format
|
|
17371
17398
|
});
|
|
17372
17399
|
this.configureDataForType();
|
|
17373
|
-
this.syncDateValues();
|
|
17374
17400
|
}
|
|
17375
17401
|
|
|
17376
17402
|
disconnectedCallback() {
|
|
@@ -17409,7 +17435,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17409
17435
|
this.setCustomHelpTextMessage();
|
|
17410
17436
|
this.configureAutoFormatting();
|
|
17411
17437
|
this.configureDataForType();
|
|
17412
|
-
this.syncDateValues();
|
|
17413
17438
|
}
|
|
17414
17439
|
|
|
17415
17440
|
/**
|
|
@@ -17553,8 +17578,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17553
17578
|
this.configureDataForType();
|
|
17554
17579
|
}
|
|
17555
17580
|
|
|
17556
|
-
this.syncDateValues(changedProperties);
|
|
17557
|
-
|
|
17558
17581
|
if (changedProperties.has('value')) {
|
|
17559
17582
|
if (this.value && this.value.length > 0) {
|
|
17560
17583
|
this.hasValue = true;
|
|
@@ -17576,14 +17599,14 @@ class BaseInput extends AuroElement$1 {
|
|
|
17576
17599
|
|
|
17577
17600
|
if (formattedValue !== this.inputElement.value) {
|
|
17578
17601
|
this.skipNextProgrammaticInputEvent = true;
|
|
17579
|
-
if (this.maskInstance && this.type
|
|
17602
|
+
if (this.maskInstance && this.type !== 'date') {
|
|
17580
17603
|
// Route through the mask so its _value and el.value stay in lock-step
|
|
17581
17604
|
// (set value calls updateControl which writes el.value = displayValue).
|
|
17582
17605
|
// Writing el.value directly leaves the mask thinking displayValue is
|
|
17583
|
-
// stale; _saveSelection on the next focus/click then warns.
|
|
17584
|
-
//
|
|
17585
|
-
//
|
|
17586
|
-
//
|
|
17606
|
+
// stale; _saveSelection on the next focus/click then warns. Date is
|
|
17607
|
+
// excluded because its formattedValue can be raw ISO when the calendar
|
|
17608
|
+
// is invalid, and re-masking through mm/dd/yyyy would truncate it and
|
|
17609
|
+
// flip validity from patternMismatch to tooShort.
|
|
17587
17610
|
this.maskInstance.value = formattedValue || '';
|
|
17588
17611
|
} else if (formattedValue) {
|
|
17589
17612
|
this.inputElement.value = formattedValue;
|
|
@@ -17625,120 +17648,65 @@ class BaseInput extends AuroElement$1 {
|
|
|
17625
17648
|
}));
|
|
17626
17649
|
}
|
|
17627
17650
|
|
|
17628
|
-
|
|
17629
|
-
/**
|
|
17630
|
-
* Synchronizes the ISO string values and Date object representations for date-related properties.
|
|
17631
|
-
* This keeps the model and display values aligned when either side changes.
|
|
17632
|
-
*
|
|
17633
|
-
* When a full date format is in use, this method updates `value`, `min`, and `max` from their corresponding
|
|
17634
|
-
* Date objects or vice versa, based on which properties have changed. It only runs when the current configuration
|
|
17635
|
-
* represents a full year/month/day date format.
|
|
17636
|
-
*
|
|
17637
|
-
* @param {Map<string, unknown>|undefined} [changedProperties=undefined] - Optional map of changed properties used to limit which values are synchronized.
|
|
17638
|
-
* @returns {void}
|
|
17639
|
-
* @private
|
|
17640
|
-
*/
|
|
17641
|
-
syncDateValues(changedProperties = undefined) {
|
|
17642
|
-
if (!this.util.isFullDateFormat(this.type, this.format)) {
|
|
17643
|
-
return;
|
|
17644
|
-
}
|
|
17645
|
-
|
|
17646
|
-
this.syncSingleDateValue(changedProperties, 'valueObject', 'value');
|
|
17647
|
-
this.syncSingleDateValue(changedProperties, 'minObject', 'min');
|
|
17648
|
-
this.syncSingleDateValue(changedProperties, 'maxObject', 'max');
|
|
17649
|
-
}
|
|
17650
|
-
|
|
17651
|
-
/**
|
|
17652
|
-
* Synchronizes one date object/string property pair.
|
|
17653
|
-
* @private
|
|
17654
|
-
* @param {Map<string, unknown>|undefined} changedProperties - Map of changed properties from Lit.
|
|
17655
|
-
* @param {string} objectProperty - Date object property name.
|
|
17656
|
-
* @param {string} valueProperty - ISO string property name.
|
|
17657
|
-
* @returns {void}
|
|
17658
|
-
*/
|
|
17659
|
-
syncSingleDateValue(changedProperties, objectProperty, valueProperty) {
|
|
17660
|
-
const objectPropertyChanged = !changedProperties || changedProperties.has(objectProperty);
|
|
17661
|
-
|
|
17662
|
-
// objectProperty wins over valueProperty when both changed
|
|
17663
|
-
if (objectPropertyChanged && this[objectProperty]) {
|
|
17664
|
-
this[valueProperty] = dateFormatter.toISOFormatString(this[objectProperty]);
|
|
17665
|
-
return;
|
|
17666
|
-
}
|
|
17667
|
-
|
|
17668
|
-
const valuePropertyChanged = !changedProperties || changedProperties.has(valueProperty);
|
|
17669
|
-
if (!valuePropertyChanged) {
|
|
17670
|
-
return;
|
|
17671
|
-
}
|
|
17672
|
-
|
|
17673
|
-
// 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)
|
|
17674
|
-
if (
|
|
17675
|
-
changedProperties &&
|
|
17676
|
-
valueProperty === 'value' &&
|
|
17677
|
-
changedProperties.get('value') === undefined &&
|
|
17678
|
-
this[objectProperty] instanceof Date &&
|
|
17679
|
-
this[valueProperty] === dateFormatter.toISOFormatString(this[objectProperty])
|
|
17680
|
-
) {
|
|
17681
|
-
return;
|
|
17682
|
-
}
|
|
17683
|
-
|
|
17684
|
-
if (dateFormatter.isValidDate(this[valueProperty])) {
|
|
17685
|
-
this.setDateObjectProperty(objectProperty, dateFormatter.stringToDateInstance(this[valueProperty]));
|
|
17686
|
-
} else {
|
|
17687
|
-
this.setDateObjectProperty(objectProperty, undefined);
|
|
17688
|
-
}
|
|
17689
|
-
}
|
|
17690
|
-
|
|
17691
17651
|
/**
|
|
17692
17652
|
* Sets up IMasks and logic based on auto-formatting requirements.
|
|
17693
17653
|
* @private
|
|
17694
17654
|
* @returns {void}
|
|
17695
17655
|
*/
|
|
17696
17656
|
configureAutoFormatting() {
|
|
17697
|
-
//
|
|
17698
|
-
//
|
|
17699
|
-
//
|
|
17700
|
-
//
|
|
17657
|
+
// _configuringMask gates two things: external re-entry into this method
|
|
17658
|
+
// while setup is mid-flight (from property changes that call back here),
|
|
17659
|
+
// and the accept/complete listeners below — both need to ignore the mask
|
|
17660
|
+
// events fired by our own value-restore step.
|
|
17701
17661
|
if (this._configuringMask) return;
|
|
17702
17662
|
this._configuringMask = true;
|
|
17703
17663
|
try {
|
|
17664
|
+
// Destroy any prior mask so IMask can attach fresh under the new format.
|
|
17665
|
+
// Null the reference too — if the new maskOptions.mask is falsy (e.g.
|
|
17666
|
+
// type switched from credit-card to text) the IMask() reassignment
|
|
17667
|
+
// below is skipped, and downstream writes at line ~823 would otherwise
|
|
17668
|
+
// route through the destroyed instance.
|
|
17704
17669
|
if (this.maskInstance) {
|
|
17705
17670
|
this.maskInstance.destroy();
|
|
17671
|
+
this.maskInstance = null;
|
|
17706
17672
|
}
|
|
17707
17673
|
|
|
17708
|
-
// Pass new format to util
|
|
17709
17674
|
this.util.updateFormat(this.format);
|
|
17710
17675
|
|
|
17711
17676
|
const maskOptions = this.util.getMaskOptions(this.type, this.format);
|
|
17712
17677
|
|
|
17713
17678
|
if (this.inputElement && maskOptions.mask) {
|
|
17714
|
-
|
|
17715
|
-
//
|
|
17716
|
-
//
|
|
17717
|
-
//
|
|
17718
|
-
// When the format changes (e.g. locale switch) and we have a valid ISO
|
|
17719
|
-
// model value, compute the display string for the NEW format instead of
|
|
17720
|
-
// re-using the old display string, which may be invalid in the new mask.
|
|
17679
|
+
// Capture the current display so it can be re-applied after IMask
|
|
17680
|
+
// attaches. The restore at the bottom goes through maskInstance.value
|
|
17681
|
+
// (not inputElement.value directly) so the mask's internal state and
|
|
17682
|
+
// the input's displayed text stay in lock-step.
|
|
17721
17683
|
let existingValue = this.inputElement.value;
|
|
17684
|
+
|
|
17685
|
+
// Format-change case (e.g. locale switch): existingValue is the OLD
|
|
17686
|
+
// mask's display string and may not parse under the new mask. When
|
|
17687
|
+
// we have a valid date model, rebuild the display from valueObject
|
|
17688
|
+
// (the canonical source) using the new mask's format function.
|
|
17722
17689
|
if (
|
|
17723
17690
|
this.util.isFullDateFormat(this.type, this.format) &&
|
|
17724
17691
|
this.value &&
|
|
17725
|
-
|
|
17726
|
-
this.valueObject instanceof Date &&
|
|
17727
|
-
!Number.isNaN(this.valueObject.getTime()) &&
|
|
17692
|
+
this.valueObject &&
|
|
17728
17693
|
typeof maskOptions.format === 'function'
|
|
17729
17694
|
) {
|
|
17730
17695
|
existingValue = maskOptions.format(this.valueObject);
|
|
17731
17696
|
}
|
|
17732
17697
|
|
|
17698
|
+
// Clear before IMask attaches so the constructor seeds an empty
|
|
17699
|
+
// internal value. Otherwise IMask reads the stale unmasked string
|
|
17700
|
+
// and emits a spurious 'accept' before the restore below runs.
|
|
17733
17701
|
this.skipNextProgrammaticInputEvent = true;
|
|
17734
17702
|
this.inputElement.value = '';
|
|
17735
17703
|
|
|
17736
17704
|
this.maskInstance = IMask(this.inputElement, maskOptions);
|
|
17737
17705
|
|
|
17706
|
+
// Mask fires 'accept' on every value change, including the restore
|
|
17707
|
+
// step below. Skip events fired during configureAutoFormatting so
|
|
17708
|
+
// we don't overwrite a value the parent just pushed.
|
|
17738
17709
|
this.maskInstance.on('accept', () => {
|
|
17739
|
-
// Suppress propagation during configureAutoFormatting's own value-restoration
|
|
17740
|
-
// (line below) — the mask emits 'accept' on every value-set, including ours,
|
|
17741
|
-
// and we don't want to overwrite a value the parent just pushed.
|
|
17742
17710
|
if (this._configuringMask) return;
|
|
17743
17711
|
this.value = this.util.toModelValue(this.maskInstance.value, this.format);
|
|
17744
17712
|
if (this.type === "date") {
|
|
@@ -17746,6 +17714,8 @@ class BaseInput extends AuroElement$1 {
|
|
|
17746
17714
|
}
|
|
17747
17715
|
});
|
|
17748
17716
|
|
|
17717
|
+
// Mask fires 'complete' on the restore step below for any value that
|
|
17718
|
+
// happens to be a complete match. Same setup-suppression as 'accept'.
|
|
17749
17719
|
this.maskInstance.on('complete', () => {
|
|
17750
17720
|
if (this._configuringMask) return;
|
|
17751
17721
|
this.value = this.util.toModelValue(this.maskInstance.value, this.format);
|
|
@@ -17754,7 +17724,9 @@ class BaseInput extends AuroElement$1 {
|
|
|
17754
17724
|
}
|
|
17755
17725
|
});
|
|
17756
17726
|
|
|
17757
|
-
//
|
|
17727
|
+
// Write existingValue through the mask (not the input directly) so
|
|
17728
|
+
// the mask reformats it under the new rules and keeps its internal
|
|
17729
|
+
// _value aligned with the input's displayed text.
|
|
17758
17730
|
if (existingValue) {
|
|
17759
17731
|
this.maskInstance.value = existingValue;
|
|
17760
17732
|
}
|
|
@@ -17848,9 +17820,9 @@ class BaseInput extends AuroElement$1 {
|
|
|
17848
17820
|
// the gated types currently support it, since the list is a public
|
|
17849
17821
|
// property a consumer could mutate.
|
|
17850
17822
|
if (this.setSelectionInputTypes.includes(this.type)) {
|
|
17851
|
-
let selectionStart;
|
|
17823
|
+
let selectionStart = null;
|
|
17852
17824
|
try {
|
|
17853
|
-
selectionStart = this.inputElement
|
|
17825
|
+
({ selectionStart } = this.inputElement);
|
|
17854
17826
|
} catch (error) { // eslint-disable-line no-unused-vars
|
|
17855
17827
|
return;
|
|
17856
17828
|
}
|
|
@@ -17954,7 +17926,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17954
17926
|
*/
|
|
17955
17927
|
reset() {
|
|
17956
17928
|
this.value = undefined;
|
|
17957
|
-
this.setDateObjectProperty('valueObject', undefined);
|
|
17958
17929
|
this.validation.reset(this);
|
|
17959
17930
|
}
|
|
17960
17931
|
|
|
@@ -17963,7 +17934,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17963
17934
|
*/
|
|
17964
17935
|
clear() {
|
|
17965
17936
|
this.value = undefined;
|
|
17966
|
-
this.setDateObjectProperty('valueObject', undefined);
|
|
17967
17937
|
}
|
|
17968
17938
|
|
|
17969
17939
|
/**
|
|
@@ -18492,7 +18462,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$3 {
|
|
|
18492
18462
|
}
|
|
18493
18463
|
};
|
|
18494
18464
|
|
|
18495
|
-
var formkitVersion$1 = '
|
|
18465
|
+
var formkitVersion$1 = '202607011722';
|
|
18496
18466
|
|
|
18497
18467
|
// Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
|
|
18498
18468
|
// See LICENSE in the project root for license information.
|
|
@@ -19618,7 +19588,7 @@ class AuroBibtemplate extends i$3 {
|
|
|
19618
19588
|
}
|
|
19619
19589
|
}
|
|
19620
19590
|
|
|
19621
|
-
var formkitVersion = '
|
|
19591
|
+
var formkitVersion = '202607011722';
|
|
19622
19592
|
|
|
19623
19593
|
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}`;
|
|
19624
19594
|
|
|
@@ -19986,12 +19956,23 @@ function getOptionLabel(option) {
|
|
|
19986
19956
|
if (!option) {
|
|
19987
19957
|
return '';
|
|
19988
19958
|
}
|
|
19989
|
-
|
|
19990
|
-
|
|
19991
|
-
if (
|
|
19992
|
-
|
|
19959
|
+
|
|
19960
|
+
// Consumer-provided override: short-circuit the DOM walk entirely.
|
|
19961
|
+
if (option.dataset && option.dataset.label) {
|
|
19962
|
+
return option.dataset.label;
|
|
19963
|
+
}
|
|
19964
|
+
|
|
19965
|
+
// Walk direct children — the `slot` attribute only applies to direct children
|
|
19966
|
+
// of the slot host, so a shallow filter is sufficient. Avoids the cloneNode +
|
|
19967
|
+
// querySelector allocation that ran on every keystroke via syncValuesAndStates.
|
|
19968
|
+
let text = '';
|
|
19969
|
+
for (const node of option.childNodes) {
|
|
19970
|
+
const isDisplayValueSlot = node.nodeType === Node.ELEMENT_NODE && node.getAttribute('slot') === 'displayValue';
|
|
19971
|
+
if (!isDisplayValueSlot) {
|
|
19972
|
+
text += node.textContent || '';
|
|
19973
|
+
}
|
|
19993
19974
|
}
|
|
19994
|
-
return
|
|
19975
|
+
return text.replace(/\s+/gu, ' ').trim();
|
|
19995
19976
|
}
|
|
19996
19977
|
|
|
19997
19978
|
// See https://git.io/JJ6SJ for "How to document your components using JSDoc"
|
|
@@ -20066,8 +20047,6 @@ class AuroCombobox extends AuroElement {
|
|
|
20066
20047
|
this.availableOptions = [];
|
|
20067
20048
|
this.dropdownId = undefined;
|
|
20068
20049
|
this.dropdownOpen = false;
|
|
20069
|
-
this.triggerExpandedState = false;
|
|
20070
|
-
this._expandedTimeout = null;
|
|
20071
20050
|
this._inFullscreenTransition = false;
|
|
20072
20051
|
this.errorMessage = null;
|
|
20073
20052
|
this.isHiddenWhileLoading = false;
|
|
@@ -20078,6 +20057,30 @@ class AuroCombobox extends AuroElement {
|
|
|
20078
20057
|
this.touched = false;
|
|
20079
20058
|
this.validation = new AuroFormValidation$1();
|
|
20080
20059
|
this.validity = undefined;
|
|
20060
|
+
this._userTyped = false;
|
|
20061
|
+
// Tracks every setTimeout scheduled via _scheduleTimer so
|
|
20062
|
+
// disconnectedCallback can cancel them. Without this, a detached
|
|
20063
|
+
// combobox's pending timers still fire — most are no-ops, but
|
|
20064
|
+
// configureMenu's racing-condition retry would otherwise loop.
|
|
20065
|
+
this._pendingTimers = new Set();
|
|
20066
|
+
}
|
|
20067
|
+
|
|
20068
|
+
/**
|
|
20069
|
+
* setTimeout wrapper that records the timer id so disconnectedCallback
|
|
20070
|
+
* can cancel any outstanding callbacks. The id is removed from the set
|
|
20071
|
+
* once the callback fires so the set doesn't grow unbounded.
|
|
20072
|
+
* @param {Function} fn - Callback to run.
|
|
20073
|
+
* @param {number} ms - Delay in milliseconds.
|
|
20074
|
+
* @returns {number} The timer id.
|
|
20075
|
+
* @private
|
|
20076
|
+
*/
|
|
20077
|
+
_scheduleTimer(fn, ms) {
|
|
20078
|
+
const id = setTimeout(() => {
|
|
20079
|
+
this._pendingTimers.delete(id);
|
|
20080
|
+
fn();
|
|
20081
|
+
}, ms);
|
|
20082
|
+
this._pendingTimers.add(id);
|
|
20083
|
+
return id;
|
|
20081
20084
|
}
|
|
20082
20085
|
|
|
20083
20086
|
// This function is to define props used within the scope of this component
|
|
@@ -20439,17 +20442,6 @@ class AuroCombobox extends AuroElement {
|
|
|
20439
20442
|
attribute: false
|
|
20440
20443
|
},
|
|
20441
20444
|
|
|
20442
|
-
/**
|
|
20443
|
-
* Deferred aria-expanded state for the trigger input.
|
|
20444
|
-
* Delays the "true" transition so VoiceOver finishes its character echo
|
|
20445
|
-
* before announcing "expanded".
|
|
20446
|
-
* @private
|
|
20447
|
-
*/
|
|
20448
|
-
triggerExpandedState: {
|
|
20449
|
-
type: Boolean,
|
|
20450
|
-
reflect: false,
|
|
20451
|
-
attribute: false
|
|
20452
|
-
},
|
|
20453
20445
|
};
|
|
20454
20446
|
}
|
|
20455
20447
|
|
|
@@ -20472,13 +20464,6 @@ class AuroCombobox extends AuroElement {
|
|
|
20472
20464
|
return this.input.value;
|
|
20473
20465
|
}
|
|
20474
20466
|
|
|
20475
|
-
// /**
|
|
20476
|
-
// * Sets the value of the input element within the combobox.
|
|
20477
|
-
// */
|
|
20478
|
-
// set inputValue(value) {
|
|
20479
|
-
// this.input.value = value;
|
|
20480
|
-
// }
|
|
20481
|
-
|
|
20482
20467
|
/**
|
|
20483
20468
|
* Checks if the element is valid.
|
|
20484
20469
|
* @returns {boolean} - Returns true if the element is valid, false otherwise.
|
|
@@ -20605,7 +20590,7 @@ class AuroCombobox extends AuroElement {
|
|
|
20605
20590
|
if (this.menu) {
|
|
20606
20591
|
this.menu.matchWord = normalizeFilterValue(this.input.value);
|
|
20607
20592
|
}
|
|
20608
|
-
const label = getOptionLabel(this.menu.optionSelected)
|
|
20593
|
+
const label = getOptionLabel(this.menu.optionSelected);
|
|
20609
20594
|
this.updateTriggerTextDisplay(label || this.value);
|
|
20610
20595
|
}
|
|
20611
20596
|
|
|
@@ -20619,44 +20604,16 @@ class AuroCombobox extends AuroElement {
|
|
|
20619
20604
|
// in suggestion mode, do not override input value if no selection has been made and the input currently has focus
|
|
20620
20605
|
const isInputFocusedWithNoSelection = !this.menu.value && (this.input.matches(':focus-within') || (this.inputInBib && this.inputInBib.matches(':focus-within')));
|
|
20621
20606
|
|
|
20622
|
-
|
|
20623
|
-
|
|
20624
|
-
|
|
20625
|
-
// syncValuesAndStates re-enters here during typing when both inputs
|
|
20626
|
-
// already match, and a no-op flag flip would make the bib branch's
|
|
20627
|
-
// bail eat legitimate user-input events.
|
|
20628
|
-
const triggerNeedsSync = this.input.value !== nextValue;
|
|
20629
|
-
const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
|
|
20630
|
-
if (triggerNeedsSync || bibNeedsSync) {
|
|
20631
|
-
this._syncingDisplayValue = true;
|
|
20632
|
-
if (triggerNeedsSync) {
|
|
20633
|
-
this.input.value = nextValue;
|
|
20634
|
-
}
|
|
20635
|
-
if (bibNeedsSync) {
|
|
20636
|
-
this.inputInBib.value = nextValue;
|
|
20637
|
-
}
|
|
20638
|
-
const pending = [];
|
|
20639
|
-
if (triggerNeedsSync) {
|
|
20640
|
-
pending.push(this.input.updateComplete);
|
|
20641
|
-
}
|
|
20642
|
-
if (bibNeedsSync) {
|
|
20643
|
-
pending.push(this.inputInBib.updateComplete);
|
|
20644
|
-
}
|
|
20645
|
-
Promise.all(pending).then(() => {
|
|
20646
|
-
// imask reasserts otherwise, and handleBlur reverts to its stale value.
|
|
20647
|
-
if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
|
|
20648
|
-
this.input.maskInstance.updateValue();
|
|
20649
|
-
}
|
|
20650
|
-
if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
|
|
20651
|
-
this.inputInBib.maskInstance.updateValue();
|
|
20652
|
-
}
|
|
20653
|
-
this._syncingDisplayValue = false;
|
|
20654
|
-
});
|
|
20655
|
-
}
|
|
20607
|
+
const suppressed = this.persistInput || (this.behavior === 'suggestion' && isInputFocusedWithNoSelection);
|
|
20608
|
+
if (!suppressed) {
|
|
20609
|
+
this.syncInputValuesAcrossTriggerAndBib(label || this.value);
|
|
20656
20610
|
}
|
|
20657
20611
|
|
|
20658
|
-
//
|
|
20659
|
-
|
|
20612
|
+
// Replace any previously appended displayValue clone in the trigger.
|
|
20613
|
+
// :not(slot) excludes the template's <slot name="displayValue"
|
|
20614
|
+
// slot="displayValue"> forwarder, which also has slot="displayValue"
|
|
20615
|
+
// and would otherwise be matched first and removed.
|
|
20616
|
+
const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]:not(slot)');
|
|
20660
20617
|
|
|
20661
20618
|
if (displayValueInTrigger) {
|
|
20662
20619
|
displayValueInTrigger.remove();
|
|
@@ -20680,6 +20637,53 @@ class AuroCombobox extends AuroElement {
|
|
|
20680
20637
|
this.requestUpdate();
|
|
20681
20638
|
}
|
|
20682
20639
|
|
|
20640
|
+
/**
|
|
20641
|
+
* Writes nextValue to the trigger input and the bib input when their current
|
|
20642
|
+
* value differs, then re-asserts imask after Lit's update flushes.
|
|
20643
|
+
* @param {string} nextValue - The value to write to both inputs.
|
|
20644
|
+
* @returns {Promise<void>} Resolves after both inputs flush and imask
|
|
20645
|
+
* re-asserts; resolves immediately when no sync is needed.
|
|
20646
|
+
* @private
|
|
20647
|
+
*/
|
|
20648
|
+
async syncInputValuesAcrossTriggerAndBib(nextValue) {
|
|
20649
|
+
// Only set the flag when there's an actual write to suppress —
|
|
20650
|
+
// syncValuesAndStates re-enters here during typing when both inputs
|
|
20651
|
+
// already match, and a no-op flag flip would make the bib branch's
|
|
20652
|
+
// bail eat legitimate user-input events.
|
|
20653
|
+
const triggerNeedsSync = this.input.value !== nextValue;
|
|
20654
|
+
const bibNeedsSync = Boolean(this.inputInBib) && this.inputInBib.value !== nextValue;
|
|
20655
|
+
if (!triggerNeedsSync && !bibNeedsSync) {
|
|
20656
|
+
return;
|
|
20657
|
+
}
|
|
20658
|
+
|
|
20659
|
+
this._syncingDisplayValue = true;
|
|
20660
|
+
|
|
20661
|
+
const pending = [];
|
|
20662
|
+
if (triggerNeedsSync) {
|
|
20663
|
+
this.input.value = nextValue;
|
|
20664
|
+
pending.push(this.input.updateComplete);
|
|
20665
|
+
}
|
|
20666
|
+
if (bibNeedsSync) {
|
|
20667
|
+
this.inputInBib.value = nextValue;
|
|
20668
|
+
pending.push(this.inputInBib.updateComplete);
|
|
20669
|
+
}
|
|
20670
|
+
// finally — not a bare .then — so that an imask throw (see commit
|
|
20671
|
+
// d1857401c: imask can throw on credit-card format change) doesn't strand
|
|
20672
|
+
// _syncingDisplayValue=true and silently swallow every subsequent input.
|
|
20673
|
+
try {
|
|
20674
|
+
await Promise.all(pending);
|
|
20675
|
+
// imask reasserts otherwise, and handleBlur reverts to its stale value.
|
|
20676
|
+
if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
|
|
20677
|
+
this.input.maskInstance.updateValue();
|
|
20678
|
+
}
|
|
20679
|
+
if (bibNeedsSync && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
|
|
20680
|
+
this.inputInBib.maskInstance.updateValue();
|
|
20681
|
+
}
|
|
20682
|
+
} finally {
|
|
20683
|
+
this._syncingDisplayValue = false;
|
|
20684
|
+
}
|
|
20685
|
+
}
|
|
20686
|
+
|
|
20683
20687
|
/**
|
|
20684
20688
|
* Processes hidden state of all menu options and determines if there are any available options not hidden.
|
|
20685
20689
|
* @private
|
|
@@ -20688,6 +20692,13 @@ class AuroCombobox extends AuroElement {
|
|
|
20688
20692
|
handleMenuOptions() {
|
|
20689
20693
|
this.generateOptionsArray();
|
|
20690
20694
|
this.availableOptions = [];
|
|
20695
|
+
// Single source of truth for the menu's filter/highlight token per call.
|
|
20696
|
+
// syncValuesAndStates re-writes the same value on exact-match keystrokes —
|
|
20697
|
+
// Lit's hasChanged makes that a no-op — and the prior duplicate set inside
|
|
20698
|
+
// handleTriggerInputValueChange is gone.
|
|
20699
|
+
if (this.menu) {
|
|
20700
|
+
this.menu.matchWord = normalizeFilterValue(this.input.value);
|
|
20701
|
+
}
|
|
20691
20702
|
this.updateFilter();
|
|
20692
20703
|
|
|
20693
20704
|
// Set aria-setsize/aria-posinset on each visible option so screen readers
|
|
@@ -20699,17 +20710,14 @@ class AuroCombobox extends AuroElement {
|
|
|
20699
20710
|
option.setAttribute('aria-posinset', index + 1);
|
|
20700
20711
|
});
|
|
20701
20712
|
|
|
20702
|
-
if (this.value && this.
|
|
20703
|
-
|
|
20704
|
-
this.setMenuValue(this.value);
|
|
20705
|
-
}
|
|
20706
|
-
|
|
20713
|
+
if (this.input.value && this.menu.options && this.menu.options.some((opt) => opt.value === this.input.value)) {
|
|
20714
|
+
this.setMenuValue(this.input.value);
|
|
20707
20715
|
this.syncValuesAndStates();
|
|
20708
20716
|
}
|
|
20709
20717
|
|
|
20710
|
-
// Re-activate when optionActive is
|
|
20711
|
-
// been reset (e.g. by clearSelection in an async update)
|
|
20712
|
-
// makeSelection() can find the correct option.
|
|
20718
|
+
// Re-activate when optionActive is not in the current scrollable viewport,
|
|
20719
|
+
// or when _index has been reset (e.g. by clearSelection in an async update)
|
|
20720
|
+
// so that makeSelection() can find the correct option.
|
|
20713
20721
|
if (!this.availableOptions.includes(this.menu.optionActive) || this.menu._index < 0) {
|
|
20714
20722
|
this.activateFirstEnabledAvailableOption();
|
|
20715
20723
|
}
|
|
@@ -20782,18 +20790,6 @@ class AuroCombobox extends AuroElement {
|
|
|
20782
20790
|
this.dropdownOpen = ev.detail.expanded;
|
|
20783
20791
|
this.updateMenuShapeSize();
|
|
20784
20792
|
|
|
20785
|
-
// Defer aria-expanded "true" so VoiceOver finishes character echo
|
|
20786
|
-
// before announcing "expanded". Set "false" immediately on close.
|
|
20787
|
-
clearTimeout(this._expandedTimeout);
|
|
20788
|
-
if (this.dropdownOpen) {
|
|
20789
|
-
const expandedDelay = 150;
|
|
20790
|
-
this._expandedTimeout = setTimeout(() => {
|
|
20791
|
-
this.triggerExpandedState = true;
|
|
20792
|
-
}, expandedDelay);
|
|
20793
|
-
} else {
|
|
20794
|
-
this.triggerExpandedState = false;
|
|
20795
|
-
}
|
|
20796
|
-
|
|
20797
20793
|
// Clear aria-activedescendant when dropdown closes
|
|
20798
20794
|
if (!this.dropdownOpen && this.input) {
|
|
20799
20795
|
this.input.setActiveDescendant(null);
|
|
@@ -20843,28 +20839,25 @@ class AuroCombobox extends AuroElement {
|
|
|
20843
20839
|
|
|
20844
20840
|
guardTouchPassthrough(this.menu);
|
|
20845
20841
|
|
|
20846
|
-
//
|
|
20847
|
-
//
|
|
20848
|
-
//
|
|
20842
|
+
// showModal() takes focus away from the trigger. Early focus once
|
|
20843
|
+
// the dialog has painted; the shared doubleRaf below is the fallback
|
|
20844
|
+
// for when the dialog needs an extra frame and also clears the
|
|
20845
|
+
// validation guard.
|
|
20849
20846
|
requestAnimationFrame(() => {
|
|
20850
20847
|
this.setInputFocus();
|
|
20851
20848
|
});
|
|
20852
|
-
|
|
20853
|
-
doubleRaf(() => {
|
|
20854
|
-
this.setInputFocus();
|
|
20855
|
-
this._inFullscreenTransition = false;
|
|
20856
|
-
});
|
|
20857
|
-
} else {
|
|
20858
|
-
// Desktop popover-open: restore the trigger caret to end-of-text.
|
|
20859
|
-
// Clicking the trigger lands on auro-input's floating <label for="…">
|
|
20860
|
-
// overlay; Chrome resets the native input's selection to [0, 0] on
|
|
20861
|
-
// label-focus before any JS runs. setInputFocus()'s non-fullscreen
|
|
20862
|
-
// branch parks the caret at end. doubleRaf lets the dropdown layout
|
|
20863
|
-
// settle first, matching the fullscreen branch timing.
|
|
20864
|
-
doubleRaf(() => {
|
|
20865
|
-
this.setInputFocus();
|
|
20866
|
-
});
|
|
20867
20849
|
}
|
|
20850
|
+
// else (desktop popover-open): Chrome resets the trigger caret to
|
|
20851
|
+
// [0, 0] when its floating <label for="…"> overlay receives focus.
|
|
20852
|
+
// The shared doubleRaf below parks the caret back at end-of-text
|
|
20853
|
+
// after the dropdown layout settles.
|
|
20854
|
+
|
|
20855
|
+
doubleRaf(() => {
|
|
20856
|
+
this.setInputFocus();
|
|
20857
|
+
if (this._inFullscreenTransition) {
|
|
20858
|
+
this._inFullscreenTransition = false;
|
|
20859
|
+
}
|
|
20860
|
+
});
|
|
20868
20861
|
}
|
|
20869
20862
|
});
|
|
20870
20863
|
|
|
@@ -20901,7 +20894,7 @@ class AuroCombobox extends AuroElement {
|
|
|
20901
20894
|
this.dropdown.trigger.inert = false;
|
|
20902
20895
|
}
|
|
20903
20896
|
|
|
20904
|
-
|
|
20897
|
+
this._scheduleTimer(() => {
|
|
20905
20898
|
this.setInputFocus();
|
|
20906
20899
|
}, 0);
|
|
20907
20900
|
});
|
|
@@ -21045,7 +21038,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21045
21038
|
|
|
21046
21039
|
// racing condition on custom-combobox with custom-menu
|
|
21047
21040
|
if (!this.menu) {
|
|
21048
|
-
|
|
21041
|
+
this._scheduleTimer(() => {
|
|
21049
21042
|
this.configureMenu();
|
|
21050
21043
|
}, 0);
|
|
21051
21044
|
return;
|
|
@@ -21075,7 +21068,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21075
21068
|
if (this.menu.optionSelected) {
|
|
21076
21069
|
const selected = this.menu.optionSelected;
|
|
21077
21070
|
|
|
21078
|
-
if (
|
|
21071
|
+
if (this.optionSelected !== selected) {
|
|
21079
21072
|
this.optionSelected = selected;
|
|
21080
21073
|
}
|
|
21081
21074
|
|
|
@@ -21088,7 +21081,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21088
21081
|
}
|
|
21089
21082
|
|
|
21090
21083
|
// Update display
|
|
21091
|
-
this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected)
|
|
21084
|
+
this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected));
|
|
21092
21085
|
|
|
21093
21086
|
// Update match word for filtering
|
|
21094
21087
|
const trimmedInput = normalizeFilterValue(this.input.value);
|
|
@@ -21100,17 +21093,24 @@ class AuroCombobox extends AuroElement {
|
|
|
21100
21093
|
// Hide dropdown on selection (except during slot changes)
|
|
21101
21094
|
if (evt.detail && evt.detail.source !== 'slotchange') {
|
|
21102
21095
|
// do not close while typing in suggestion mode with no value selected, to allow freeform input
|
|
21103
|
-
|
|
21104
|
-
|
|
21096
|
+
this.hideBib();
|
|
21097
|
+
|
|
21098
|
+
// Move focus to the clear button when the user makes a selection.
|
|
21099
|
+
if (!isEcho && this.menu.value !== undefined) {
|
|
21100
|
+
this.setClearBtnFocus();
|
|
21105
21101
|
}
|
|
21106
21102
|
|
|
21107
21103
|
// Announce the selection after the dropdown closes so it isn't
|
|
21108
21104
|
// overridden by VoiceOver's "collapsed" announcement from aria-expanded.
|
|
21105
|
+
// Skip when there's no selected value (e.g. menu.clearSelection() from
|
|
21106
|
+
// the unmatched-value path), otherwise VoiceOver reads "undefined".
|
|
21109
21107
|
const selectedValue = this.menu.value;
|
|
21110
|
-
|
|
21111
|
-
|
|
21112
|
-
|
|
21113
|
-
|
|
21108
|
+
if (selectedValue) {
|
|
21109
|
+
const announcementDelay = 300;
|
|
21110
|
+
this._scheduleTimer(() => {
|
|
21111
|
+
announceToScreenReader(this._getAnnouncementRoot(), `${selectedValue}, selected`);
|
|
21112
|
+
}, announcementDelay);
|
|
21113
|
+
}
|
|
21114
21114
|
}
|
|
21115
21115
|
|
|
21116
21116
|
// Programmatic value syncs leave availableOptions stale because
|
|
@@ -21119,22 +21119,6 @@ class AuroCombobox extends AuroElement {
|
|
|
21119
21119
|
// only — fresh user selections take the existing hideBib path.
|
|
21120
21120
|
if (isEcho && this.menu.optionSelected) {
|
|
21121
21121
|
this._programmaticFilterRefresh = true;
|
|
21122
|
-
this.handleMenuOptions();
|
|
21123
|
-
setTimeout(() => {
|
|
21124
|
-
this._programmaticFilterRefresh = false;
|
|
21125
|
-
}, 0);
|
|
21126
|
-
}
|
|
21127
|
-
|
|
21128
|
-
// base-input skips auto-validate when focus is inside its own shadow,
|
|
21129
|
-
// which it is after setTriggerInputFocus — re-run so prior tooShort
|
|
21130
|
-
// clears. processCreditCard reasserts errorMessage on every render.
|
|
21131
|
-
if (!isEcho && this.menu.optionSelected && this.input.validate) {
|
|
21132
|
-
this.input.updateComplete.then(() => {
|
|
21133
|
-
this.input.validate(true);
|
|
21134
|
-
if (this.input.validity === 'valid') {
|
|
21135
|
-
this.input.errorMessage = '';
|
|
21136
|
-
}
|
|
21137
|
-
});
|
|
21138
21122
|
}
|
|
21139
21123
|
});
|
|
21140
21124
|
|
|
@@ -21147,6 +21131,14 @@ class AuroCombobox extends AuroElement {
|
|
|
21147
21131
|
// stale option. Safe from re-entrancy because any resulting
|
|
21148
21132
|
// input.value changes dispatch isProgrammatic events.
|
|
21149
21133
|
this.menu.addEventListener('auroMenu-selectValueFailure', () => {
|
|
21134
|
+
// Announce the rejection BEFORE we clear `this.value` so the live
|
|
21135
|
+
// region carries the attempted value — without this the bib closes
|
|
21136
|
+
// silently and screen-reader users get no signal that their request
|
|
21137
|
+
// (e.g. a direct setMenuValue() call) was dropped.
|
|
21138
|
+
const attemptedValue = this.value;
|
|
21139
|
+
if (attemptedValue) {
|
|
21140
|
+
announceToScreenReader(this._getAnnouncementRoot(), `No matching option for ${attemptedValue}`);
|
|
21141
|
+
}
|
|
21150
21142
|
this.value = undefined;
|
|
21151
21143
|
this.optionSelected = undefined;
|
|
21152
21144
|
});
|
|
@@ -21158,10 +21150,13 @@ class AuroCombobox extends AuroElement {
|
|
|
21158
21150
|
this.input.setActiveDescendant(this.optionActive);
|
|
21159
21151
|
}
|
|
21160
21152
|
|
|
21161
|
-
//
|
|
21162
|
-
//
|
|
21163
|
-
//
|
|
21164
|
-
|
|
21153
|
+
// In fullscreen mode the menu sits inside a nested <dialog> shadow root,
|
|
21154
|
+
// and aria-activedescendant references across that boundary are lost —
|
|
21155
|
+
// VoiceOver/NVDA don't read the active option natively, so we mirror it
|
|
21156
|
+
// into the polite live region. In popover mode aria-activedescendant on
|
|
21157
|
+
// the trigger input is read natively; double-announcing would flood the
|
|
21158
|
+
// queue on arrow-key repeat.
|
|
21159
|
+
if (this.optionActive && this.dropdown.isBibFullscreen) {
|
|
21165
21160
|
const optionText = this.optionActive.textContent.trim();
|
|
21166
21161
|
const selectedState = this.optionActive.hasAttribute('selected') ? ', selected' : ', not selected';
|
|
21167
21162
|
const optionIndex = this.availableOptions.indexOf(this.optionActive) + 1;
|
|
@@ -21194,7 +21189,12 @@ class AuroCombobox extends AuroElement {
|
|
|
21194
21189
|
* Validate every time we remove focus from the combo box.
|
|
21195
21190
|
*/
|
|
21196
21191
|
this.addEventListener('focusout', () => {
|
|
21197
|
-
|
|
21192
|
+
// Skip while the dropdown is open — focus transits out briefly on
|
|
21193
|
+
// mousedown of a menu option (popover top-layer breaks :focus-within),
|
|
21194
|
+
// and validating against the pre-selection value flashes a stale error
|
|
21195
|
+
// between mousedown and mouseup. The next focusout fires after the
|
|
21196
|
+
// dropdown closes and validates against the post-selection value.
|
|
21197
|
+
if (!this.componentHasFocus && !this._inFullscreenTransition && !this.dropdownOpen) {
|
|
21198
21198
|
this.validate();
|
|
21199
21199
|
}
|
|
21200
21200
|
});
|
|
@@ -21257,49 +21257,40 @@ class AuroCombobox extends AuroElement {
|
|
|
21257
21257
|
if (this._syncingDisplayValue) {
|
|
21258
21258
|
return;
|
|
21259
21259
|
}
|
|
21260
|
-
this._syncingBibValue = true;
|
|
21261
|
-
this.input.value = this.inputInBib.value;
|
|
21262
|
-
this.input.updateComplete.then(() => {
|
|
21263
|
-
this._syncingBibValue = false;
|
|
21264
|
-
});
|
|
21265
21260
|
|
|
21266
|
-
//
|
|
21267
|
-
|
|
21268
|
-
|
|
21269
|
-
|
|
21270
|
-
|
|
21271
|
-
|
|
21272
|
-
|
|
21273
|
-
|
|
21274
|
-
|
|
21261
|
+
// Filtering runs via re-entrance: writing this.input.value below
|
|
21262
|
+
// dispatches an 'input' event that the trigger's listener routes to
|
|
21263
|
+
// handleTriggerInputValueChange, which refreshes the menu filter.
|
|
21264
|
+
if (this.input.value !== this.inputInBib.value) {
|
|
21265
|
+
this._syncingBibValue = true;
|
|
21266
|
+
this.input.value = this.inputInBib.value;
|
|
21267
|
+
this.input.updateComplete.then(() => {
|
|
21268
|
+
this._syncingBibValue = false;
|
|
21269
|
+
});
|
|
21275
21270
|
}
|
|
21276
21271
|
|
|
21277
|
-
this.handleMenuOptions();
|
|
21278
21272
|
this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
|
|
21279
|
-
|
|
21273
|
+
} else if (event.target === this.input) {
|
|
21274
|
+
|
|
21275
|
+
// Also sync the native input immediately so keystrokes arriving
|
|
21276
|
+
// before Lit's async update cycle (e.g. rapid Backspaces during a
|
|
21277
|
+
// fullscreen transition) operate on the correct content.
|
|
21278
|
+
// Skip the next programmatic input event to prevent the patched setter
|
|
21279
|
+
// from re-entering handleInputValueChange via the bib path.
|
|
21280
|
+
const bibNativeInput = this.inputInBib.inputElement;
|
|
21281
|
+
if (bibNativeInput && bibNativeInput.value !== this.input.value) {
|
|
21282
|
+
this.inputInBib.skipNextProgrammaticInputEvent = true;
|
|
21283
|
+
bibNativeInput.value = this.input.value || '';
|
|
21284
|
+
}
|
|
21280
21285
|
}
|
|
21281
21286
|
|
|
21287
|
+
this.value = this.input.value;
|
|
21288
|
+
|
|
21282
21289
|
// Ignore re-entrant input events caused by programmatic value sets.
|
|
21283
21290
|
if (this._syncingBibValue || this._syncingDisplayValue) {
|
|
21284
21291
|
return;
|
|
21285
21292
|
}
|
|
21286
21293
|
|
|
21287
|
-
this.inputInBib.value = this.input.value;
|
|
21288
|
-
|
|
21289
|
-
// Also sync the native input immediately so keystrokes arriving
|
|
21290
|
-
// before Lit's async update cycle (e.g. rapid Backspaces during a
|
|
21291
|
-
// fullscreen transition) operate on the correct content.
|
|
21292
|
-
// Skip the next programmatic input event to prevent the patched setter
|
|
21293
|
-
// from re-entering handleInputValueChange via the bib path.
|
|
21294
|
-
const bibNativeInput = this.inputInBib.inputElement;
|
|
21295
|
-
if (bibNativeInput && bibNativeInput.value !== this.input.value) {
|
|
21296
|
-
this.inputInBib.skipNextProgrammaticInputEvent = true;
|
|
21297
|
-
bibNativeInput.value = this.input.value || '';
|
|
21298
|
-
}
|
|
21299
|
-
|
|
21300
|
-
this.menu.matchWord = normalizeFilterValue(this.input.value);
|
|
21301
|
-
this.optionActive = null;
|
|
21302
|
-
|
|
21303
21294
|
if (this.behavior === 'suggestion') {
|
|
21304
21295
|
this.value = this.input.value;
|
|
21305
21296
|
}
|
|
@@ -21307,7 +21298,6 @@ class AuroCombobox extends AuroElement {
|
|
|
21307
21298
|
if (!this.input.value && !this._clearing) {
|
|
21308
21299
|
this.clear();
|
|
21309
21300
|
}
|
|
21310
|
-
this.handleMenuOptions();
|
|
21311
21301
|
|
|
21312
21302
|
// Validate only if the value was set programmatically (not during user
|
|
21313
21303
|
// interaction). In fullscreen dialog mode, componentHasFocus returns false
|
|
@@ -21317,34 +21307,44 @@ class AuroCombobox extends AuroElement {
|
|
|
21317
21307
|
this.validate();
|
|
21318
21308
|
}
|
|
21319
21309
|
|
|
21320
|
-
|
|
21321
|
-
|
|
21322
|
-
|
|
21323
|
-
|
|
21324
|
-
|
|
21325
|
-
|
|
21326
|
-
|
|
21327
|
-
|
|
21328
|
-
|
|
21310
|
+
this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
|
|
21311
|
+
}
|
|
21312
|
+
|
|
21313
|
+
/**
|
|
21314
|
+
* Handles input value changes originating from the trigger input.
|
|
21315
|
+
* Refreshes menu options and filtering, delegates to handleInputValueChange
|
|
21316
|
+
* for value synchronization, and manages fullscreen bib focus.
|
|
21317
|
+
* @private
|
|
21318
|
+
* @param {Event} event - The input event from the trigger input element.
|
|
21319
|
+
* @returns {void}
|
|
21320
|
+
*/
|
|
21321
|
+
handleTriggerInputValueChange(event) {
|
|
21322
|
+
// 'input' fires for every user-initiated value change — typing, paste,
|
|
21323
|
+
// IME composition end, dead-key composition, drag-drop. Flip _userTyped
|
|
21324
|
+
// here so updated('availableOptions') auto-opens the bib for sources
|
|
21325
|
+
// that keydown alone misses: paste fires no keydown, IME uses
|
|
21326
|
+
// key='Process', and dead keys produce multi-char keys (all bypass the
|
|
21327
|
+
// prior keydown.key.length===1 gate). Skip programmatic syncs.
|
|
21328
|
+
if (!this._syncingDisplayValue && !this._syncingBibValue && !this._programmaticFilterRefresh) {
|
|
21329
|
+
this._userTyped = true;
|
|
21329
21330
|
}
|
|
21330
21331
|
|
|
21331
|
-
|
|
21332
|
-
|
|
21333
|
-
|
|
21334
|
-
|
|
21335
|
-
|
|
21336
|
-
// dialog opens — the user then has to tap the input again to resume
|
|
21337
|
-
// typing.
|
|
21338
|
-
if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0) {
|
|
21339
|
-
if (!this.dropdown.isPopoverVisible) {
|
|
21340
|
-
this.showBib();
|
|
21341
|
-
}
|
|
21342
|
-
if (this.dropdown.isPopoverVisible) {
|
|
21343
|
-
this.setInputFocus();
|
|
21344
|
-
}
|
|
21332
|
+
this.handleMenuOptions();
|
|
21333
|
+
this.optionActive = null;
|
|
21334
|
+
|
|
21335
|
+
if (this.value === this.input.value) {
|
|
21336
|
+
return;
|
|
21345
21337
|
}
|
|
21346
21338
|
|
|
21347
|
-
this.
|
|
21339
|
+
this.handleInputValueChange(event);
|
|
21340
|
+
|
|
21341
|
+
if (this.dropdown.isBibFullscreen && this.input.value && this.input.value.length > 0 && this.dropdown.isPopoverVisible) {
|
|
21342
|
+
this.setInputFocus();
|
|
21343
|
+
}
|
|
21344
|
+
|
|
21345
|
+
if (this._programmaticFilterRefresh) {
|
|
21346
|
+
this._programmaticFilterRefresh = false;
|
|
21347
|
+
}
|
|
21348
21348
|
}
|
|
21349
21349
|
|
|
21350
21350
|
/**
|
|
@@ -21412,6 +21412,11 @@ class AuroCombobox extends AuroElement {
|
|
|
21412
21412
|
disconnectedCallback() {
|
|
21413
21413
|
super.disconnectedCallback();
|
|
21414
21414
|
this._inFullscreenTransition = false;
|
|
21415
|
+
// Cancel any outstanding timers so detached callbacks don't fire on
|
|
21416
|
+
// disposed DOM — most are no-ops, but configureMenu's racing-condition
|
|
21417
|
+
// retry would otherwise keep rescheduling itself indefinitely.
|
|
21418
|
+
this._pendingTimers.forEach((id) => clearTimeout(id));
|
|
21419
|
+
this._pendingTimers.clear();
|
|
21415
21420
|
}
|
|
21416
21421
|
|
|
21417
21422
|
firstUpdated() {
|
|
@@ -21453,7 +21458,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21453
21458
|
this.menu.value = value;
|
|
21454
21459
|
// Backup clear: if menu.value === menu.optionSelected.value already, the
|
|
21455
21460
|
// listener won't fire and the flag would swallow the next user click.
|
|
21456
|
-
|
|
21461
|
+
this._scheduleTimer(() => {
|
|
21457
21462
|
this._pendingMenuValueSync = false;
|
|
21458
21463
|
}, 0);
|
|
21459
21464
|
}
|
|
@@ -21470,15 +21475,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21470
21475
|
this.menu.value = undefined;
|
|
21471
21476
|
this.validation.reset(this);
|
|
21472
21477
|
this.touched = false;
|
|
21473
|
-
// Force validity back to the cleared state. validation.reset() calls
|
|
21474
|
-
// validate() at the end, and (post-credit-card-fix) the trigger input's
|
|
21475
|
-
// residual validity may still be copied into the combobox by the
|
|
21476
|
-
// auroInputElements loop. Explicitly clear here so reset() actually
|
|
21477
|
-
// leaves the component in a "no validity" state.
|
|
21478
21478
|
this.validity = undefined;
|
|
21479
|
-
this.errorMessage = '';
|
|
21480
|
-
this.input.validity = undefined;
|
|
21481
|
-
this.input.errorMessage = '';
|
|
21482
21479
|
}
|
|
21483
21480
|
|
|
21484
21481
|
/**
|
|
@@ -21496,6 +21493,17 @@ class AuroCombobox extends AuroElement {
|
|
|
21496
21493
|
this.optionSelected = undefined;
|
|
21497
21494
|
this.value = undefined;
|
|
21498
21495
|
|
|
21496
|
+
// Clear the appended displayValue clone in the trigger if present.
|
|
21497
|
+
// :not(slot) excludes the template's <slot name="displayValue"
|
|
21498
|
+
// slot="displayValue"> forwarder (line 1816), which also has
|
|
21499
|
+
// slot="displayValue" and would otherwise be matched first and removed,
|
|
21500
|
+
// permanently breaking the consumer-provided displayValue slot.
|
|
21501
|
+
const displayValueInTrigger = this.input.querySelector('[slot="displayValue"]:not(slot)');
|
|
21502
|
+
|
|
21503
|
+
if (displayValueInTrigger) {
|
|
21504
|
+
displayValueInTrigger.remove();
|
|
21505
|
+
}
|
|
21506
|
+
|
|
21499
21507
|
if (this.input.value) {
|
|
21500
21508
|
this.input.clear();
|
|
21501
21509
|
}
|
|
@@ -21519,81 +21527,50 @@ class AuroCombobox extends AuroElement {
|
|
|
21519
21527
|
}
|
|
21520
21528
|
|
|
21521
21529
|
updated(changedProperties) {
|
|
21522
|
-
// After the component is ready,
|
|
21530
|
+
// After the component is ready, propagate direct changes down to child components.
|
|
21523
21531
|
if (changedProperties.has('value')) {
|
|
21524
|
-
|
|
21525
|
-
|
|
21526
|
-
|
|
21527
|
-
|
|
21532
|
+
// Only flag programmatic refreshes — user-typed value changes must not
|
|
21533
|
+
// suppress the availableOptions branch's showBib(). Firefox batches
|
|
21534
|
+
// 'value' and 'availableOptions' into the same updated() call, so
|
|
21535
|
+
// setting the flag unconditionally here masks the user-typed open path.
|
|
21536
|
+
if (!this._userTyped) {
|
|
21537
|
+
this._programmaticFilterRefresh = true;
|
|
21528
21538
|
}
|
|
21529
21539
|
|
|
21530
|
-
if (this.
|
|
21531
|
-
|
|
21532
|
-
|
|
21540
|
+
if (this.input.value !== this.value) {
|
|
21541
|
+
// Clear menu.value AND menu.optionSelected together. Clearing only
|
|
21542
|
+
// menu.value leaves the previously-selected option element pinned
|
|
21543
|
+
// as menu.optionSelected; a later auroMenu-selectedOption event
|
|
21544
|
+
// would then write its stale .value back into combobox.value
|
|
21545
|
+
// (e.g. Tab-after-Backspace re-selecting the prior option).
|
|
21546
|
+
if (this.menu.value || this.menu.optionSelected) {
|
|
21547
|
+
this.menu.clearSelection();
|
|
21548
|
+
}
|
|
21533
21549
|
|
|
21534
|
-
|
|
21535
|
-
|
|
21536
|
-
|
|
21537
|
-
|
|
21538
|
-
|
|
21539
|
-
|
|
21540
|
-
|
|
21541
|
-
|
|
21542
|
-
|
|
21543
|
-
|
|
21544
|
-
|
|
21545
|
-
|
|
21546
|
-
|
|
21547
|
-
|
|
21548
|
-
|
|
21549
|
-
|
|
21550
|
-
|
|
21551
|
-
|
|
21552
|
-
// matches to avoid spurious events during the normal typing path.
|
|
21553
|
-
const nextValue = this.value || '';
|
|
21554
|
-
const triggerNeedsSync = this.input.value !== nextValue;
|
|
21555
|
-
const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
|
|
21556
|
-
if (triggerNeedsSync || bibNeedsSync) {
|
|
21557
|
-
this._syncingDisplayValue = true;
|
|
21558
|
-
if (triggerNeedsSync) {
|
|
21559
|
-
this.input.value = nextValue;
|
|
21560
|
-
}
|
|
21561
|
-
if (bibNeedsSync) {
|
|
21562
|
-
this.inputInBib.value = nextValue;
|
|
21563
|
-
}
|
|
21564
|
-
const pending = [];
|
|
21565
|
-
if (triggerNeedsSync) {
|
|
21566
|
-
pending.push(this.input.updateComplete);
|
|
21567
|
-
}
|
|
21568
|
-
if (bibNeedsSync) {
|
|
21569
|
-
pending.push(this.inputInBib.updateComplete);
|
|
21570
|
-
}
|
|
21571
|
-
Promise.all(pending).then(() => {
|
|
21572
|
-
if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
|
|
21573
|
-
this.input.maskInstance.updateValue();
|
|
21574
|
-
}
|
|
21575
|
-
if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
|
|
21576
|
-
this.inputInBib.maskInstance.updateValue();
|
|
21577
|
-
}
|
|
21578
|
-
this._syncingDisplayValue = false;
|
|
21579
|
-
// handleInputValueChange bailed on the flag — refresh the filter.
|
|
21580
|
-
this._programmaticFilterRefresh = true;
|
|
21581
|
-
this.handleMenuOptions();
|
|
21582
|
-
setTimeout(() => {
|
|
21583
|
-
this._programmaticFilterRefresh = false;
|
|
21584
|
-
}, 0);
|
|
21585
|
-
});
|
|
21586
|
-
}
|
|
21550
|
+
if (!this.persistInput) {
|
|
21551
|
+
this.syncInputValuesAcrossTriggerAndBib(this.value || '');
|
|
21552
|
+
}
|
|
21553
|
+
|
|
21554
|
+
// Programmatic value with no matching option: updateFilter will close
|
|
21555
|
+
// the bib silently (see line 648 — no noMatchOption + 0 results
|
|
21556
|
+
// hides). Announce so screen-reader users hear the request was
|
|
21557
|
+
// dropped. Gated on `input.value !== this.value` so this never fires
|
|
21558
|
+
// for user typing — that path always reconciles input.value to
|
|
21559
|
+
// this.value before updated() runs.
|
|
21560
|
+
if (
|
|
21561
|
+
this.value &&
|
|
21562
|
+
this.menu &&
|
|
21563
|
+
this.menu.options &&
|
|
21564
|
+
this.menu.options.length > 0 &&
|
|
21565
|
+
!this.menu.options.some((opt) => opt.value === this.value)
|
|
21566
|
+
) {
|
|
21567
|
+
announceToScreenReader(this._getAnnouncementRoot(), `No matching option for ${this.value}`);
|
|
21587
21568
|
}
|
|
21588
21569
|
}
|
|
21570
|
+
|
|
21589
21571
|
if (!this.value) {
|
|
21590
21572
|
this.clear();
|
|
21591
21573
|
}
|
|
21592
|
-
if (this.value && !this.componentHasFocus) {
|
|
21593
|
-
// If the value got set programmatically make sure we hide the bib
|
|
21594
|
-
// when input is not taking the focus (input can be in dropdown.trigger or in bibtemplate)
|
|
21595
|
-
this.hideBib();
|
|
21596
|
-
}
|
|
21597
21574
|
|
|
21598
21575
|
// Sync the input and match word, but don't directly set menu.value again
|
|
21599
21576
|
if (this.menu) {
|
|
@@ -21606,7 +21583,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21606
21583
|
composed: true,
|
|
21607
21584
|
detail: {
|
|
21608
21585
|
optionSelected: this.menu.optionSelected,
|
|
21609
|
-
value: this.
|
|
21586
|
+
value: this.value
|
|
21610
21587
|
}
|
|
21611
21588
|
}));
|
|
21612
21589
|
|
|
@@ -21629,16 +21606,30 @@ class AuroCombobox extends AuroElement {
|
|
|
21629
21606
|
// from a programmatic filter refresh after a value swap — the user
|
|
21630
21607
|
// didn't interact, so we shouldn't auto-open the bib (especially for
|
|
21631
21608
|
// the no-match path where the existing condition unconditionally fires).
|
|
21632
|
-
if (this._programmaticFilterRefresh) {
|
|
21633
|
-
|
|
21634
|
-
|
|
21635
|
-
|
|
21609
|
+
if ((this.menu && !this._programmaticFilterRefresh)) {
|
|
21610
|
+
if (
|
|
21611
|
+
this.availableOptions.length > 0 ||
|
|
21612
|
+
this.menu.loading ||
|
|
21613
|
+
this.noMatchOption
|
|
21614
|
+
) {
|
|
21615
|
+
if (this._userTyped) {
|
|
21616
|
+
if (!this.dropdownOpen) {
|
|
21617
|
+
this.showBib();
|
|
21618
|
+
}
|
|
21619
|
+
this._userTyped = false;
|
|
21620
|
+
}
|
|
21621
|
+
}
|
|
21622
|
+
|
|
21636
21623
|
if (!this.availableOptions.includes(this.menu.optionActive)) {
|
|
21637
21624
|
this.activateFirstEnabledAvailableOption();
|
|
21638
21625
|
}
|
|
21639
|
-
} else if (this.dropdown
|
|
21626
|
+
} else if (!this.dropdown.isBibFullscreen) {
|
|
21640
21627
|
this.hideBib();
|
|
21641
21628
|
}
|
|
21629
|
+
|
|
21630
|
+
if (this._programmaticFilterRefresh) {
|
|
21631
|
+
this._programmaticFilterRefresh = false;
|
|
21632
|
+
}
|
|
21642
21633
|
}
|
|
21643
21634
|
|
|
21644
21635
|
if (changedProperties.has('error')) {
|
|
@@ -21780,10 +21771,10 @@ class AuroCombobox extends AuroElement {
|
|
|
21780
21771
|
shape="${this.shape}"
|
|
21781
21772
|
size="${this.size}">
|
|
21782
21773
|
<${this.inputTag}
|
|
21783
|
-
@input="${this.
|
|
21774
|
+
@input="${this.handleTriggerInputValueChange}"
|
|
21784
21775
|
appearance="${this.onDark ? 'inverse' : this.appearance}"
|
|
21785
21776
|
.a11yActivedescendant="${this.dropdownOpen && this.optionActive ? this.optionActive.id : undefined}"
|
|
21786
|
-
.a11yExpanded="${this.
|
|
21777
|
+
.a11yExpanded="${this.dropdownOpen}"
|
|
21787
21778
|
.a11yControls="${this.dropdownId}"
|
|
21788
21779
|
.autocomplete="${this.autocomplete}"
|
|
21789
21780
|
.inputmode="${this.inputmode}"
|
|
@@ -23388,14 +23379,10 @@ async function dynamicMenuExample() {
|
|
|
23388
23379
|
// Helper function that generates HTML for menuoptions
|
|
23389
23380
|
function generateMenuOptionHtml(menu, label, value) {
|
|
23390
23381
|
const option = document.createElement('auro-menuoption');
|
|
23391
|
-
const displayValue = document.createElement('div');
|
|
23392
|
-
displayValue.setAttribute("slot", "displayValue");
|
|
23393
|
-
displayValue.innerHTML = value;
|
|
23394
23382
|
|
|
23395
23383
|
option.value = value;
|
|
23396
23384
|
option.innerHTML = label;
|
|
23397
23385
|
menu.appendChild(option);
|
|
23398
|
-
option.appendChild(displayValue);
|
|
23399
23386
|
}
|
|
23400
23387
|
|
|
23401
23388
|
// Main javascript that runs all JS to create example
|