@aurodesignsystem-dev/auro-formkit 0.0.0-pr1497.6 → 0.0.0-pr1498.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 +2 -2
- package/components/checkbox/demo/getting-started.min.js +2 -2
- package/components/checkbox/demo/index.min.js +2 -2
- package/components/checkbox/dist/index.js +2 -2
- package/components/checkbox/dist/registered.js +2 -2
- package/components/combobox/demo/customize.md +7 -3
- package/components/combobox/demo/customize.min.js +1438 -1668
- package/components/combobox/demo/getting-started.min.js +1440 -1670
- package/components/combobox/demo/index.min.js +1440 -1670
- package/components/combobox/dist/auro-combobox.d.ts +5 -0
- package/components/combobox/dist/index.js +485 -140
- package/components/combobox/dist/registered.js +485 -140
- package/components/counter/demo/customize.min.js +20 -3
- package/components/counter/demo/index.min.js +20 -3
- package/components/counter/dist/index.js +5447 -192
- package/components/counter/dist/registered.js +5447 -192
- package/components/datepicker/demo/customize.min.js +149 -58
- package/components/datepicker/demo/index.min.js +149 -58
- package/components/datepicker/dist/index.js +149 -58
- package/components/datepicker/dist/registered.js +149 -58
- package/components/dropdown/demo/customize.min.js +18 -1
- package/components/dropdown/demo/getting-started.min.js +18 -1
- package/components/dropdown/demo/index.min.js +18 -1
- package/components/dropdown/dist/auro-dropdown.d.ts +1 -0
- package/components/dropdown/dist/index.js +18 -1
- package/components/dropdown/dist/registered.js +18 -1
- package/components/form/demo/customize.min.js +7910 -7909
- package/components/form/demo/getting-started.min.js +7910 -7909
- package/components/form/demo/index.min.js +7910 -7909
- package/components/form/demo/registerDemoDeps.min.js +7872 -7871
- package/components/input/demo/customize.min.js +131 -56
- package/components/input/demo/getting-started.min.js +131 -56
- package/components/input/demo/index.min.js +131 -56
- package/components/input/dist/base-input.d.ts +16 -0
- package/components/input/dist/index.js +131 -56
- package/components/input/dist/registered.js +131 -56
- package/components/menu/demo/api.md +41 -45
- package/components/menu/demo/customize.md +0 -28
- package/components/menu/demo/index.min.js +779 -1354
- package/components/menu/dist/auro-menu.d.ts +95 -110
- package/components/menu/dist/auro-menuoption.d.ts +32 -138
- package/components/menu/dist/index.js +753 -1308
- package/components/menu/dist/registered.js +765 -1308
- package/components/radio/demo/customize.min.js +2 -2
- package/components/radio/demo/getting-started.min.js +2 -2
- package/components/radio/demo/index.min.js +2 -2
- package/components/radio/dist/index.js +2 -2
- package/components/radio/dist/registered.js +2 -2
- package/components/select/demo/customize.md +7 -3
- package/components/select/demo/customize.min.js +1088 -1615
- package/components/select/demo/getting-started.min.js +1088 -1615
- package/components/select/demo/index.min.js +1088 -1615
- package/components/select/dist/index.js +63 -15
- package/components/select/dist/registered.js +63 -15
- package/components/select/dist/selectUtils.d.ts +12 -0
- package/custom-elements.json +379 -1025
- package/package.json +1 -1
- package/components/menu/dist/auro-menu.context.d.ts +0 -238
|
@@ -555,7 +555,7 @@ let AuroFormValidation$1 = class AuroFormValidation {
|
|
|
555
555
|
} else if (this.runtimeUtils.elementMatch(elem, 'auro-input') && elem.errorMessage === '') {
|
|
556
556
|
const input = elem.renderRoot.querySelector('input');
|
|
557
557
|
|
|
558
|
-
if (input.validationMessage.length > 0) {
|
|
558
|
+
if (input && input.validationMessage.length > 0) {
|
|
559
559
|
elem.errorMessage = input.validationMessage;
|
|
560
560
|
}
|
|
561
561
|
} else if (this.runtimeUtils.elementMatch(elem, 'auro-combobox') && elem.errorMessage === '') {
|
|
@@ -797,6 +797,21 @@ function isClearBtnFocused(ctx, clearBtn = getClearBtn(ctx)) {
|
|
|
797
797
|
return isFocused;
|
|
798
798
|
}
|
|
799
799
|
|
|
800
|
+
/**
|
|
801
|
+
* Reconcile the menu `_index` from `optionActive` so subsequent `makeSelection` calls find the correct option after async clearSelection has reset it.
|
|
802
|
+
* @param {Object} menu - The menu component.
|
|
803
|
+
*/
|
|
804
|
+
function reconcileMenuIndex(menu) {
|
|
805
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
806
|
+
if (menu._index < 0 && menu.optionActive && menu.items) {
|
|
807
|
+
const idx = menu.items.indexOf(menu.optionActive);
|
|
808
|
+
if (idx >= 0) {
|
|
809
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
810
|
+
menu._index = idx;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
800
815
|
const comboboxKeyboardStrategy = {
|
|
801
816
|
ArrowDown(component, evt, ctx) {
|
|
802
817
|
// If the clear button has focus, let the browser handle ArrowDown normally.
|
|
@@ -860,6 +875,7 @@ const comboboxKeyboardStrategy = {
|
|
|
860
875
|
// block the browser's built-in "Enter activates focused button" behavior.
|
|
861
876
|
evt.stopPropagation();
|
|
862
877
|
} else if (ctx.isExpanded && component.menu.optionActive) {
|
|
878
|
+
reconcileMenuIndex(component.menu);
|
|
863
879
|
component.menu.makeSelection();
|
|
864
880
|
|
|
865
881
|
if (ctx.isModal) {
|
|
@@ -907,6 +923,7 @@ const comboboxKeyboardStrategy = {
|
|
|
907
923
|
// When the clear button is focused, Tab events do not bubble out of
|
|
908
924
|
// its shadow DOM, so this handler only fires when the clear button
|
|
909
925
|
// is NOT focused. In that case, select the active option and close.
|
|
926
|
+
reconcileMenuIndex(component.menu);
|
|
910
927
|
component.menu.makeSelection();
|
|
911
928
|
component.hideBib();
|
|
912
929
|
|
|
@@ -4789,7 +4806,7 @@ let AuroHelpText$2 = class AuroHelpText extends LitElement {
|
|
|
4789
4806
|
}
|
|
4790
4807
|
};
|
|
4791
4808
|
|
|
4792
|
-
var formkitVersion$2 = '
|
|
4809
|
+
var formkitVersion$2 = '202606100232';
|
|
4793
4810
|
|
|
4794
4811
|
let AuroElement$2 = class AuroElement extends LitElement {
|
|
4795
4812
|
static get properties() {
|
|
@@ -5647,6 +5664,15 @@ class AuroDropdown extends AuroElement$2 {
|
|
|
5647
5664
|
this.focusTrap = undefined;
|
|
5648
5665
|
}
|
|
5649
5666
|
|
|
5667
|
+
// Restore any focus-loss suppression we installed for a prior trap state.
|
|
5668
|
+
// The desktopModal trap moves focus into the bib via RAF, which would
|
|
5669
|
+
// otherwise trip AuroFloatingUI.handleFocusLoss (a popover-top-layer
|
|
5670
|
+
// timing issue makes its :focus-within guard miss the focused button).
|
|
5671
|
+
if (this._priorNoHideOnFocusLoss !== undefined) {
|
|
5672
|
+
this.noHideOnThisFocusLoss = this._priorNoHideOnFocusLoss;
|
|
5673
|
+
this._priorNoHideOnFocusLoss = undefined;
|
|
5674
|
+
}
|
|
5675
|
+
|
|
5650
5676
|
if (this.isPopoverVisible) {
|
|
5651
5677
|
if (!this.isBibFullscreen) {
|
|
5652
5678
|
if (this.desktopModal) {
|
|
@@ -5722,6 +5748,14 @@ class AuroDropdown extends AuroElement$2 {
|
|
|
5722
5748
|
};
|
|
5723
5749
|
this.addEventListener('keydown', this._bibTabHandler);
|
|
5724
5750
|
|
|
5751
|
+
// Suppress AuroFloatingUI's auto-hide-on-focus-loss while the
|
|
5752
|
+
// desktopModal trap owns focus management. Without this, the very
|
|
5753
|
+
// first focus move into the bib (from the RAF below) triggers
|
|
5754
|
+
// handleFocusLoss → hideBib, tearing down the trap before the
|
|
5755
|
+
// user can press Tab.
|
|
5756
|
+
this._priorNoHideOnFocusLoss = this.noHideOnThisFocusLoss;
|
|
5757
|
+
this.noHideOnThisFocusLoss = true;
|
|
5758
|
+
|
|
5725
5759
|
// Move initial focus into the bib content, matching FocusTrap behavior
|
|
5726
5760
|
requestAnimationFrame(() => {
|
|
5727
5761
|
const focusables = getFocusableElements(this.bibContent);
|
|
@@ -10644,7 +10678,7 @@ class AuroFormValidation {
|
|
|
10644
10678
|
} else if (this.runtimeUtils.elementMatch(elem, 'auro-input') && elem.errorMessage === '') {
|
|
10645
10679
|
const input = elem.renderRoot.querySelector('input');
|
|
10646
10680
|
|
|
10647
|
-
if (input.validationMessage.length > 0) {
|
|
10681
|
+
if (input && input.validationMessage.length > 0) {
|
|
10648
10682
|
elem.errorMessage = input.validationMessage;
|
|
10649
10683
|
}
|
|
10650
10684
|
} else if (this.runtimeUtils.elementMatch(elem, 'auro-combobox') && elem.errorMessage === '') {
|
|
@@ -16481,7 +16515,6 @@ class AuroInputUtilities {
|
|
|
16481
16515
|
const maskOptions = this.getMaskOptions('date', normalizedFormat);
|
|
16482
16516
|
|
|
16483
16517
|
if (!(valueObject instanceof Date) || Number.isNaN(valueObject.getTime()) || !maskOptions || typeof maskOptions.format !== 'function') {
|
|
16484
|
-
console.debug('Invalid date object or mask options for formatting', { valueObject, maskOptions }); // eslint-disable-line no-console
|
|
16485
16518
|
return undefined;
|
|
16486
16519
|
}
|
|
16487
16520
|
|
|
@@ -17184,7 +17217,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17184
17217
|
* @returns {Date|undefined}
|
|
17185
17218
|
*/
|
|
17186
17219
|
get valueObject() {
|
|
17187
|
-
return this._valueObject;
|
|
17220
|
+
return this._valueObject || this._computeDateObjectFallback(this.value);
|
|
17188
17221
|
}
|
|
17189
17222
|
|
|
17190
17223
|
/**
|
|
@@ -17192,7 +17225,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17192
17225
|
* @returns {Date|undefined}
|
|
17193
17226
|
*/
|
|
17194
17227
|
get minObject() {
|
|
17195
|
-
return this._minObject;
|
|
17228
|
+
return this._minObject || this._computeDateObjectFallback(this.min);
|
|
17196
17229
|
}
|
|
17197
17230
|
|
|
17198
17231
|
/**
|
|
@@ -17200,7 +17233,31 @@ class BaseInput extends AuroElement$1 {
|
|
|
17200
17233
|
* @returns {Date|undefined}
|
|
17201
17234
|
*/
|
|
17202
17235
|
get maxObject() {
|
|
17203
|
-
return this._maxObject;
|
|
17236
|
+
return this._maxObject || this._computeDateObjectFallback(this.max);
|
|
17237
|
+
}
|
|
17238
|
+
|
|
17239
|
+
/**
|
|
17240
|
+
* Parses a date string into a Date object when the corresponding `_*Object`
|
|
17241
|
+
* field hasn't been synced yet by `updated()`. Returns undefined when the
|
|
17242
|
+
* input type/format isn't a full date or the string is not a valid date.
|
|
17243
|
+
*
|
|
17244
|
+
* Why this exists: a parent (datepicker) can call `inputN.validate()` from
|
|
17245
|
+
* inside its own `updated()` before this input's `updated()` has run
|
|
17246
|
+
* `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
|
|
17247
|
+
* and range checks would otherwise silently no-op (flipping the result to
|
|
17248
|
+
* `valid` or `patternMismatch`).
|
|
17249
|
+
* @private
|
|
17250
|
+
* @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
|
|
17251
|
+
* @returns {Date|undefined}
|
|
17252
|
+
*/
|
|
17253
|
+
_computeDateObjectFallback(dateStr) {
|
|
17254
|
+
if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
|
|
17255
|
+
return undefined;
|
|
17256
|
+
}
|
|
17257
|
+
if (!dateFormatter.isValidDate(dateStr)) {
|
|
17258
|
+
return undefined;
|
|
17259
|
+
}
|
|
17260
|
+
return dateFormatter.stringToDateInstance(dateStr);
|
|
17204
17261
|
}
|
|
17205
17262
|
|
|
17206
17263
|
/**
|
|
@@ -17225,8 +17282,30 @@ class BaseInput extends AuroElement$1 {
|
|
|
17225
17282
|
connectedCallback() {
|
|
17226
17283
|
super.connectedCallback();
|
|
17227
17284
|
|
|
17285
|
+
// Mark for query selectors when registered under a versioned tag (e.g. inside
|
|
17286
|
+
// a datepicker/combobox). Must run before parent components call validate() on
|
|
17287
|
+
// their inner inputs — the validation framework matches via tag-or-attribute,
|
|
17288
|
+
// and parents trigger validation during their own updated() cycle, which can
|
|
17289
|
+
// precede this input's firstUpdated().
|
|
17290
|
+
if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
|
|
17291
|
+
this.setAttribute('auro-input', '');
|
|
17292
|
+
}
|
|
17293
|
+
|
|
17228
17294
|
this.locale = this.domHandler.getLocale(this);
|
|
17229
17295
|
notifyOnLangChange(this);
|
|
17296
|
+
|
|
17297
|
+
// Pre-compute lengthForType and date-object fields so a parent (e.g. datepicker)
|
|
17298
|
+
// calling our validate() synchronously during its own updated() cycle sees
|
|
17299
|
+
// populated values. Without this, range validation silently no-ops because
|
|
17300
|
+
// `max.length === lengthForType` fails when lengthForType is still undefined.
|
|
17301
|
+
// Rebuild util here (constructor seeded with en-US default) so configureDataForType's
|
|
17302
|
+
// locale-derived format lookup uses the actual locale just resolved above.
|
|
17303
|
+
this.util = new AuroInputUtilities({
|
|
17304
|
+
locale: this.locale,
|
|
17305
|
+
format: this.format
|
|
17306
|
+
});
|
|
17307
|
+
this.configureDataForType();
|
|
17308
|
+
this.syncDateValues();
|
|
17230
17309
|
}
|
|
17231
17310
|
|
|
17232
17311
|
disconnectedCallback() {
|
|
@@ -17247,8 +17326,8 @@ class BaseInput extends AuroElement$1 {
|
|
|
17247
17326
|
}
|
|
17248
17327
|
|
|
17249
17328
|
// add attribute for query selectors when auro-input is registered under a custom name
|
|
17250
|
-
if (this.tagName.toLowerCase() !== 'auro-input') {
|
|
17251
|
-
this.setAttribute('auro-input',
|
|
17329
|
+
if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
|
|
17330
|
+
this.setAttribute('auro-input', '');
|
|
17252
17331
|
}
|
|
17253
17332
|
this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
|
|
17254
17333
|
|
|
@@ -17296,6 +17375,12 @@ class BaseInput extends AuroElement$1 {
|
|
|
17296
17375
|
return;
|
|
17297
17376
|
}
|
|
17298
17377
|
|
|
17378
|
+
// While configureAutoFormatting is running, imask's internal updateControl
|
|
17379
|
+
// writes el.value to align display with the masked value. A synthetic input
|
|
17380
|
+
// event from that write would re-enter handleInput → processCreditCard mid-setup
|
|
17381
|
+
// and clobber a Lit value that was just pushed by the parent combobox.
|
|
17382
|
+
if (component._configuringMask) return;
|
|
17383
|
+
|
|
17299
17384
|
// If all guard clauses are passed, dispatch the event
|
|
17300
17385
|
const inputEvent = new InputEvent('input', {
|
|
17301
17386
|
bubbles: true,
|
|
@@ -17414,7 +17499,16 @@ class BaseInput extends AuroElement$1 {
|
|
|
17414
17499
|
|
|
17415
17500
|
if (formattedValue !== this.inputElement.value) {
|
|
17416
17501
|
this.skipNextProgrammaticInputEvent = true;
|
|
17417
|
-
if (
|
|
17502
|
+
if (this.maskInstance && this.type === 'credit-card') {
|
|
17503
|
+
// Route through the mask so its _value and el.value stay in lock-step
|
|
17504
|
+
// (set value calls updateControl which writes el.value = displayValue).
|
|
17505
|
+
// Writing el.value directly leaves the mask thinking displayValue is
|
|
17506
|
+
// stale; _saveSelection on the next focus/click then warns. Scoped to
|
|
17507
|
+
// credit-card so date's own formattedValue (raw ISO when calendar-invalid)
|
|
17508
|
+
// isn't re-masked through the mm/dd/yyyy mask, which would truncate it
|
|
17509
|
+
// and flip validity from patternMismatch to tooShort.
|
|
17510
|
+
this.maskInstance.value = formattedValue || '';
|
|
17511
|
+
} else if (formattedValue) {
|
|
17418
17512
|
this.inputElement.value = formattedValue;
|
|
17419
17513
|
} else {
|
|
17420
17514
|
this.inputElement.value = '';
|
|
@@ -17523,58 +17617,73 @@ class BaseInput extends AuroElement$1 {
|
|
|
17523
17617
|
* @returns {void}
|
|
17524
17618
|
*/
|
|
17525
17619
|
configureAutoFormatting() {
|
|
17526
|
-
|
|
17527
|
-
|
|
17528
|
-
|
|
17529
|
-
|
|
17530
|
-
|
|
17531
|
-
this.
|
|
17620
|
+
// Re-entrancy guard. The patched-setter's synthetic input event (suppressed
|
|
17621
|
+
// by _configuringMask above) could otherwise trigger handleInput →
|
|
17622
|
+
// processCreditCard → configureAutoFormatting before the outer call's
|
|
17623
|
+
// set value has finished its alignCursor pass.
|
|
17624
|
+
if (this._configuringMask) return;
|
|
17625
|
+
this._configuringMask = true;
|
|
17626
|
+
try {
|
|
17627
|
+
if (this.maskInstance) {
|
|
17628
|
+
this.maskInstance.destroy();
|
|
17629
|
+
}
|
|
17532
17630
|
|
|
17533
|
-
|
|
17631
|
+
// Pass new format to util
|
|
17632
|
+
this.util.updateFormat(this.format);
|
|
17534
17633
|
|
|
17535
|
-
|
|
17634
|
+
const maskOptions = this.util.getMaskOptions(this.type, this.format);
|
|
17536
17635
|
|
|
17537
|
-
|
|
17538
|
-
// IMask's constructor processes the current input value which requires
|
|
17539
|
-
// selection state — clearing first avoids that scenario entirely.
|
|
17540
|
-
// When the format changes (e.g. locale switch) and we have a valid ISO
|
|
17541
|
-
// model value, compute the display string for the NEW format instead of
|
|
17542
|
-
// re-using the old display string, which may be invalid in the new mask.
|
|
17543
|
-
let existingValue = this.inputElement.value;
|
|
17544
|
-
if (
|
|
17545
|
-
this.util.isFullDateFormat(this.type, this.format) &&
|
|
17546
|
-
this.value &&
|
|
17547
|
-
dateFormatter.isValidDate(this.value) &&
|
|
17548
|
-
this.valueObject instanceof Date &&
|
|
17549
|
-
!Number.isNaN(this.valueObject.getTime()) &&
|
|
17550
|
-
typeof maskOptions.format === 'function'
|
|
17551
|
-
) {
|
|
17552
|
-
existingValue = maskOptions.format(this.valueObject);
|
|
17553
|
-
}
|
|
17636
|
+
if (this.inputElement && maskOptions.mask) {
|
|
17554
17637
|
|
|
17555
|
-
|
|
17556
|
-
|
|
17638
|
+
// Stash and clear any existing value before IMask init.
|
|
17639
|
+
// IMask's constructor processes the current input value which requires
|
|
17640
|
+
// selection state — clearing first avoids that scenario entirely.
|
|
17641
|
+
// When the format changes (e.g. locale switch) and we have a valid ISO
|
|
17642
|
+
// model value, compute the display string for the NEW format instead of
|
|
17643
|
+
// re-using the old display string, which may be invalid in the new mask.
|
|
17644
|
+
let existingValue = this.inputElement.value;
|
|
17645
|
+
if (
|
|
17646
|
+
this.util.isFullDateFormat(this.type, this.format) &&
|
|
17647
|
+
this.value &&
|
|
17648
|
+
dateFormatter.isValidDate(this.value) &&
|
|
17649
|
+
this.valueObject instanceof Date &&
|
|
17650
|
+
!Number.isNaN(this.valueObject.getTime()) &&
|
|
17651
|
+
typeof maskOptions.format === 'function'
|
|
17652
|
+
) {
|
|
17653
|
+
existingValue = maskOptions.format(this.valueObject);
|
|
17654
|
+
}
|
|
17557
17655
|
|
|
17558
|
-
|
|
17656
|
+
this.skipNextProgrammaticInputEvent = true;
|
|
17657
|
+
this.inputElement.value = '';
|
|
17658
|
+
|
|
17659
|
+
this.maskInstance = IMask(this.inputElement, maskOptions);
|
|
17660
|
+
|
|
17661
|
+
this.maskInstance.on('accept', () => {
|
|
17662
|
+
// Suppress propagation during configureAutoFormatting's own value-restoration
|
|
17663
|
+
// (line below) — the mask emits 'accept' on every value-set, including ours,
|
|
17664
|
+
// and we don't want to overwrite a value the parent just pushed.
|
|
17665
|
+
if (this._configuringMask) return;
|
|
17666
|
+
this.value = this.util.toModelValue(this.maskInstance.value, this.format);
|
|
17667
|
+
if (this.type === "date") {
|
|
17668
|
+
this._rawMaskValue = this.maskInstance.value;
|
|
17669
|
+
}
|
|
17670
|
+
});
|
|
17559
17671
|
|
|
17560
|
-
|
|
17561
|
-
|
|
17562
|
-
|
|
17563
|
-
this.
|
|
17564
|
-
|
|
17565
|
-
|
|
17672
|
+
this.maskInstance.on('complete', () => {
|
|
17673
|
+
if (this._configuringMask) return;
|
|
17674
|
+
this.value = this.util.toModelValue(this.maskInstance.value, this.format);
|
|
17675
|
+
if (this.type === "date") {
|
|
17676
|
+
this._rawMaskValue = this.maskInstance.value;
|
|
17677
|
+
}
|
|
17678
|
+
});
|
|
17566
17679
|
|
|
17567
|
-
|
|
17568
|
-
|
|
17569
|
-
|
|
17570
|
-
this._rawMaskValue = this.maskInstance.value;
|
|
17680
|
+
// Restore the stashed value through IMask so it's properly masked
|
|
17681
|
+
if (existingValue) {
|
|
17682
|
+
this.maskInstance.value = existingValue;
|
|
17571
17683
|
}
|
|
17572
|
-
});
|
|
17573
|
-
|
|
17574
|
-
// Restore the stashed value through IMask so it's properly masked
|
|
17575
|
-
if (existingValue) {
|
|
17576
|
-
this.maskInstance.value = existingValue;
|
|
17577
17684
|
}
|
|
17685
|
+
} finally {
|
|
17686
|
+
this._configuringMask = false;
|
|
17578
17687
|
}
|
|
17579
17688
|
}
|
|
17580
17689
|
|
|
@@ -17861,10 +17970,10 @@ class BaseInput extends AuroElement$1 {
|
|
|
17861
17970
|
},
|
|
17862
17971
|
{
|
|
17863
17972
|
name: 'Diners club',
|
|
17864
|
-
regex: /^(?<num>36|38)\d{0}/u,
|
|
17973
|
+
regex: /^(?<num>30[0-5]|36|38)\d{0}/u,
|
|
17865
17974
|
formatLength: 16,
|
|
17866
17975
|
errorMessage: CreditCardValidationMessage,
|
|
17867
|
-
cardIcon: '
|
|
17976
|
+
cardIcon: 'cc-dinersclub',
|
|
17868
17977
|
maskFormat: "0000 000000 0000"
|
|
17869
17978
|
},
|
|
17870
17979
|
{
|
|
@@ -18274,7 +18383,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
|
|
|
18274
18383
|
}
|
|
18275
18384
|
};
|
|
18276
18385
|
|
|
18277
|
-
var formkitVersion$1 = '
|
|
18386
|
+
var formkitVersion$1 = '202606100232';
|
|
18278
18387
|
|
|
18279
18388
|
// Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
|
|
18280
18389
|
// See LICENSE in the project root for license information.
|
|
@@ -19399,7 +19508,7 @@ class AuroBibtemplate extends LitElement {
|
|
|
19399
19508
|
}
|
|
19400
19509
|
}
|
|
19401
19510
|
|
|
19402
|
-
var formkitVersion = '
|
|
19511
|
+
var formkitVersion = '202606100232';
|
|
19403
19512
|
|
|
19404
19513
|
var styleCss$1 = css`.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}`;
|
|
19405
19514
|
|
|
@@ -19756,6 +19865,25 @@ function normalizeFilterValue(value) {
|
|
|
19756
19865
|
return raw.trim() ? raw.trimEnd() : raw;
|
|
19757
19866
|
}
|
|
19758
19867
|
|
|
19868
|
+
/**
|
|
19869
|
+
* Returns the option's plain-text label suitable for input.value, with
|
|
19870
|
+
* HTML-indentation whitespace collapsed and the displayValue slot text
|
|
19871
|
+
* removed (the slot renders separately in the trigger).
|
|
19872
|
+
* @param {HTMLElement} option - An auro-menuoption element.
|
|
19873
|
+
* @returns {string} Normalized label.
|
|
19874
|
+
*/
|
|
19875
|
+
function getOptionLabel(option) {
|
|
19876
|
+
if (!option) {
|
|
19877
|
+
return '';
|
|
19878
|
+
}
|
|
19879
|
+
const clone = option.cloneNode(true);
|
|
19880
|
+
const displayValueEl = clone.querySelector('[slot="displayValue"]');
|
|
19881
|
+
if (displayValueEl) {
|
|
19882
|
+
displayValueEl.remove();
|
|
19883
|
+
}
|
|
19884
|
+
return (clone.textContent || '').replace(/\s+/gu, ' ').trim();
|
|
19885
|
+
}
|
|
19886
|
+
|
|
19759
19887
|
// See https://git.io/JJ6SJ for "How to document your components using JSDoc"
|
|
19760
19888
|
/**
|
|
19761
19889
|
* The `auro-combobox` element provides users with a way to select an option from a list of filtered or suggested options based on user input.
|
|
@@ -19883,7 +20011,19 @@ class AuroCombobox extends AuroElement {
|
|
|
19883
20011
|
availableOptions: {
|
|
19884
20012
|
state: true,
|
|
19885
20013
|
type: Array,
|
|
19886
|
-
reflect: false
|
|
20014
|
+
reflect: false,
|
|
20015
|
+
hasChanged(newVal, oldVal) {
|
|
20016
|
+
if (!oldVal && !newVal) {
|
|
20017
|
+
return false;
|
|
20018
|
+
}
|
|
20019
|
+
if (!oldVal || !newVal) {
|
|
20020
|
+
return true;
|
|
20021
|
+
}
|
|
20022
|
+
if (newVal.length !== oldVal.length) {
|
|
20023
|
+
return true;
|
|
20024
|
+
}
|
|
20025
|
+
return newVal.some((opt, idx) => opt !== oldVal[idx]);
|
|
20026
|
+
}
|
|
19887
20027
|
},
|
|
19888
20028
|
|
|
19889
20029
|
/**
|
|
@@ -20314,18 +20454,15 @@ class AuroCombobox extends AuroElement {
|
|
|
20314
20454
|
matchString = `${matchString} ${option.getAttribute('suggest')}`.toLowerCase();
|
|
20315
20455
|
}
|
|
20316
20456
|
|
|
20317
|
-
|
|
20318
|
-
|
|
20319
|
-
|
|
20320
|
-
|
|
20321
|
-
|
|
20322
|
-
|
|
20323
|
-
} else if (matchString.includes(filterValue) && !option.hasAttribute('static')) {
|
|
20324
|
-
// only count options that match the typed input value AND are not static
|
|
20457
|
+
if (!filterValue && option.hasAttribute('static')) {
|
|
20458
|
+
// Static options are shown when the input is empty so consumers can
|
|
20459
|
+
// surface headers / "add new" rows when no filter is active.
|
|
20460
|
+
option.removeAttribute('hidden');
|
|
20461
|
+
this.availableOptions.push(option);
|
|
20462
|
+
} else if (filterValue && matchString.includes(filterValue) && !option.hasAttribute('static')) {
|
|
20325
20463
|
option.removeAttribute('hidden');
|
|
20326
20464
|
this.availableOptions.push(option);
|
|
20327
20465
|
} else if (!option.hasAttribute('persistent')) {
|
|
20328
|
-
// Hide all other non-persistent options
|
|
20329
20466
|
option.setAttribute('hidden', '');
|
|
20330
20467
|
option.removeAttribute('aria-setsize');
|
|
20331
20468
|
option.removeAttribute('aria-posinset');
|
|
@@ -20333,7 +20470,9 @@ class AuroCombobox extends AuroElement {
|
|
|
20333
20470
|
});
|
|
20334
20471
|
|
|
20335
20472
|
if (this.availableOptions.length === 0) {
|
|
20336
|
-
|
|
20473
|
+
// Only surface the nomatch option once the user has actually typed
|
|
20474
|
+
// something. Empty input is handled by the bib being closed.
|
|
20475
|
+
if (this.noMatchOption && filterValue) {
|
|
20337
20476
|
this.noMatchOption.removeAttribute('hidden');
|
|
20338
20477
|
} else if (!this.menu.loading || this.isHiddenWhileLoading) {
|
|
20339
20478
|
this.hideBib();
|
|
@@ -20349,11 +20488,14 @@ class AuroCombobox extends AuroElement {
|
|
|
20349
20488
|
* @returns {void}
|
|
20350
20489
|
*/
|
|
20351
20490
|
syncValuesAndStates() {
|
|
20352
|
-
//
|
|
20491
|
+
// Do NOT call setMenuValue here — the caller (handleMenuOptions) already
|
|
20492
|
+
// guards setMenuValue with an option-match check. Calling it unconditionally
|
|
20493
|
+
// sets free-form values on the menu, triggering clearSelection which resets
|
|
20494
|
+
// _index and breaks subsequent makeSelection calls.
|
|
20353
20495
|
if (this.menu) {
|
|
20354
20496
|
this.menu.matchWord = normalizeFilterValue(this.input.value);
|
|
20355
20497
|
}
|
|
20356
|
-
const label = this.menu.currentLabel;
|
|
20498
|
+
const label = getOptionLabel(this.menu.optionSelected) || this.menu.currentLabel;
|
|
20357
20499
|
this.updateTriggerTextDisplay(label || this.value);
|
|
20358
20500
|
}
|
|
20359
20501
|
|
|
@@ -20368,7 +20510,39 @@ class AuroCombobox extends AuroElement {
|
|
|
20368
20510
|
const isInputFocusedWithNoSelection = !this.menu.value && (this.input.matches(':focus-within') || (this.inputInBib && this.inputInBib.matches(':focus-within')));
|
|
20369
20511
|
|
|
20370
20512
|
if (!this.persistInput && !(this.behavior === 'suggestion' && isInputFocusedWithNoSelection)) {
|
|
20371
|
-
|
|
20513
|
+
const nextValue = label || this.value;
|
|
20514
|
+
// Only set the flag when there's an actual write to suppress —
|
|
20515
|
+
// syncValuesAndStates re-enters here during typing when both inputs
|
|
20516
|
+
// already match, and a no-op flag flip would make the bib branch's
|
|
20517
|
+
// bail eat legitimate user-input events.
|
|
20518
|
+
const triggerNeedsSync = this.input.value !== nextValue;
|
|
20519
|
+
const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
|
|
20520
|
+
if (triggerNeedsSync || bibNeedsSync) {
|
|
20521
|
+
this._syncingDisplayValue = true;
|
|
20522
|
+
if (triggerNeedsSync) {
|
|
20523
|
+
this.input.value = nextValue;
|
|
20524
|
+
}
|
|
20525
|
+
if (bibNeedsSync) {
|
|
20526
|
+
this.inputInBib.value = nextValue;
|
|
20527
|
+
}
|
|
20528
|
+
const pending = [];
|
|
20529
|
+
if (triggerNeedsSync) {
|
|
20530
|
+
pending.push(this.input.updateComplete);
|
|
20531
|
+
}
|
|
20532
|
+
if (bibNeedsSync) {
|
|
20533
|
+
pending.push(this.inputInBib.updateComplete);
|
|
20534
|
+
}
|
|
20535
|
+
Promise.all(pending).then(() => {
|
|
20536
|
+
// imask reasserts otherwise, and handleBlur reverts to its stale value.
|
|
20537
|
+
if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
|
|
20538
|
+
this.input.maskInstance.updateValue();
|
|
20539
|
+
}
|
|
20540
|
+
if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
|
|
20541
|
+
this.inputInBib.maskInstance.updateValue();
|
|
20542
|
+
}
|
|
20543
|
+
this._syncingDisplayValue = false;
|
|
20544
|
+
});
|
|
20545
|
+
}
|
|
20372
20546
|
}
|
|
20373
20547
|
|
|
20374
20548
|
// update the displayValue in the trigger if displayValue slot content is present
|
|
@@ -20382,6 +20556,14 @@ class AuroCombobox extends AuroElement {
|
|
|
20382
20556
|
const displayValueEl = this.menu.optionSelected.querySelector("[slot='displayValue']");
|
|
20383
20557
|
if (displayValueEl) {
|
|
20384
20558
|
this.input.appendChild(displayValueEl.cloneNode(true));
|
|
20559
|
+
// auro-input's hasDisplayValueContent is non-reactive; nudge it to
|
|
20560
|
+
// re-evaluate the slot and re-render so the displayValue wrapper
|
|
20561
|
+
// gets its hasContent class. Without this, the apple/icon stays
|
|
20562
|
+
// hidden when input.value is set in the same tick as the append.
|
|
20563
|
+
if (typeof this.input.checkDisplayValueSlotChange === 'function') {
|
|
20564
|
+
this.input.checkDisplayValueSlotChange();
|
|
20565
|
+
this.input.requestUpdate();
|
|
20566
|
+
}
|
|
20385
20567
|
}
|
|
20386
20568
|
}
|
|
20387
20569
|
|
|
@@ -20415,7 +20597,10 @@ class AuroCombobox extends AuroElement {
|
|
|
20415
20597
|
this.syncValuesAndStates();
|
|
20416
20598
|
}
|
|
20417
20599
|
|
|
20418
|
-
|
|
20600
|
+
// Re-activate when optionActive is no longer visible, or when _index has
|
|
20601
|
+
// been reset (e.g. by clearSelection in an async update) so that
|
|
20602
|
+
// makeSelection() can find the correct option.
|
|
20603
|
+
if (!this.availableOptions.includes(this.menu.optionActive) || this.menu._index < 0) {
|
|
20419
20604
|
this.activateFirstEnabledAvailableOption();
|
|
20420
20605
|
}
|
|
20421
20606
|
}
|
|
@@ -20540,8 +20725,13 @@ class AuroCombobox extends AuroElement {
|
|
|
20540
20725
|
|
|
20541
20726
|
guardTouchPassthrough(this.menu);
|
|
20542
20727
|
|
|
20543
|
-
//
|
|
20544
|
-
//
|
|
20728
|
+
// The dialog's showModal() steals focus from the trigger.
|
|
20729
|
+
// A single rAF is enough for the dialog DOM to be painted and
|
|
20730
|
+
// focusable, then doubleRaf finalizes the transition.
|
|
20731
|
+
requestAnimationFrame(() => {
|
|
20732
|
+
this.setInputFocus();
|
|
20733
|
+
});
|
|
20734
|
+
|
|
20545
20735
|
doubleRaf(() => {
|
|
20546
20736
|
this.setInputFocus();
|
|
20547
20737
|
this._inFullscreenTransition = false;
|
|
@@ -20622,10 +20812,30 @@ class AuroCombobox extends AuroElement {
|
|
|
20622
20812
|
*/
|
|
20623
20813
|
setInputFocus() {
|
|
20624
20814
|
if (this.dropdown.isBibFullscreen && this.dropdown.isPopoverVisible) {
|
|
20625
|
-
|
|
20815
|
+
// Sync the native input value synchronously before focusing.
|
|
20816
|
+
// Lit's property-to-attribute sync is async (microtask), so the
|
|
20817
|
+
// native <input> inside inputInBib may still hold a stale value
|
|
20818
|
+
// when focus moves here during the fullscreen transition. Without
|
|
20819
|
+
// this, keystrokes (e.g. Backspace) operate on the wrong content.
|
|
20820
|
+
const nativeInput = this.inputInBib.inputElement;
|
|
20821
|
+
const triggerNativeInput = this.input.inputElement;
|
|
20822
|
+
if (nativeInput && triggerNativeInput && nativeInput.value !== triggerNativeInput.value) {
|
|
20823
|
+
this.inputInBib.skipNextProgrammaticInputEvent = true;
|
|
20824
|
+
nativeInput.value = triggerNativeInput.value || '';
|
|
20825
|
+
}
|
|
20826
|
+
|
|
20827
|
+
// Focus the native input directly to ensure it receives keystrokes
|
|
20828
|
+
// after the fullscreen dialog opens. The dialog's showModal() may
|
|
20829
|
+
// have moved focus to the dialog element itself; focusing the
|
|
20830
|
+
// auro-input custom element doesn't always reach the native input
|
|
20831
|
+
// when the dialog DOM isn't fully painted.
|
|
20832
|
+
if (nativeInput) {
|
|
20833
|
+
nativeInput.focus();
|
|
20834
|
+
} else {
|
|
20835
|
+
this.inputInBib.focus();
|
|
20836
|
+
}
|
|
20626
20837
|
|
|
20627
20838
|
// Place cursor at end of existing text so the user can continue editing
|
|
20628
|
-
const nativeInput = this.inputInBib.inputElement;
|
|
20629
20839
|
if (nativeInput && nativeInput.value) {
|
|
20630
20840
|
const len = nativeInput.value.length;
|
|
20631
20841
|
nativeInput.setSelectionRange(len, len);
|
|
@@ -20733,53 +20943,92 @@ class AuroCombobox extends AuroElement {
|
|
|
20733
20943
|
this.menu.setAttribute('nocheckmark', '');
|
|
20734
20944
|
}
|
|
20735
20945
|
|
|
20736
|
-
|
|
20737
|
-
|
|
20738
|
-
|
|
20946
|
+
// Handle menu option selection
|
|
20947
|
+
this.menu.addEventListener('auroMenu-selectedOption', (evt) => {
|
|
20948
|
+
// Capture before the consumption below — true means this event is the
|
|
20949
|
+
// echo of our own setMenuValue (programmatic value sync), false means
|
|
20950
|
+
// a fresh user selection.
|
|
20951
|
+
const isEcho = this._pendingMenuValueSync === true;
|
|
20739
20952
|
|
|
20740
|
-
|
|
20741
|
-
|
|
20742
|
-
// Update the optionSelected from the event details, not manually
|
|
20743
|
-
[this.optionSelected] = event.detail.options;
|
|
20953
|
+
if (this.menu.optionSelected) {
|
|
20954
|
+
const selected = this.menu.optionSelected;
|
|
20744
20955
|
|
|
20745
|
-
|
|
20746
|
-
|
|
20956
|
+
if (!this.optionSelected || this.optionSelected !== selected) {
|
|
20957
|
+
this.optionSelected = selected;
|
|
20958
|
+
}
|
|
20747
20959
|
|
|
20748
|
-
|
|
20749
|
-
|
|
20960
|
+
// Skip writeback when this event is the echo of our own setMenuValue —
|
|
20961
|
+
// otherwise it cascades against handleInputValueChange in suggestion mode.
|
|
20962
|
+
if (this._pendingMenuValueSync) {
|
|
20963
|
+
this._pendingMenuValueSync = false;
|
|
20964
|
+
} else if (!this.value || this.value !== this.optionSelected.value) {
|
|
20965
|
+
this.value = this.optionSelected.value;
|
|
20966
|
+
}
|
|
20750
20967
|
|
|
20751
|
-
|
|
20752
|
-
|
|
20753
|
-
if (this.menu.matchWord !== trimmedInput) {
|
|
20754
|
-
this.menu.matchWord = trimmedInput;
|
|
20755
|
-
}
|
|
20968
|
+
// Update display
|
|
20969
|
+
this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected) || this.menu.value);
|
|
20756
20970
|
|
|
20757
|
-
|
|
20758
|
-
|
|
20971
|
+
// Update match word for filtering
|
|
20972
|
+
const trimmedInput = normalizeFilterValue(this.input.value);
|
|
20973
|
+
if (this.menu.matchWord !== trimmedInput) {
|
|
20974
|
+
this.menu.matchWord = trimmedInput;
|
|
20975
|
+
}
|
|
20976
|
+
}
|
|
20759
20977
|
|
|
20760
20978
|
// Hide dropdown on selection (except during slot changes)
|
|
20761
|
-
if (
|
|
20762
|
-
|
|
20763
|
-
|
|
20764
|
-
|
|
20765
|
-
|
|
20766
|
-
}
|
|
20767
|
-
}, 0);
|
|
20979
|
+
if (evt.detail && evt.detail.source !== 'slotchange') {
|
|
20980
|
+
// do not close while typing in suggestion mode with no value selected, to allow freeform input
|
|
20981
|
+
if (this.menu.value || this.behavior !== 'suggestion') {
|
|
20982
|
+
this.hideBib();
|
|
20983
|
+
}
|
|
20768
20984
|
|
|
20769
20985
|
// Announce the selection after the dropdown closes so it isn't
|
|
20770
20986
|
// overridden by VoiceOver's "collapsed" announcement from aria-expanded.
|
|
20771
|
-
const selectedValue =
|
|
20987
|
+
const selectedValue = this.menu.value;
|
|
20772
20988
|
const announcementDelay = 300;
|
|
20773
20989
|
setTimeout(() => {
|
|
20774
20990
|
announceToScreenReader(this._getAnnouncementRoot(), `${selectedValue}, selected`);
|
|
20775
20991
|
}, announcementDelay);
|
|
20776
20992
|
}
|
|
20993
|
+
|
|
20994
|
+
// Programmatic value syncs leave availableOptions stale because
|
|
20995
|
+
// updateTriggerTextDisplay sets input.value with _syncingDisplayValue
|
|
20996
|
+
// and handleInputValueChange bails. Refresh the filter on echo events
|
|
20997
|
+
// only — fresh user selections take the existing hideBib path.
|
|
20998
|
+
if (isEcho && this.menu.optionSelected) {
|
|
20999
|
+
this._programmaticFilterRefresh = true;
|
|
21000
|
+
this.handleMenuOptions();
|
|
21001
|
+
setTimeout(() => {
|
|
21002
|
+
this._programmaticFilterRefresh = false;
|
|
21003
|
+
}, 0);
|
|
21004
|
+
}
|
|
21005
|
+
|
|
21006
|
+
// base-input skips auto-validate when focus is inside its own shadow,
|
|
21007
|
+
// which it is after setTriggerInputFocus — re-run so prior tooShort
|
|
21008
|
+
// clears. processCreditCard reasserts errorMessage on every render.
|
|
21009
|
+
if (!isEcho && this.menu.optionSelected && this.input.validate) {
|
|
21010
|
+
this.input.updateComplete.then(() => {
|
|
21011
|
+
this.input.validate(true);
|
|
21012
|
+
if (this.input.validity === 'valid') {
|
|
21013
|
+
this.input.errorMessage = '';
|
|
21014
|
+
}
|
|
21015
|
+
});
|
|
21016
|
+
}
|
|
20777
21017
|
});
|
|
20778
21018
|
|
|
20779
21019
|
this.menu.addEventListener('auroMenu-customEventFired', () => {
|
|
20780
21020
|
this.hideBib();
|
|
20781
21021
|
});
|
|
20782
21022
|
|
|
21023
|
+
// When the menu cannot match a programmatic value to any option,
|
|
21024
|
+
// clear the combobox selection state so it doesn't reference a
|
|
21025
|
+
// stale option. Safe from re-entrancy because any resulting
|
|
21026
|
+
// input.value changes dispatch isProgrammatic events.
|
|
21027
|
+
this.menu.addEventListener('auroMenu-selectValueFailure', () => {
|
|
21028
|
+
this.value = undefined;
|
|
21029
|
+
this.optionSelected = undefined;
|
|
21030
|
+
});
|
|
21031
|
+
|
|
20783
21032
|
this.menu.addEventListener('auroMenu-activatedOption', (evt) => {
|
|
20784
21033
|
this.optionActive = evt.detail;
|
|
20785
21034
|
|
|
@@ -20880,11 +21129,12 @@ class AuroCombobox extends AuroElement {
|
|
|
20880
21129
|
// event from the trigger. The _syncingBibValue guard persists across the
|
|
20881
21130
|
// async boundary and prevents that re-entrant event from running the
|
|
20882
21131
|
// non-fullscreen path (which would call clear() → hideBib()).
|
|
20883
|
-
// When the event comes from the fullscreen bib input, sync the value to
|
|
20884
|
-
// the trigger and run filtering, but suppress the re-entrant input event
|
|
20885
|
-
// that the trigger fires (via Lit updated() → notifyValueChanged()) so
|
|
20886
|
-
// the non-fullscreen hide/clear logic doesn't close the dialog.
|
|
20887
21132
|
if (event.target === this.inputInBib) {
|
|
21133
|
+
// Mirror the trigger-branch bail below — programmatic inputInBib writes
|
|
21134
|
+
// notify back into this branch via Lit's notifyValueChanged.
|
|
21135
|
+
if (this._syncingDisplayValue) {
|
|
21136
|
+
return;
|
|
21137
|
+
}
|
|
20888
21138
|
this._syncingBibValue = true;
|
|
20889
21139
|
this.input.value = this.inputInBib.value;
|
|
20890
21140
|
this.input.updateComplete.then(() => {
|
|
@@ -20894,18 +21144,37 @@ class AuroCombobox extends AuroElement {
|
|
|
20894
21144
|
// Run filtering inline — the re-entrant event won't reach this code.
|
|
20895
21145
|
this.menu.matchWord = normalizeFilterValue(this.inputInBib.value);
|
|
20896
21146
|
this.optionActive = null;
|
|
21147
|
+
|
|
21148
|
+
// In suggestion mode, keep the combobox value in sync with the typed
|
|
21149
|
+
// text so that freeform values are captured (mirroring the non-fullscreen
|
|
21150
|
+
// path). Clear the selection when the input is emptied.
|
|
21151
|
+
if (this.behavior === 'suggestion') {
|
|
21152
|
+
this.value = this.inputInBib.value || undefined;
|
|
21153
|
+
}
|
|
21154
|
+
|
|
20897
21155
|
this.handleMenuOptions();
|
|
20898
21156
|
this.dispatchEvent(new CustomEvent('inputValue', { detail: { value: this.inputValue } }));
|
|
20899
21157
|
return;
|
|
20900
21158
|
}
|
|
20901
21159
|
|
|
20902
|
-
// Ignore re-entrant input events caused by
|
|
20903
|
-
if (this._syncingBibValue) {
|
|
21160
|
+
// Ignore re-entrant input events caused by programmatic value sets.
|
|
21161
|
+
if (this._syncingBibValue || this._syncingDisplayValue) {
|
|
20904
21162
|
return;
|
|
20905
21163
|
}
|
|
20906
21164
|
|
|
20907
21165
|
this.inputInBib.value = this.input.value;
|
|
20908
21166
|
|
|
21167
|
+
// Also sync the native input immediately so keystrokes arriving
|
|
21168
|
+
// before Lit's async update cycle (e.g. rapid Backspaces during a
|
|
21169
|
+
// fullscreen transition) operate on the correct content.
|
|
21170
|
+
// Skip the next programmatic input event to prevent the patched setter
|
|
21171
|
+
// from re-entering handleInputValueChange via the bib path.
|
|
21172
|
+
const bibNativeInput = this.inputInBib.inputElement;
|
|
21173
|
+
if (bibNativeInput && bibNativeInput.value !== this.input.value) {
|
|
21174
|
+
this.inputInBib.skipNextProgrammaticInputEvent = true;
|
|
21175
|
+
bibNativeInput.value = this.input.value || '';
|
|
21176
|
+
}
|
|
21177
|
+
|
|
20909
21178
|
this.menu.matchWord = normalizeFilterValue(this.input.value);
|
|
20910
21179
|
this.optionActive = null;
|
|
20911
21180
|
|
|
@@ -20913,7 +21182,7 @@ class AuroCombobox extends AuroElement {
|
|
|
20913
21182
|
this.value = this.input.value;
|
|
20914
21183
|
}
|
|
20915
21184
|
|
|
20916
|
-
if (!this.input.value) {
|
|
21185
|
+
if (!this.input.value && !this._clearing) {
|
|
20917
21186
|
this.clear();
|
|
20918
21187
|
}
|
|
20919
21188
|
this.handleMenuOptions();
|
|
@@ -20982,6 +21251,16 @@ class AuroCombobox extends AuroElement {
|
|
|
20982
21251
|
return;
|
|
20983
21252
|
}
|
|
20984
21253
|
|
|
21254
|
+
// Ignore dispatches from the bib (fullscreen) input. It's re-validated
|
|
21255
|
+
// inside this.validate()'s auroInputElements loop with its own
|
|
21256
|
+
// (often undefined) validity, and the event is composed/bubbles up to
|
|
21257
|
+
// this listener with `target` retargeted to the combobox. Letting it
|
|
21258
|
+
// through would overwrite the trigger input's correct validity with
|
|
21259
|
+
// the bib input's stale one (e.g. wiping `tooShort` during typing).
|
|
21260
|
+
if (this.inputInBib && evt.composedPath()[0] === this.inputInBib && this.inputInBib !== this.input) {
|
|
21261
|
+
return;
|
|
21262
|
+
}
|
|
21263
|
+
|
|
20985
21264
|
this.input.validity = evt.detail.validity;
|
|
20986
21265
|
this.input.errorMessage = evt.detail.message;
|
|
20987
21266
|
this.validity = evt.detail.validity;
|
|
@@ -21043,10 +21322,18 @@ class AuroCombobox extends AuroElement {
|
|
|
21043
21322
|
* @returns {void}
|
|
21044
21323
|
*/
|
|
21045
21324
|
setMenuValue(value) {
|
|
21046
|
-
if (!this.menu) {
|
|
21325
|
+
if (!this.menu || this.menu.value === value) {
|
|
21047
21326
|
return;
|
|
21048
21327
|
}
|
|
21328
|
+
// One-shot flag consumed by the auroMenu-selectedOption listener so the
|
|
21329
|
+
// echo of this sync doesn't propagate back to this.value.
|
|
21330
|
+
this._pendingMenuValueSync = true;
|
|
21049
21331
|
this.menu.value = value;
|
|
21332
|
+
// Backup clear: if menu.value === menu.optionSelected.value already, the
|
|
21333
|
+
// listener won't fire and the flag would swallow the next user click.
|
|
21334
|
+
setTimeout(() => {
|
|
21335
|
+
this._pendingMenuValueSync = false;
|
|
21336
|
+
}, 0);
|
|
21050
21337
|
}
|
|
21051
21338
|
|
|
21052
21339
|
/**
|
|
@@ -21061,6 +21348,15 @@ class AuroCombobox extends AuroElement {
|
|
|
21061
21348
|
this.menu.value = undefined;
|
|
21062
21349
|
this.validation.reset(this);
|
|
21063
21350
|
this.touched = false;
|
|
21351
|
+
// Force validity back to the cleared state. validation.reset() calls
|
|
21352
|
+
// validate() at the end, and (post-credit-card-fix) the trigger input's
|
|
21353
|
+
// residual validity may still be copied into the combobox by the
|
|
21354
|
+
// auroInputElements loop. Explicitly clear here so reset() actually
|
|
21355
|
+
// leaves the component in a "no validity" state.
|
|
21356
|
+
this.validity = undefined;
|
|
21357
|
+
this.errorMessage = '';
|
|
21358
|
+
this.input.validity = undefined;
|
|
21359
|
+
this.input.errorMessage = '';
|
|
21064
21360
|
}
|
|
21065
21361
|
|
|
21066
21362
|
/**
|
|
@@ -21068,16 +21364,24 @@ class AuroCombobox extends AuroElement {
|
|
|
21068
21364
|
* @returns {void}
|
|
21069
21365
|
*/
|
|
21070
21366
|
clear() {
|
|
21071
|
-
//
|
|
21072
|
-
|
|
21073
|
-
this.
|
|
21074
|
-
|
|
21075
|
-
// Then clear input and menu
|
|
21076
|
-
if (this.input.value) {
|
|
21077
|
-
this.input.clear();
|
|
21367
|
+
// input.clear() fires an input event that re-enters handleInputValueChange
|
|
21368
|
+
// → which calls clear() again on empty input. Guard against that loop.
|
|
21369
|
+
if (this._clearing) {
|
|
21370
|
+
return;
|
|
21078
21371
|
}
|
|
21079
|
-
|
|
21080
|
-
|
|
21372
|
+
this._clearing = true;
|
|
21373
|
+
try {
|
|
21374
|
+
this.optionSelected = undefined;
|
|
21375
|
+
this.value = undefined;
|
|
21376
|
+
|
|
21377
|
+
if (this.input.value) {
|
|
21378
|
+
this.input.clear();
|
|
21379
|
+
}
|
|
21380
|
+
if (this.menu.value || this.menu.optionSelected) {
|
|
21381
|
+
this.menu.reset();
|
|
21382
|
+
}
|
|
21383
|
+
} finally {
|
|
21384
|
+
this._clearing = false;
|
|
21081
21385
|
}
|
|
21082
21386
|
}
|
|
21083
21387
|
|
|
@@ -21105,29 +21409,64 @@ class AuroCombobox extends AuroElement {
|
|
|
21105
21409
|
this.input.value = this.value;
|
|
21106
21410
|
}
|
|
21107
21411
|
|
|
21108
|
-
|
|
21109
|
-
|
|
21110
|
-
|
|
21111
|
-
|
|
21112
|
-
|
|
21113
|
-
|
|
21114
|
-
|
|
21115
|
-
|
|
21116
|
-
|
|
21117
|
-
if (!this.menu.options || this.menu.options.length === 0) ; else if (this.menu.options.filter((opt) => opt.value === this.value).length > 0) {
|
|
21412
|
+
// Sync menu.value only when an option actually matches; otherwise clear it.
|
|
21413
|
+
if (this.menu.options && this.menu.options.length > 0) {
|
|
21414
|
+
if (this.menu.options.some((opt) => opt.value === this.value)) {
|
|
21415
|
+
this.setMenuValue(this.value);
|
|
21416
|
+
} else if (this.behavior === 'filter') {
|
|
21417
|
+
// In filter mode, freeform values aren't allowed. Push the unmatched
|
|
21418
|
+
// value through setMenuValue so menu dispatches auroMenu-selectValueFailure
|
|
21419
|
+
// and the listener at line 1206 clears combobox.value (mirrors select's
|
|
21420
|
+
// pattern). Bypassing setMenuValue here would skip the failure cascade.
|
|
21118
21421
|
this.setMenuValue(this.value);
|
|
21119
21422
|
} else {
|
|
21120
|
-
this.menu.value
|
|
21121
|
-
|
|
21122
|
-
|
|
21123
|
-
|
|
21124
|
-
|
|
21125
|
-
|
|
21126
|
-
|
|
21127
|
-
|
|
21128
|
-
this.
|
|
21423
|
+
if (this.menu.value) {
|
|
21424
|
+
this.menu.value = undefined;
|
|
21425
|
+
}
|
|
21426
|
+
// Sync the display for programmatic freeform value changes (e.g. the
|
|
21427
|
+
// swap-values pattern). Guard with _syncingDisplayValue so that the
|
|
21428
|
+
// re-entrant input event from base-input.notifyValueChanged() is
|
|
21429
|
+
// ignored by handleInputValueChange. Skip the sync when input already
|
|
21430
|
+
// matches to avoid spurious events during the normal typing path.
|
|
21431
|
+
const nextValue = this.value || '';
|
|
21432
|
+
const triggerNeedsSync = this.input.value !== nextValue;
|
|
21433
|
+
const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
|
|
21434
|
+
if (triggerNeedsSync || bibNeedsSync) {
|
|
21435
|
+
this._syncingDisplayValue = true;
|
|
21436
|
+
if (triggerNeedsSync) {
|
|
21437
|
+
this.input.value = nextValue;
|
|
21438
|
+
}
|
|
21439
|
+
if (bibNeedsSync) {
|
|
21440
|
+
this.inputInBib.value = nextValue;
|
|
21441
|
+
}
|
|
21442
|
+
const pending = [];
|
|
21443
|
+
if (triggerNeedsSync) {
|
|
21444
|
+
pending.push(this.input.updateComplete);
|
|
21445
|
+
}
|
|
21446
|
+
if (bibNeedsSync) {
|
|
21447
|
+
pending.push(this.inputInBib.updateComplete);
|
|
21448
|
+
}
|
|
21449
|
+
Promise.all(pending).then(() => {
|
|
21450
|
+
if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
|
|
21451
|
+
this.input.maskInstance.updateValue();
|
|
21452
|
+
}
|
|
21453
|
+
if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
|
|
21454
|
+
this.inputInBib.maskInstance.updateValue();
|
|
21455
|
+
}
|
|
21456
|
+
this._syncingDisplayValue = false;
|
|
21457
|
+
// handleInputValueChange bailed on the flag — refresh the filter.
|
|
21458
|
+
this._programmaticFilterRefresh = true;
|
|
21459
|
+
this.handleMenuOptions();
|
|
21460
|
+
setTimeout(() => {
|
|
21461
|
+
this._programmaticFilterRefresh = false;
|
|
21462
|
+
}, 0);
|
|
21463
|
+
});
|
|
21464
|
+
}
|
|
21129
21465
|
}
|
|
21130
21466
|
}
|
|
21467
|
+
if (!this.value) {
|
|
21468
|
+
this.clear();
|
|
21469
|
+
}
|
|
21131
21470
|
if (this.value && !this.componentHasFocus) {
|
|
21132
21471
|
// If the value got set programmatically make sure we hide the bib
|
|
21133
21472
|
// when input is not taking the focus (input can be in dropdown.trigger or in bibtemplate)
|
|
@@ -21164,12 +21503,18 @@ class AuroCombobox extends AuroElement {
|
|
|
21164
21503
|
// branch from calling hideBib() when the dropdown was just opened but
|
|
21165
21504
|
// :focus-within hasn't propagated through the top-layer dialog's nested
|
|
21166
21505
|
// shadow DOM boundaries.
|
|
21167
|
-
|
|
21506
|
+
// Skip the show/hide side effects when the availableOptions change came
|
|
21507
|
+
// from a programmatic filter refresh after a value swap — the user
|
|
21508
|
+
// didn't interact, so we shouldn't auto-open the bib (especially for
|
|
21509
|
+
// the no-match path where the existing condition unconditionally fires).
|
|
21510
|
+
if (this._programmaticFilterRefresh) {
|
|
21511
|
+
this._programmaticFilterRefresh = false;
|
|
21512
|
+
} else if ((this.availableOptions.length > 0 && (this.componentHasFocus || this.dropdownOpen)) || (this.menu && this.menu.loading) || (this.availableOptions.length === 0 && this.noMatchOption)) {
|
|
21168
21513
|
this.showBib();
|
|
21169
21514
|
if (!this.availableOptions.includes(this.menu.optionActive)) {
|
|
21170
21515
|
this.activateFirstEnabledAvailableOption();
|
|
21171
21516
|
}
|
|
21172
|
-
} else {
|
|
21517
|
+
} else if (this.dropdown && this.dropdown.isPopoverVisible) {
|
|
21173
21518
|
this.hideBib();
|
|
21174
21519
|
}
|
|
21175
21520
|
}
|