@aurodesignsystem-dev/auro-formkit 0.0.0-pr1507.0 → 0.0.0-pr1508.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.html +1 -2
- package/components/checkbox/demo/customize.min.js +1 -1
- package/components/checkbox/demo/getting-started.min.js +1 -1
- package/components/checkbox/demo/index.min.js +1 -1
- package/components/checkbox/dist/index.js +1 -1
- package/components/checkbox/dist/registered.js +1 -1
- package/components/combobox/demo/customize.html +1 -2
- package/components/combobox/demo/customize.md +108 -132
- package/components/combobox/demo/customize.min.js +150 -293
- package/components/combobox/demo/getting-started.min.js +150 -293
- package/components/combobox/demo/index.min.js +150 -293
- package/components/combobox/dist/auro-combobox.d.ts +9 -0
- package/components/combobox/dist/index.js +150 -293
- package/components/combobox/dist/registered.js +150 -293
- package/components/counter/demo/customize.min.js +10 -2
- package/components/counter/demo/index.min.js +10 -2
- package/components/counter/dist/index.js +10 -2
- package/components/counter/dist/registered.js +10 -2
- package/components/datepicker/demo/api.md +2 -0
- package/components/datepicker/demo/customize.js +0 -2
- package/components/datepicker/demo/customize.md +138 -38
- package/components/datepicker/demo/customize.min.js +59 -168
- package/components/datepicker/demo/index.min.js +59 -150
- package/components/datepicker/dist/index.js +59 -150
- package/components/datepicker/dist/registered.js +59 -150
- package/components/datepicker/dist/src/auro-datepicker.d.ts +2 -0
- package/components/dropdown/demo/customize.min.js +9 -1
- package/components/dropdown/demo/getting-started.min.js +9 -1
- package/components/dropdown/demo/index.min.js +9 -1
- package/components/dropdown/dist/auro-dropdown.d.ts +1 -0
- package/components/dropdown/dist/index.js +9 -1
- package/components/dropdown/dist/registered.js +9 -1
- package/components/form/demo/customize.html +6 -6
- package/components/form/demo/customize.js +19 -0
- package/components/form/demo/customize.md +203 -51
- package/components/form/demo/customize.min.js +707 -633
- package/components/form/demo/getting-started.min.js +635 -633
- package/components/form/demo/index.min.js +635 -633
- package/components/form/demo/registerDemoDeps.min.js +272 -596
- package/components/form/dist/auro-form.d.ts +122 -4
- package/components/form/dist/index.js +363 -37
- package/components/form/dist/registered.js +363 -37
- package/components/input/demo/customize.html +1 -2
- package/components/input/demo/customize.md +54 -53
- package/components/input/demo/customize.min.js +41 -147
- package/components/input/demo/getting-started.min.js +41 -147
- package/components/input/demo/index.min.js +41 -147
- package/components/input/dist/base-input.d.ts +1 -49
- package/components/input/dist/index.js +41 -147
- package/components/input/dist/registered.js +41 -147
- package/components/radio/demo/customize.min.js +1 -1
- package/components/radio/demo/getting-started.min.js +1 -1
- package/components/radio/demo/index.min.js +1 -1
- package/components/radio/dist/index.js +1 -1
- package/components/radio/dist/registered.js +1 -1
- package/components/select/demo/customize.html +1 -2
- package/components/select/demo/customize.min.js +10 -2
- package/components/select/demo/getting-started.min.js +10 -2
- package/components/select/demo/index.min.js +10 -2
- package/components/select/dist/index.js +10 -2
- package/components/select/dist/registered.js +10 -2
- package/custom-elements.json +1592 -1707
- package/package.json +1 -1
|
@@ -767,6 +767,8 @@ function navigateArrow(component, direction, options = {}) {
|
|
|
767
767
|
}
|
|
768
768
|
}
|
|
769
769
|
|
|
770
|
+
/* eslint-disable no-underscore-dangle */
|
|
771
|
+
|
|
770
772
|
/**
|
|
771
773
|
* Returns the clear button element from the active input's shadow
|
|
772
774
|
* DOM, if available.
|
|
@@ -802,11 +804,9 @@ function isClearBtnFocused(ctx, clearBtn = getClearBtn(ctx)) {
|
|
|
802
804
|
* @param {Object} menu - The menu component.
|
|
803
805
|
*/
|
|
804
806
|
function reconcileMenuIndex(menu) {
|
|
805
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
806
807
|
if (menu._index < 0 && menu.optionActive && menu.items) {
|
|
807
808
|
const idx = menu.items.indexOf(menu.optionActive);
|
|
808
809
|
if (idx >= 0) {
|
|
809
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
810
810
|
menu._index = idx;
|
|
811
811
|
}
|
|
812
812
|
}
|
|
@@ -4806,7 +4806,7 @@ let AuroHelpText$2 = class AuroHelpText extends LitElement {
|
|
|
4806
4806
|
}
|
|
4807
4807
|
};
|
|
4808
4808
|
|
|
4809
|
-
var formkitVersion$2 = '
|
|
4809
|
+
var formkitVersion$2 = '202606231948';
|
|
4810
4810
|
|
|
4811
4811
|
let AuroElement$2 = class AuroElement extends LitElement {
|
|
4812
4812
|
static get properties() {
|
|
@@ -5757,6 +5757,14 @@ class AuroDropdown extends AuroElement$2 {
|
|
|
5757
5757
|
};
|
|
5758
5758
|
this.addEventListener('keydown', this._bibTabHandler);
|
|
5759
5759
|
|
|
5760
|
+
// Suppress AuroFloatingUI's auto-hide-on-focus-loss while the
|
|
5761
|
+
// desktopModal trap owns focus management. Without this, the very
|
|
5762
|
+
// first focus move into the bib (from the RAF below) triggers
|
|
5763
|
+
// handleFocusLoss → hideBib, tearing down the trap before the
|
|
5764
|
+
// user can press Tab.
|
|
5765
|
+
this._priorNoHideOnFocusLoss = this.noHideOnThisFocusLoss;
|
|
5766
|
+
this.noHideOnThisFocusLoss = true;
|
|
5767
|
+
|
|
5760
5768
|
// Move initial focus into the bib content, matching FocusTrap behavior
|
|
5761
5769
|
requestAnimationFrame(() => {
|
|
5762
5770
|
const focusables = getFocusableElements(this.bibContent);
|
|
@@ -16520,6 +16528,7 @@ class AuroInputUtilities {
|
|
|
16520
16528
|
const maskOptions = this.getMaskOptions('date', normalizedFormat);
|
|
16521
16529
|
|
|
16522
16530
|
if (!(valueObject instanceof Date) || Number.isNaN(valueObject.getTime()) || !maskOptions || typeof maskOptions.format !== 'function') {
|
|
16531
|
+
console.debug('Invalid date object or mask options for formatting', { valueObject, maskOptions }); // eslint-disable-line no-console
|
|
16523
16532
|
return undefined;
|
|
16524
16533
|
}
|
|
16525
16534
|
|
|
@@ -16671,10 +16680,8 @@ class BaseInput extends AuroElement$1 {
|
|
|
16671
16680
|
this.layout = 'classic';
|
|
16672
16681
|
this.locale = 'en-US';
|
|
16673
16682
|
this.max = undefined;
|
|
16674
|
-
this._maxObject = undefined;
|
|
16675
16683
|
this.maxLength = undefined;
|
|
16676
16684
|
this.min = undefined;
|
|
16677
|
-
this._minObject = undefined;
|
|
16678
16685
|
this.minLength = undefined;
|
|
16679
16686
|
this.required = false;
|
|
16680
16687
|
this.onDark = false;
|
|
@@ -16682,7 +16689,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
16682
16689
|
this.size = 'lg';
|
|
16683
16690
|
this.shape = 'classic';
|
|
16684
16691
|
this.value = undefined;
|
|
16685
|
-
this._valueObject = undefined;
|
|
16686
16692
|
|
|
16687
16693
|
this._initializePrivateDefaults();
|
|
16688
16694
|
}
|
|
@@ -17222,7 +17228,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17222
17228
|
* @returns {Date|undefined}
|
|
17223
17229
|
*/
|
|
17224
17230
|
get valueObject() {
|
|
17225
|
-
return this.
|
|
17231
|
+
return this.value && dateFormatter.isValidDate(this.value) ? dateFormatter.stringToDateInstance(this.value) : undefined;
|
|
17226
17232
|
}
|
|
17227
17233
|
|
|
17228
17234
|
/**
|
|
@@ -17230,7 +17236,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17230
17236
|
* @returns {Date|undefined}
|
|
17231
17237
|
*/
|
|
17232
17238
|
get minObject() {
|
|
17233
|
-
return this.
|
|
17239
|
+
return this.min && dateFormatter.isValidDate(this.min) ? dateFormatter.stringToDateInstance(this.min) : undefined;
|
|
17234
17240
|
}
|
|
17235
17241
|
|
|
17236
17242
|
/**
|
|
@@ -17238,50 +17244,7 @@ class BaseInput extends AuroElement$1 {
|
|
|
17238
17244
|
* @returns {Date|undefined}
|
|
17239
17245
|
*/
|
|
17240
17246
|
get maxObject() {
|
|
17241
|
-
return this.
|
|
17242
|
-
}
|
|
17243
|
-
|
|
17244
|
-
/**
|
|
17245
|
-
* Parses a date string into a Date object when the corresponding `_*Object`
|
|
17246
|
-
* field hasn't been synced yet by `updated()`. Returns undefined when the
|
|
17247
|
-
* input type/format isn't a full date or the string is not a valid date.
|
|
17248
|
-
*
|
|
17249
|
-
* Why this exists: a parent (datepicker) can call `inputN.validate()` from
|
|
17250
|
-
* inside its own `updated()` before this input's `updated()` has run
|
|
17251
|
-
* `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
|
|
17252
|
-
* and range checks would otherwise silently no-op (flipping the result to
|
|
17253
|
-
* `valid` or `patternMismatch`).
|
|
17254
|
-
* @private
|
|
17255
|
-
* @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
|
|
17256
|
-
* @returns {Date|undefined}
|
|
17257
|
-
*/
|
|
17258
|
-
_computeDateObjectFallback(dateStr) {
|
|
17259
|
-
if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
|
|
17260
|
-
return undefined;
|
|
17261
|
-
}
|
|
17262
|
-
if (!dateFormatter.isValidDate(dateStr)) {
|
|
17263
|
-
return undefined;
|
|
17264
|
-
}
|
|
17265
|
-
return dateFormatter.stringToDateInstance(dateStr);
|
|
17266
|
-
}
|
|
17267
|
-
|
|
17268
|
-
/**
|
|
17269
|
-
* Internal setter for readonly date object properties.
|
|
17270
|
-
* @private
|
|
17271
|
-
* @param {'valueObject'|'minObject'|'maxObject'} propertyName - Public object property name.
|
|
17272
|
-
* @param {Date|undefined} propertyValue - Value to assign.
|
|
17273
|
-
* @returns {void}
|
|
17274
|
-
*/
|
|
17275
|
-
setDateObjectProperty(propertyName, propertyValue) {
|
|
17276
|
-
const internalPropertyName = `_${propertyName}`;
|
|
17277
|
-
const previousValue = this[internalPropertyName];
|
|
17278
|
-
|
|
17279
|
-
if (previousValue === propertyValue) {
|
|
17280
|
-
return;
|
|
17281
|
-
}
|
|
17282
|
-
|
|
17283
|
-
this[internalPropertyName] = propertyValue;
|
|
17284
|
-
this.requestUpdate(propertyName, previousValue);
|
|
17247
|
+
return this.max && dateFormatter.isValidDate(this.max) ? dateFormatter.stringToDateInstance(this.max) : undefined;
|
|
17285
17248
|
}
|
|
17286
17249
|
|
|
17287
17250
|
connectedCallback() {
|
|
@@ -17310,7 +17273,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17310
17273
|
format: this.format
|
|
17311
17274
|
});
|
|
17312
17275
|
this.configureDataForType();
|
|
17313
|
-
this.syncDateValues();
|
|
17314
17276
|
}
|
|
17315
17277
|
|
|
17316
17278
|
disconnectedCallback() {
|
|
@@ -17330,10 +17292,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17330
17292
|
this.wrapperElement.addEventListener('click', this.handleClick);
|
|
17331
17293
|
}
|
|
17332
17294
|
|
|
17333
|
-
// add attribute for query selectors when auro-input is registered under a custom name
|
|
17334
|
-
if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
|
|
17335
|
-
this.setAttribute('auro-input', '');
|
|
17336
|
-
}
|
|
17337
17295
|
this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
|
|
17338
17296
|
|
|
17339
17297
|
// use validity message override if declared when initializing the component
|
|
@@ -17344,7 +17302,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17344
17302
|
this.setCustomHelpTextMessage();
|
|
17345
17303
|
this.configureAutoFormatting();
|
|
17346
17304
|
this.configureDataForType();
|
|
17347
|
-
this.syncDateValues();
|
|
17348
17305
|
}
|
|
17349
17306
|
|
|
17350
17307
|
/**
|
|
@@ -17481,8 +17438,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17481
17438
|
this.configureDataForType();
|
|
17482
17439
|
}
|
|
17483
17440
|
|
|
17484
|
-
this.syncDateValues(changedProperties);
|
|
17485
|
-
|
|
17486
17441
|
if (changedProperties.has('value')) {
|
|
17487
17442
|
if (this.value && this.value.length > 0) {
|
|
17488
17443
|
this.hasValue = true;
|
|
@@ -17504,14 +17459,14 @@ class BaseInput extends AuroElement$1 {
|
|
|
17504
17459
|
|
|
17505
17460
|
if (formattedValue !== this.inputElement.value) {
|
|
17506
17461
|
this.skipNextProgrammaticInputEvent = true;
|
|
17507
|
-
if (this.maskInstance && this.type
|
|
17462
|
+
if (this.maskInstance && this.type !== 'date') {
|
|
17508
17463
|
// Route through the mask so its _value and el.value stay in lock-step
|
|
17509
17464
|
// (set value calls updateControl which writes el.value = displayValue).
|
|
17510
17465
|
// Writing el.value directly leaves the mask thinking displayValue is
|
|
17511
|
-
// stale; _saveSelection on the next focus/click then warns.
|
|
17512
|
-
//
|
|
17513
|
-
//
|
|
17514
|
-
//
|
|
17466
|
+
// stale; _saveSelection on the next focus/click then warns. Date is
|
|
17467
|
+
// excluded because its formattedValue can be raw ISO when the calendar
|
|
17468
|
+
// is invalid, and re-masking through mm/dd/yyyy would truncate it and
|
|
17469
|
+
// flip validity from patternMismatch to tooShort.
|
|
17515
17470
|
this.maskInstance.value = formattedValue || '';
|
|
17516
17471
|
} else if (formattedValue) {
|
|
17517
17472
|
this.inputElement.value = formattedValue;
|
|
@@ -17553,120 +17508,65 @@ class BaseInput extends AuroElement$1 {
|
|
|
17553
17508
|
}));
|
|
17554
17509
|
}
|
|
17555
17510
|
|
|
17556
|
-
|
|
17557
|
-
/**
|
|
17558
|
-
* Synchronizes the ISO string values and Date object representations for date-related properties.
|
|
17559
|
-
* This keeps the model and display values aligned when either side changes.
|
|
17560
|
-
*
|
|
17561
|
-
* When a full date format is in use, this method updates `value`, `min`, and `max` from their corresponding
|
|
17562
|
-
* Date objects or vice versa, based on which properties have changed. It only runs when the current configuration
|
|
17563
|
-
* represents a full year/month/day date format.
|
|
17564
|
-
*
|
|
17565
|
-
* @param {Map<string, unknown>|undefined} [changedProperties=undefined] - Optional map of changed properties used to limit which values are synchronized.
|
|
17566
|
-
* @returns {void}
|
|
17567
|
-
* @private
|
|
17568
|
-
*/
|
|
17569
|
-
syncDateValues(changedProperties = undefined) {
|
|
17570
|
-
if (!this.util.isFullDateFormat(this.type, this.format)) {
|
|
17571
|
-
return;
|
|
17572
|
-
}
|
|
17573
|
-
|
|
17574
|
-
this.syncSingleDateValue(changedProperties, 'valueObject', 'value');
|
|
17575
|
-
this.syncSingleDateValue(changedProperties, 'minObject', 'min');
|
|
17576
|
-
this.syncSingleDateValue(changedProperties, 'maxObject', 'max');
|
|
17577
|
-
}
|
|
17578
|
-
|
|
17579
|
-
/**
|
|
17580
|
-
* Synchronizes one date object/string property pair.
|
|
17581
|
-
* @private
|
|
17582
|
-
* @param {Map<string, unknown>|undefined} changedProperties - Map of changed properties from Lit.
|
|
17583
|
-
* @param {string} objectProperty - Date object property name.
|
|
17584
|
-
* @param {string} valueProperty - ISO string property name.
|
|
17585
|
-
* @returns {void}
|
|
17586
|
-
*/
|
|
17587
|
-
syncSingleDateValue(changedProperties, objectProperty, valueProperty) {
|
|
17588
|
-
const objectPropertyChanged = !changedProperties || changedProperties.has(objectProperty);
|
|
17589
|
-
|
|
17590
|
-
// objectProperty wins over valueProperty when both changed
|
|
17591
|
-
if (objectPropertyChanged && this[objectProperty]) {
|
|
17592
|
-
this[valueProperty] = dateFormatter.toISOFormatString(this[objectProperty]);
|
|
17593
|
-
return;
|
|
17594
|
-
}
|
|
17595
|
-
|
|
17596
|
-
const valuePropertyChanged = !changedProperties || changedProperties.has(valueProperty);
|
|
17597
|
-
if (!valuePropertyChanged) {
|
|
17598
|
-
return;
|
|
17599
|
-
}
|
|
17600
|
-
|
|
17601
|
-
// 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)
|
|
17602
|
-
if (
|
|
17603
|
-
changedProperties &&
|
|
17604
|
-
valueProperty === 'value' &&
|
|
17605
|
-
changedProperties.get('value') === undefined &&
|
|
17606
|
-
this[objectProperty] instanceof Date &&
|
|
17607
|
-
this[valueProperty] === dateFormatter.toISOFormatString(this[objectProperty])
|
|
17608
|
-
) {
|
|
17609
|
-
return;
|
|
17610
|
-
}
|
|
17611
|
-
|
|
17612
|
-
if (dateFormatter.isValidDate(this[valueProperty])) {
|
|
17613
|
-
this.setDateObjectProperty(objectProperty, dateFormatter.stringToDateInstance(this[valueProperty]));
|
|
17614
|
-
} else {
|
|
17615
|
-
this.setDateObjectProperty(objectProperty, undefined);
|
|
17616
|
-
}
|
|
17617
|
-
}
|
|
17618
|
-
|
|
17619
17511
|
/**
|
|
17620
17512
|
* Sets up IMasks and logic based on auto-formatting requirements.
|
|
17621
17513
|
* @private
|
|
17622
17514
|
* @returns {void}
|
|
17623
17515
|
*/
|
|
17624
17516
|
configureAutoFormatting() {
|
|
17625
|
-
//
|
|
17626
|
-
//
|
|
17627
|
-
//
|
|
17628
|
-
//
|
|
17517
|
+
// _configuringMask gates two things: external re-entry into this method
|
|
17518
|
+
// while setup is mid-flight (from property changes that call back here),
|
|
17519
|
+
// and the accept/complete listeners below — both need to ignore the mask
|
|
17520
|
+
// events fired by our own value-restore step.
|
|
17629
17521
|
if (this._configuringMask) return;
|
|
17630
17522
|
this._configuringMask = true;
|
|
17631
17523
|
try {
|
|
17524
|
+
// Destroy any prior mask so IMask can attach fresh under the new format.
|
|
17525
|
+
// Null the reference too — if the new maskOptions.mask is falsy (e.g.
|
|
17526
|
+
// type switched from credit-card to text) the IMask() reassignment
|
|
17527
|
+
// below is skipped, and downstream writes at line ~823 would otherwise
|
|
17528
|
+
// route through the destroyed instance.
|
|
17632
17529
|
if (this.maskInstance) {
|
|
17633
17530
|
this.maskInstance.destroy();
|
|
17531
|
+
this.maskInstance = null;
|
|
17634
17532
|
}
|
|
17635
17533
|
|
|
17636
|
-
// Pass new format to util
|
|
17637
17534
|
this.util.updateFormat(this.format);
|
|
17638
17535
|
|
|
17639
17536
|
const maskOptions = this.util.getMaskOptions(this.type, this.format);
|
|
17640
17537
|
|
|
17641
17538
|
if (this.inputElement && maskOptions.mask) {
|
|
17642
|
-
|
|
17643
|
-
//
|
|
17644
|
-
//
|
|
17645
|
-
//
|
|
17646
|
-
// When the format changes (e.g. locale switch) and we have a valid ISO
|
|
17647
|
-
// model value, compute the display string for the NEW format instead of
|
|
17648
|
-
// re-using the old display string, which may be invalid in the new mask.
|
|
17539
|
+
// Capture the current display so it can be re-applied after IMask
|
|
17540
|
+
// attaches. The restore at the bottom goes through maskInstance.value
|
|
17541
|
+
// (not inputElement.value directly) so the mask's internal state and
|
|
17542
|
+
// the input's displayed text stay in lock-step.
|
|
17649
17543
|
let existingValue = this.inputElement.value;
|
|
17544
|
+
|
|
17545
|
+
// Format-change case (e.g. locale switch): existingValue is the OLD
|
|
17546
|
+
// mask's display string and may not parse under the new mask. When
|
|
17547
|
+
// we have a valid date model, rebuild the display from valueObject
|
|
17548
|
+
// (the canonical source) using the new mask's format function.
|
|
17650
17549
|
if (
|
|
17651
17550
|
this.util.isFullDateFormat(this.type, this.format) &&
|
|
17652
17551
|
this.value &&
|
|
17653
|
-
|
|
17654
|
-
this.valueObject instanceof Date &&
|
|
17655
|
-
!Number.isNaN(this.valueObject.getTime()) &&
|
|
17552
|
+
this.valueObject &&
|
|
17656
17553
|
typeof maskOptions.format === 'function'
|
|
17657
17554
|
) {
|
|
17658
17555
|
existingValue = maskOptions.format(this.valueObject);
|
|
17659
17556
|
}
|
|
17660
17557
|
|
|
17558
|
+
// Clear before IMask attaches so the constructor seeds an empty
|
|
17559
|
+
// internal value. Otherwise IMask reads the stale unmasked string
|
|
17560
|
+
// and emits a spurious 'accept' before the restore below runs.
|
|
17661
17561
|
this.skipNextProgrammaticInputEvent = true;
|
|
17662
17562
|
this.inputElement.value = '';
|
|
17663
17563
|
|
|
17664
17564
|
this.maskInstance = IMask(this.inputElement, maskOptions);
|
|
17665
17565
|
|
|
17566
|
+
// Mask fires 'accept' on every value change, including the restore
|
|
17567
|
+
// step below. Skip events fired during configureAutoFormatting so
|
|
17568
|
+
// we don't overwrite a value the parent just pushed.
|
|
17666
17569
|
this.maskInstance.on('accept', () => {
|
|
17667
|
-
// Suppress propagation during configureAutoFormatting's own value-restoration
|
|
17668
|
-
// (line below) — the mask emits 'accept' on every value-set, including ours,
|
|
17669
|
-
// and we don't want to overwrite a value the parent just pushed.
|
|
17670
17570
|
if (this._configuringMask) return;
|
|
17671
17571
|
this.value = this.util.toModelValue(this.maskInstance.value, this.format);
|
|
17672
17572
|
if (this.type === "date") {
|
|
@@ -17674,6 +17574,8 @@ class BaseInput extends AuroElement$1 {
|
|
|
17674
17574
|
}
|
|
17675
17575
|
});
|
|
17676
17576
|
|
|
17577
|
+
// Mask fires 'complete' on the restore step below for any value that
|
|
17578
|
+
// happens to be a complete match. Same setup-suppression as 'accept'.
|
|
17677
17579
|
this.maskInstance.on('complete', () => {
|
|
17678
17580
|
if (this._configuringMask) return;
|
|
17679
17581
|
this.value = this.util.toModelValue(this.maskInstance.value, this.format);
|
|
@@ -17682,7 +17584,9 @@ class BaseInput extends AuroElement$1 {
|
|
|
17682
17584
|
}
|
|
17683
17585
|
});
|
|
17684
17586
|
|
|
17685
|
-
//
|
|
17587
|
+
// Write existingValue through the mask (not the input directly) so
|
|
17588
|
+
// the mask reformats it under the new rules and keeps its internal
|
|
17589
|
+
// _value aligned with the input's displayed text.
|
|
17686
17590
|
if (existingValue) {
|
|
17687
17591
|
this.maskInstance.value = existingValue;
|
|
17688
17592
|
}
|
|
@@ -17850,7 +17754,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17850
17754
|
*/
|
|
17851
17755
|
reset() {
|
|
17852
17756
|
this.value = undefined;
|
|
17853
|
-
this.setDateObjectProperty('valueObject', undefined);
|
|
17854
17757
|
this.validation.reset(this);
|
|
17855
17758
|
}
|
|
17856
17759
|
|
|
@@ -17859,7 +17762,6 @@ class BaseInput extends AuroElement$1 {
|
|
|
17859
17762
|
*/
|
|
17860
17763
|
clear() {
|
|
17861
17764
|
this.value = undefined;
|
|
17862
|
-
this.setDateObjectProperty('valueObject', undefined);
|
|
17863
17765
|
}
|
|
17864
17766
|
|
|
17865
17767
|
/**
|
|
@@ -18388,7 +18290,7 @@ let AuroHelpText$1 = class AuroHelpText extends LitElement {
|
|
|
18388
18290
|
}
|
|
18389
18291
|
};
|
|
18390
18292
|
|
|
18391
|
-
var formkitVersion$1 = '
|
|
18293
|
+
var formkitVersion$1 = '202606231948';
|
|
18392
18294
|
|
|
18393
18295
|
// Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
|
|
18394
18296
|
// See LICENSE in the project root for license information.
|
|
@@ -19509,7 +19411,7 @@ class AuroBibtemplate extends LitElement {
|
|
|
19509
19411
|
}
|
|
19510
19412
|
}
|
|
19511
19413
|
|
|
19512
|
-
var formkitVersion = '
|
|
19414
|
+
var formkitVersion = '202606231948';
|
|
19513
19415
|
|
|
19514
19416
|
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}`;
|
|
19515
19417
|
|
|
@@ -20496,7 +20398,7 @@ class AuroCombobox extends AuroElement {
|
|
|
20496
20398
|
if (this.menu) {
|
|
20497
20399
|
this.menu.matchWord = normalizeFilterValue(this.input.value);
|
|
20498
20400
|
}
|
|
20499
|
-
const label = getOptionLabel(this.menu.optionSelected)
|
|
20401
|
+
const label = getOptionLabel(this.menu.optionSelected);
|
|
20500
20402
|
this.updateTriggerTextDisplay(label || this.value);
|
|
20501
20403
|
}
|
|
20502
20404
|
|
|
@@ -20510,40 +20412,9 @@ class AuroCombobox extends AuroElement {
|
|
|
20510
20412
|
// in suggestion mode, do not override input value if no selection has been made and the input currently has focus
|
|
20511
20413
|
const isInputFocusedWithNoSelection = !this.menu.value && (this.input.matches(':focus-within') || (this.inputInBib && this.inputInBib.matches(':focus-within')));
|
|
20512
20414
|
|
|
20513
|
-
|
|
20514
|
-
|
|
20515
|
-
|
|
20516
|
-
// syncValuesAndStates re-enters here during typing when both inputs
|
|
20517
|
-
// already match, and a no-op flag flip would make the bib branch's
|
|
20518
|
-
// bail eat legitimate user-input events.
|
|
20519
|
-
const triggerNeedsSync = this.input.value !== nextValue;
|
|
20520
|
-
const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
|
|
20521
|
-
if (triggerNeedsSync || bibNeedsSync) {
|
|
20522
|
-
this._syncingDisplayValue = true;
|
|
20523
|
-
if (triggerNeedsSync) {
|
|
20524
|
-
this.input.value = nextValue;
|
|
20525
|
-
}
|
|
20526
|
-
if (bibNeedsSync) {
|
|
20527
|
-
this.inputInBib.value = nextValue;
|
|
20528
|
-
}
|
|
20529
|
-
const pending = [];
|
|
20530
|
-
if (triggerNeedsSync) {
|
|
20531
|
-
pending.push(this.input.updateComplete);
|
|
20532
|
-
}
|
|
20533
|
-
if (bibNeedsSync) {
|
|
20534
|
-
pending.push(this.inputInBib.updateComplete);
|
|
20535
|
-
}
|
|
20536
|
-
Promise.all(pending).then(() => {
|
|
20537
|
-
// imask reasserts otherwise, and handleBlur reverts to its stale value.
|
|
20538
|
-
if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
|
|
20539
|
-
this.input.maskInstance.updateValue();
|
|
20540
|
-
}
|
|
20541
|
-
if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
|
|
20542
|
-
this.inputInBib.maskInstance.updateValue();
|
|
20543
|
-
}
|
|
20544
|
-
this._syncingDisplayValue = false;
|
|
20545
|
-
});
|
|
20546
|
-
}
|
|
20415
|
+
const suppressed = this.persistInput || (this.behavior === 'suggestion' && isInputFocusedWithNoSelection);
|
|
20416
|
+
if (!suppressed) {
|
|
20417
|
+
this.syncInputValuesAcrossTriggerAndBib(label || this.value);
|
|
20547
20418
|
}
|
|
20548
20419
|
|
|
20549
20420
|
// update the displayValue in the trigger if displayValue slot content is present
|
|
@@ -20571,6 +20442,48 @@ class AuroCombobox extends AuroElement {
|
|
|
20571
20442
|
this.requestUpdate();
|
|
20572
20443
|
}
|
|
20573
20444
|
|
|
20445
|
+
/**
|
|
20446
|
+
* Writes nextValue to the trigger input and the bib input when their current
|
|
20447
|
+
* value differs, then re-asserts imask after Lit's update flushes.
|
|
20448
|
+
* @param {string} nextValue - The value to write to both inputs.
|
|
20449
|
+
* @returns {Promise<void> | null} Promise that resolves after the inputs flush,
|
|
20450
|
+
* or null when no sync was needed (so callers can skip post-flush work).
|
|
20451
|
+
* @private
|
|
20452
|
+
*/
|
|
20453
|
+
syncInputValuesAcrossTriggerAndBib(nextValue) {
|
|
20454
|
+
// Only set the flag when there's an actual write to suppress —
|
|
20455
|
+
// syncValuesAndStates re-enters here during typing when both inputs
|
|
20456
|
+
// already match, and a no-op flag flip would make the bib branch's
|
|
20457
|
+
// bail eat legitimate user-input events.
|
|
20458
|
+
const triggerNeedsSync = this.input.value !== nextValue;
|
|
20459
|
+
const bibNeedsSync = this.inputInBib.value !== nextValue;
|
|
20460
|
+
if (!triggerNeedsSync && !bibNeedsSync) {
|
|
20461
|
+
return null;
|
|
20462
|
+
}
|
|
20463
|
+
|
|
20464
|
+
this._syncingDisplayValue = true;
|
|
20465
|
+
|
|
20466
|
+
const pending = [];
|
|
20467
|
+
if (triggerNeedsSync) {
|
|
20468
|
+
this.input.value = nextValue;
|
|
20469
|
+
pending.push(this.input.updateComplete);
|
|
20470
|
+
}
|
|
20471
|
+
if (bibNeedsSync) {
|
|
20472
|
+
this.inputInBib.value = nextValue;
|
|
20473
|
+
pending.push(this.inputInBib.updateComplete);
|
|
20474
|
+
}
|
|
20475
|
+
return Promise.all(pending).then(() => {
|
|
20476
|
+
// imask reasserts otherwise, and handleBlur reverts to its stale value.
|
|
20477
|
+
if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
|
|
20478
|
+
this.input.maskInstance.updateValue();
|
|
20479
|
+
}
|
|
20480
|
+
if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
|
|
20481
|
+
this.inputInBib.maskInstance.updateValue();
|
|
20482
|
+
}
|
|
20483
|
+
this._syncingDisplayValue = false;
|
|
20484
|
+
});
|
|
20485
|
+
}
|
|
20486
|
+
|
|
20574
20487
|
/**
|
|
20575
20488
|
* Processes hidden state of all menu options and determines if there are any available options not hidden.
|
|
20576
20489
|
* @private
|
|
@@ -20598,9 +20511,9 @@ class AuroCombobox extends AuroElement {
|
|
|
20598
20511
|
this.syncValuesAndStates();
|
|
20599
20512
|
}
|
|
20600
20513
|
|
|
20601
|
-
// Re-activate when optionActive is
|
|
20602
|
-
// been reset (e.g. by clearSelection in an async update)
|
|
20603
|
-
// makeSelection() can find the correct option.
|
|
20514
|
+
// Re-activate when optionActive is not in the current scrollable viewport,
|
|
20515
|
+
// or when _index has been reset (e.g. by clearSelection in an async update)
|
|
20516
|
+
// so that makeSelection() can find the correct option.
|
|
20604
20517
|
if (!this.availableOptions.includes(this.menu.optionActive) || this.menu._index < 0) {
|
|
20605
20518
|
this.activateFirstEnabledAvailableOption();
|
|
20606
20519
|
}
|
|
@@ -20734,28 +20647,25 @@ class AuroCombobox extends AuroElement {
|
|
|
20734
20647
|
|
|
20735
20648
|
guardTouchPassthrough(this.menu);
|
|
20736
20649
|
|
|
20737
|
-
//
|
|
20738
|
-
//
|
|
20739
|
-
//
|
|
20650
|
+
// showModal() takes focus away from the trigger. Early focus once
|
|
20651
|
+
// the dialog has painted; the shared doubleRaf below is the fallback
|
|
20652
|
+
// for when the dialog needs an extra frame and also clears the
|
|
20653
|
+
// validation guard.
|
|
20740
20654
|
requestAnimationFrame(() => {
|
|
20741
20655
|
this.setInputFocus();
|
|
20742
20656
|
});
|
|
20743
|
-
|
|
20744
|
-
doubleRaf(() => {
|
|
20745
|
-
this.setInputFocus();
|
|
20746
|
-
this._inFullscreenTransition = false;
|
|
20747
|
-
});
|
|
20748
|
-
} else {
|
|
20749
|
-
// Desktop popover-open: restore the trigger caret to end-of-text.
|
|
20750
|
-
// Clicking the trigger lands on auro-input's floating <label for="…">
|
|
20751
|
-
// overlay; Chrome resets the native input's selection to [0, 0] on
|
|
20752
|
-
// label-focus before any JS runs. setInputFocus()'s non-fullscreen
|
|
20753
|
-
// branch parks the caret at end. doubleRaf lets the dropdown layout
|
|
20754
|
-
// settle first, matching the fullscreen branch timing.
|
|
20755
|
-
doubleRaf(() => {
|
|
20756
|
-
this.setInputFocus();
|
|
20757
|
-
});
|
|
20758
20657
|
}
|
|
20658
|
+
// else (desktop popover-open): Chrome resets the trigger caret to
|
|
20659
|
+
// [0, 0] when its floating <label for="…"> overlay receives focus.
|
|
20660
|
+
// The shared doubleRaf below parks the caret back at end-of-text
|
|
20661
|
+
// after the dropdown layout settles.
|
|
20662
|
+
|
|
20663
|
+
doubleRaf(() => {
|
|
20664
|
+
this.setInputFocus();
|
|
20665
|
+
if (this._inFullscreenTransition) {
|
|
20666
|
+
this._inFullscreenTransition = false;
|
|
20667
|
+
}
|
|
20668
|
+
});
|
|
20759
20669
|
}
|
|
20760
20670
|
});
|
|
20761
20671
|
|
|
@@ -20860,24 +20770,7 @@ class AuroCombobox extends AuroElement {
|
|
|
20860
20770
|
nativeInput.setSelectionRange(len, len);
|
|
20861
20771
|
}
|
|
20862
20772
|
} else {
|
|
20863
|
-
|
|
20864
|
-
// fullscreen→floating switch can close the dialog and leave focus on
|
|
20865
|
-
// body. Every other Branch 2 callsite already has focus on the input
|
|
20866
|
-
// via the focusin → this.focus() redirect (L1448-1452), so this is a
|
|
20867
|
-
// no-op in the common case.
|
|
20868
|
-
if (!this.input.componentHasFocus) {
|
|
20869
|
-
this.input.focus();
|
|
20870
|
-
}
|
|
20871
|
-
|
|
20872
|
-
// Park the trigger native input's caret at end-of-text every time the
|
|
20873
|
-
// popover opens. Chrome focuses the native via the floating <label for="…">
|
|
20874
|
-
// overlay on click and resets selection to [0, 0]; no JS fires between
|
|
20875
|
-
// mousedown and selectionchange, so we restore the caret here.
|
|
20876
|
-
const triggerNativeInput = this.input.inputElement;
|
|
20877
|
-
if (triggerNativeInput && triggerNativeInput.value) {
|
|
20878
|
-
const len = triggerNativeInput.value.length;
|
|
20879
|
-
triggerNativeInput.setSelectionRange(len, len);
|
|
20880
|
-
}
|
|
20773
|
+
this.input.focus();
|
|
20881
20774
|
}
|
|
20882
20775
|
}
|
|
20883
20776
|
|
|
@@ -20983,7 +20876,7 @@ class AuroCombobox extends AuroElement {
|
|
|
20983
20876
|
if (this.menu.optionSelected) {
|
|
20984
20877
|
const selected = this.menu.optionSelected;
|
|
20985
20878
|
|
|
20986
|
-
if (
|
|
20879
|
+
if (this.optionSelected !== selected) {
|
|
20987
20880
|
this.optionSelected = selected;
|
|
20988
20881
|
}
|
|
20989
20882
|
|
|
@@ -20996,7 +20889,7 @@ class AuroCombobox extends AuroElement {
|
|
|
20996
20889
|
}
|
|
20997
20890
|
|
|
20998
20891
|
// Update display
|
|
20999
|
-
this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected)
|
|
20892
|
+
this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected));
|
|
21000
20893
|
|
|
21001
20894
|
// Update match word for filtering
|
|
21002
20895
|
const trimmedInput = normalizeFilterValue(this.input.value);
|
|
@@ -21012,13 +20905,22 @@ class AuroCombobox extends AuroElement {
|
|
|
21012
20905
|
this.hideBib();
|
|
21013
20906
|
}
|
|
21014
20907
|
|
|
20908
|
+
// Move focus to the clear button when the user makes a selection.
|
|
20909
|
+
if (!isEcho && this.menu.value !== undefined) {
|
|
20910
|
+
this.setClearBtnFocus();
|
|
20911
|
+
}
|
|
20912
|
+
|
|
21015
20913
|
// Announce the selection after the dropdown closes so it isn't
|
|
21016
20914
|
// overridden by VoiceOver's "collapsed" announcement from aria-expanded.
|
|
20915
|
+
// Skip when there's no selected value (e.g. menu.clearSelection() from
|
|
20916
|
+
// the unmatched-value path), otherwise VoiceOver reads "undefined".
|
|
21017
20917
|
const selectedValue = this.menu.value;
|
|
21018
|
-
|
|
21019
|
-
|
|
21020
|
-
|
|
21021
|
-
|
|
20918
|
+
if (selectedValue) {
|
|
20919
|
+
const announcementDelay = 300;
|
|
20920
|
+
setTimeout(() => {
|
|
20921
|
+
announceToScreenReader(this._getAnnouncementRoot(), `${selectedValue}, selected`);
|
|
20922
|
+
}, announcementDelay);
|
|
20923
|
+
}
|
|
21022
20924
|
}
|
|
21023
20925
|
|
|
21024
20926
|
// Programmatic value syncs leave availableOptions stale because
|
|
@@ -21032,18 +20934,6 @@ class AuroCombobox extends AuroElement {
|
|
|
21032
20934
|
this._programmaticFilterRefresh = false;
|
|
21033
20935
|
}, 0);
|
|
21034
20936
|
}
|
|
21035
|
-
|
|
21036
|
-
// base-input skips auto-validate when focus is inside its own shadow,
|
|
21037
|
-
// which it is after setTriggerInputFocus — re-run so prior tooShort
|
|
21038
|
-
// clears. processCreditCard reasserts errorMessage on every render.
|
|
21039
|
-
if (!isEcho && this.menu.optionSelected && this.input.validate) {
|
|
21040
|
-
this.input.updateComplete.then(() => {
|
|
21041
|
-
this.input.validate(true);
|
|
21042
|
-
if (this.input.validity === 'valid') {
|
|
21043
|
-
this.input.errorMessage = '';
|
|
21044
|
-
}
|
|
21045
|
-
});
|
|
21046
|
-
}
|
|
21047
20937
|
});
|
|
21048
20938
|
|
|
21049
20939
|
this.menu.addEventListener('auroMenu-customEventFired', () => {
|
|
@@ -21378,15 +21268,7 @@ class AuroCombobox extends AuroElement {
|
|
|
21378
21268
|
this.menu.value = undefined;
|
|
21379
21269
|
this.validation.reset(this);
|
|
21380
21270
|
this.touched = false;
|
|
21381
|
-
// Force validity back to the cleared state. validation.reset() calls
|
|
21382
|
-
// validate() at the end, and (post-credit-card-fix) the trigger input's
|
|
21383
|
-
// residual validity may still be copied into the combobox by the
|
|
21384
|
-
// auroInputElements loop. Explicitly clear here so reset() actually
|
|
21385
|
-
// leaves the component in a "no validity" state.
|
|
21386
21271
|
this.validity = undefined;
|
|
21387
|
-
this.errorMessage = '';
|
|
21388
|
-
this.input.validity = undefined;
|
|
21389
|
-
this.input.errorMessage = '';
|
|
21390
21272
|
}
|
|
21391
21273
|
|
|
21392
21274
|
/**
|
|
@@ -21439,52 +21321,27 @@ class AuroCombobox extends AuroElement {
|
|
|
21439
21321
|
this.input.value = this.value;
|
|
21440
21322
|
}
|
|
21441
21323
|
|
|
21442
|
-
// Sync menu.value only when an option actually matches
|
|
21324
|
+
// Sync menu.value only when an option actually matches, or when filter
|
|
21325
|
+
// mode needs setMenuValue to dispatch auroMenu-selectValueFailure (the
|
|
21326
|
+
// listener at line 1206 clears combobox.value, mirroring select's
|
|
21327
|
+
// pattern). Otherwise sync the input display for freeform values.
|
|
21443
21328
|
if (this.menu.options && this.menu.options.length > 0) {
|
|
21444
|
-
if (this.menu.options.some((opt) => opt.value === this.value)) {
|
|
21445
|
-
this.setMenuValue(this.value);
|
|
21446
|
-
} else if (this.behavior === 'filter') {
|
|
21447
|
-
// In filter mode, freeform values aren't allowed. Push the unmatched
|
|
21448
|
-
// value through setMenuValue so menu dispatches auroMenu-selectValueFailure
|
|
21449
|
-
// and the listener at line 1206 clears combobox.value (mirrors select's
|
|
21450
|
-
// pattern). Bypassing setMenuValue here would skip the failure cascade.
|
|
21329
|
+
if (this.menu.options.some((opt) => opt.value === this.value) || this.behavior === 'filter') {
|
|
21451
21330
|
this.setMenuValue(this.value);
|
|
21452
21331
|
} else {
|
|
21453
|
-
|
|
21454
|
-
|
|
21332
|
+
// Clear menu.value AND menu.optionSelected together. Clearing only
|
|
21333
|
+
// menu.value leaves the previously-selected option element pinned
|
|
21334
|
+
// as menu.optionSelected; a later auroMenu-selectedOption event
|
|
21335
|
+
// would then write its stale .value back into combobox.value.
|
|
21336
|
+
if (this.menu.value || this.menu.optionSelected) {
|
|
21337
|
+
this.menu.clearSelection();
|
|
21455
21338
|
}
|
|
21456
|
-
//
|
|
21457
|
-
//
|
|
21458
|
-
//
|
|
21459
|
-
|
|
21460
|
-
|
|
21461
|
-
|
|
21462
|
-
const triggerNeedsSync = this.input.value !== nextValue;
|
|
21463
|
-
const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
|
|
21464
|
-
if (triggerNeedsSync || bibNeedsSync) {
|
|
21465
|
-
this._syncingDisplayValue = true;
|
|
21466
|
-
if (triggerNeedsSync) {
|
|
21467
|
-
this.input.value = nextValue;
|
|
21468
|
-
}
|
|
21469
|
-
if (bibNeedsSync) {
|
|
21470
|
-
this.inputInBib.value = nextValue;
|
|
21471
|
-
}
|
|
21472
|
-
const pending = [];
|
|
21473
|
-
if (triggerNeedsSync) {
|
|
21474
|
-
pending.push(this.input.updateComplete);
|
|
21475
|
-
}
|
|
21476
|
-
if (bibNeedsSync) {
|
|
21477
|
-
pending.push(this.inputInBib.updateComplete);
|
|
21478
|
-
}
|
|
21479
|
-
Promise.all(pending).then(() => {
|
|
21480
|
-
if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
|
|
21481
|
-
this.input.maskInstance.updateValue();
|
|
21482
|
-
}
|
|
21483
|
-
if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
|
|
21484
|
-
this.inputInBib.maskInstance.updateValue();
|
|
21485
|
-
}
|
|
21486
|
-
this._syncingDisplayValue = false;
|
|
21487
|
-
// handleInputValueChange bailed on the flag — refresh the filter.
|
|
21339
|
+
// Suggestion-mode freeform value: sync the trigger + bib to show it,
|
|
21340
|
+
// then refresh the filter once the inputs flush (handleInputValueChange
|
|
21341
|
+
// bailed on _syncingDisplayValue).
|
|
21342
|
+
const syncPromise = this.syncInputValuesAcrossTriggerAndBib(this.value || '');
|
|
21343
|
+
if (syncPromise) {
|
|
21344
|
+
syncPromise.then(() => {
|
|
21488
21345
|
this._programmaticFilterRefresh = true;
|
|
21489
21346
|
this.handleMenuOptions();
|
|
21490
21347
|
setTimeout(() => {
|