@aurodesignsystem-dev/auro-formkit 0.0.0-pr1503.2 → 0.0.0-pr1505.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.
Files changed (62) hide show
  1. package/components/checkbox/demo/customize.min.js +2 -2
  2. package/components/checkbox/demo/getting-started.min.js +2 -2
  3. package/components/checkbox/demo/index.min.js +2 -2
  4. package/components/checkbox/dist/index.js +2 -2
  5. package/components/checkbox/dist/registered.js +2 -2
  6. package/components/combobox/demo/customize.md +7 -3
  7. package/components/combobox/demo/customize.min.js +1450 -1662
  8. package/components/combobox/demo/getting-started.min.js +1450 -1662
  9. package/components/combobox/demo/index.min.js +1450 -1662
  10. package/components/combobox/dist/auro-combobox.d.ts +5 -0
  11. package/components/combobox/dist/index.js +504 -141
  12. package/components/combobox/dist/registered.js +504 -141
  13. package/components/counter/demo/customize.min.js +3 -3
  14. package/components/counter/demo/index.min.js +3 -3
  15. package/components/counter/dist/index.js +3 -3
  16. package/components/counter/dist/registered.js +3 -3
  17. package/components/datepicker/demo/api.md +2 -0
  18. package/components/datepicker/demo/customize.md +195 -33
  19. package/components/datepicker/demo/customize.min.js +145 -60
  20. package/components/datepicker/demo/index.min.js +137 -59
  21. package/components/datepicker/dist/index.js +137 -59
  22. package/components/datepicker/dist/registered.js +137 -59
  23. package/components/datepicker/dist/src/auro-datepicker.d.ts +2 -0
  24. package/components/dropdown/demo/customize.min.js +1 -1
  25. package/components/dropdown/demo/getting-started.min.js +1 -1
  26. package/components/dropdown/demo/index.min.js +1 -1
  27. package/components/dropdown/dist/index.js +1 -1
  28. package/components/dropdown/dist/registered.js +1 -1
  29. package/components/form/demo/customize.min.js +2209 -2237
  30. package/components/form/demo/getting-started.min.js +2209 -2237
  31. package/components/form/demo/index.min.js +2209 -2237
  32. package/components/form/demo/registerDemoDeps.min.js +2207 -2235
  33. package/components/input/demo/customize.md +56 -55
  34. package/components/input/demo/customize.min.js +131 -56
  35. package/components/input/demo/getting-started.min.js +131 -56
  36. package/components/input/demo/index.md +2 -2
  37. package/components/input/demo/index.min.js +131 -56
  38. package/components/input/dist/base-input.d.ts +16 -0
  39. package/components/input/dist/index.js +131 -56
  40. package/components/input/dist/registered.js +131 -56
  41. package/components/menu/demo/api.md +41 -45
  42. package/components/menu/demo/customize.md +0 -28
  43. package/components/menu/demo/index.min.js +779 -1354
  44. package/components/menu/dist/auro-menu.d.ts +95 -110
  45. package/components/menu/dist/auro-menuoption.d.ts +32 -138
  46. package/components/menu/dist/index.js +753 -1308
  47. package/components/menu/dist/registered.js +765 -1308
  48. package/components/radio/demo/customize.min.js +2 -2
  49. package/components/radio/demo/getting-started.min.js +2 -2
  50. package/components/radio/demo/index.min.js +2 -2
  51. package/components/radio/dist/index.js +2 -2
  52. package/components/radio/dist/registered.js +2 -2
  53. package/components/select/demo/customize.md +7 -3
  54. package/components/select/demo/customize.min.js +1072 -1616
  55. package/components/select/demo/getting-started.min.js +1072 -1616
  56. package/components/select/demo/index.min.js +1072 -1616
  57. package/components/select/dist/index.js +46 -15
  58. package/components/select/dist/registered.js +46 -15
  59. package/components/select/dist/selectUtils.d.ts +12 -0
  60. package/custom-elements.json +384 -1022
  61. package/package.json +1 -3
  62. package/components/menu/dist/auro-menu.context.d.ts +0 -238
@@ -4490,7 +4490,7 @@ class AuroFormValidation {
4490
4490
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-input') && elem.errorMessage === '') {
4491
4491
  const input = elem.renderRoot.querySelector('input');
4492
4492
 
4493
- if (input.validationMessage.length > 0) {
4493
+ if (input && input.validationMessage.length > 0) {
4494
4494
  elem.errorMessage = input.validationMessage;
4495
4495
  }
4496
4496
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-combobox') && elem.errorMessage === '') {
@@ -10331,7 +10331,6 @@ class AuroInputUtilities {
10331
10331
  const maskOptions = this.getMaskOptions('date', normalizedFormat);
10332
10332
 
10333
10333
  if (!(valueObject instanceof Date) || Number.isNaN(valueObject.getTime()) || !maskOptions || typeof maskOptions.format !== 'function') {
10334
- console.debug('Invalid date object or mask options for formatting', { valueObject, maskOptions }); // eslint-disable-line no-console
10335
10334
  return undefined;
10336
10335
  }
10337
10336
 
@@ -11034,7 +11033,7 @@ class BaseInput extends AuroElement {
11034
11033
  * @returns {Date|undefined}
11035
11034
  */
11036
11035
  get valueObject() {
11037
- return this._valueObject;
11036
+ return this._valueObject || this._computeDateObjectFallback(this.value);
11038
11037
  }
11039
11038
 
11040
11039
  /**
@@ -11042,7 +11041,7 @@ class BaseInput extends AuroElement {
11042
11041
  * @returns {Date|undefined}
11043
11042
  */
11044
11043
  get minObject() {
11045
- return this._minObject;
11044
+ return this._minObject || this._computeDateObjectFallback(this.min);
11046
11045
  }
11047
11046
 
11048
11047
  /**
@@ -11050,7 +11049,31 @@ class BaseInput extends AuroElement {
11050
11049
  * @returns {Date|undefined}
11051
11050
  */
11052
11051
  get maxObject() {
11053
- return this._maxObject;
11052
+ return this._maxObject || this._computeDateObjectFallback(this.max);
11053
+ }
11054
+
11055
+ /**
11056
+ * Parses a date string into a Date object when the corresponding `_*Object`
11057
+ * field hasn't been synced yet by `updated()`. Returns undefined when the
11058
+ * input type/format isn't a full date or the string is not a valid date.
11059
+ *
11060
+ * Why this exists: a parent (datepicker) can call `inputN.validate()` from
11061
+ * inside its own `updated()` before this input's `updated()` has run
11062
+ * `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
11063
+ * and range checks would otherwise silently no-op (flipping the result to
11064
+ * `valid` or `patternMismatch`).
11065
+ * @private
11066
+ * @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
11067
+ * @returns {Date|undefined}
11068
+ */
11069
+ _computeDateObjectFallback(dateStr) {
11070
+ if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
11071
+ return undefined;
11072
+ }
11073
+ if (!dateFormatter.isValidDate(dateStr)) {
11074
+ return undefined;
11075
+ }
11076
+ return dateFormatter.stringToDateInstance(dateStr);
11054
11077
  }
11055
11078
 
11056
11079
  /**
@@ -11075,8 +11098,30 @@ class BaseInput extends AuroElement {
11075
11098
  connectedCallback() {
11076
11099
  super.connectedCallback();
11077
11100
 
11101
+ // Mark for query selectors when registered under a versioned tag (e.g. inside
11102
+ // a datepicker/combobox). Must run before parent components call validate() on
11103
+ // their inner inputs — the validation framework matches via tag-or-attribute,
11104
+ // and parents trigger validation during their own updated() cycle, which can
11105
+ // precede this input's firstUpdated().
11106
+ if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
11107
+ this.setAttribute('auro-input', '');
11108
+ }
11109
+
11078
11110
  this.locale = this.domHandler.getLocale(this);
11079
11111
  notifyOnLangChange(this);
11112
+
11113
+ // Pre-compute lengthForType and date-object fields so a parent (e.g. datepicker)
11114
+ // calling our validate() synchronously during its own updated() cycle sees
11115
+ // populated values. Without this, range validation silently no-ops because
11116
+ // `max.length === lengthForType` fails when lengthForType is still undefined.
11117
+ // Rebuild util here (constructor seeded with en-US default) so configureDataForType's
11118
+ // locale-derived format lookup uses the actual locale just resolved above.
11119
+ this.util = new AuroInputUtilities({
11120
+ locale: this.locale,
11121
+ format: this.format
11122
+ });
11123
+ this.configureDataForType();
11124
+ this.syncDateValues();
11080
11125
  }
11081
11126
 
11082
11127
  disconnectedCallback() {
@@ -11097,8 +11142,8 @@ class BaseInput extends AuroElement {
11097
11142
  }
11098
11143
 
11099
11144
  // add attribute for query selectors when auro-input is registered under a custom name
11100
- if (this.tagName.toLowerCase() !== 'auro-input') {
11101
- this.setAttribute('auro-input', true);
11145
+ if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
11146
+ this.setAttribute('auro-input', '');
11102
11147
  }
11103
11148
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
11104
11149
 
@@ -11146,6 +11191,12 @@ class BaseInput extends AuroElement {
11146
11191
  return;
11147
11192
  }
11148
11193
 
11194
+ // While configureAutoFormatting is running, imask's internal updateControl
11195
+ // writes el.value to align display with the masked value. A synthetic input
11196
+ // event from that write would re-enter handleInput → processCreditCard mid-setup
11197
+ // and clobber a Lit value that was just pushed by the parent combobox.
11198
+ if (component._configuringMask) return;
11199
+
11149
11200
  // If all guard clauses are passed, dispatch the event
11150
11201
  const inputEvent = new InputEvent('input', {
11151
11202
  bubbles: true,
@@ -11264,7 +11315,16 @@ class BaseInput extends AuroElement {
11264
11315
 
11265
11316
  if (formattedValue !== this.inputElement.value) {
11266
11317
  this.skipNextProgrammaticInputEvent = true;
11267
- if (formattedValue) {
11318
+ if (this.maskInstance && this.type === 'credit-card') {
11319
+ // Route through the mask so its _value and el.value stay in lock-step
11320
+ // (set value calls updateControl which writes el.value = displayValue).
11321
+ // Writing el.value directly leaves the mask thinking displayValue is
11322
+ // stale; _saveSelection on the next focus/click then warns. Scoped to
11323
+ // credit-card so date's own formattedValue (raw ISO when calendar-invalid)
11324
+ // isn't re-masked through the mm/dd/yyyy mask, which would truncate it
11325
+ // and flip validity from patternMismatch to tooShort.
11326
+ this.maskInstance.value = formattedValue || '';
11327
+ } else if (formattedValue) {
11268
11328
  this.inputElement.value = formattedValue;
11269
11329
  } else {
11270
11330
  this.inputElement.value = '';
@@ -11373,58 +11433,73 @@ class BaseInput extends AuroElement {
11373
11433
  * @returns {void}
11374
11434
  */
11375
11435
  configureAutoFormatting() {
11376
- if (this.maskInstance) {
11377
- this.maskInstance.destroy();
11378
- }
11379
-
11380
- // Pass new format to util
11381
- this.util.updateFormat(this.format);
11382
-
11383
- const maskOptions = this.util.getMaskOptions(this.type, this.format);
11384
-
11385
- if (this.inputElement && maskOptions.mask) {
11386
-
11387
- // Stash and clear any existing value before IMask init.
11388
- // IMask's constructor processes the current input value which requires
11389
- // selection state — clearing first avoids that scenario entirely.
11390
- // When the format changes (e.g. locale switch) and we have a valid ISO
11391
- // model value, compute the display string for the NEW format instead of
11392
- // re-using the old display string, which may be invalid in the new mask.
11393
- let existingValue = this.inputElement.value;
11394
- if (
11395
- this.util.isFullDateFormat(this.type, this.format) &&
11396
- this.value &&
11397
- dateFormatter.isValidDate(this.value) &&
11398
- this.valueObject instanceof Date &&
11399
- !Number.isNaN(this.valueObject.getTime()) &&
11400
- typeof maskOptions.format === 'function'
11401
- ) {
11402
- existingValue = maskOptions.format(this.valueObject);
11436
+ // Re-entrancy guard. The patched-setter's synthetic input event (suppressed
11437
+ // by _configuringMask above) could otherwise trigger handleInput →
11438
+ // processCreditCard → configureAutoFormatting before the outer call's
11439
+ // set value has finished its alignCursor pass.
11440
+ if (this._configuringMask) return;
11441
+ this._configuringMask = true;
11442
+ try {
11443
+ if (this.maskInstance) {
11444
+ this.maskInstance.destroy();
11403
11445
  }
11404
11446
 
11405
- this.skipNextProgrammaticInputEvent = true;
11406
- this.inputElement.value = '';
11407
-
11408
- this.maskInstance = IMask(this.inputElement, maskOptions);
11447
+ // Pass new format to util
11448
+ this.util.updateFormat(this.format);
11409
11449
 
11410
- this.maskInstance.on('accept', () => {
11411
- this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11412
- if (this.type === "date") {
11413
- this._rawMaskValue = this.maskInstance.value;
11450
+ const maskOptions = this.util.getMaskOptions(this.type, this.format);
11451
+
11452
+ if (this.inputElement && maskOptions.mask) {
11453
+
11454
+ // Stash and clear any existing value before IMask init.
11455
+ // IMask's constructor processes the current input value which requires
11456
+ // selection state — clearing first avoids that scenario entirely.
11457
+ // When the format changes (e.g. locale switch) and we have a valid ISO
11458
+ // model value, compute the display string for the NEW format instead of
11459
+ // re-using the old display string, which may be invalid in the new mask.
11460
+ let existingValue = this.inputElement.value;
11461
+ if (
11462
+ this.util.isFullDateFormat(this.type, this.format) &&
11463
+ this.value &&
11464
+ dateFormatter.isValidDate(this.value) &&
11465
+ this.valueObject instanceof Date &&
11466
+ !Number.isNaN(this.valueObject.getTime()) &&
11467
+ typeof maskOptions.format === 'function'
11468
+ ) {
11469
+ existingValue = maskOptions.format(this.valueObject);
11414
11470
  }
11415
- });
11416
11471
 
11417
- this.maskInstance.on('complete', () => {
11418
- this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11419
- if (this.type === "date") {
11420
- this._rawMaskValue = this.maskInstance.value;
11421
- }
11422
- });
11472
+ this.skipNextProgrammaticInputEvent = true;
11473
+ this.inputElement.value = '';
11474
+
11475
+ this.maskInstance = IMask(this.inputElement, maskOptions);
11476
+
11477
+ this.maskInstance.on('accept', () => {
11478
+ // Suppress propagation during configureAutoFormatting's own value-restoration
11479
+ // (line below) — the mask emits 'accept' on every value-set, including ours,
11480
+ // and we don't want to overwrite a value the parent just pushed.
11481
+ if (this._configuringMask) return;
11482
+ this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11483
+ if (this.type === "date") {
11484
+ this._rawMaskValue = this.maskInstance.value;
11485
+ }
11486
+ });
11423
11487
 
11424
- // Restore the stashed value through IMask so it's properly masked
11425
- if (existingValue) {
11426
- this.maskInstance.value = existingValue;
11488
+ this.maskInstance.on('complete', () => {
11489
+ if (this._configuringMask) return;
11490
+ this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11491
+ if (this.type === "date") {
11492
+ this._rawMaskValue = this.maskInstance.value;
11493
+ }
11494
+ });
11495
+
11496
+ // Restore the stashed value through IMask so it's properly masked
11497
+ if (existingValue) {
11498
+ this.maskInstance.value = existingValue;
11499
+ }
11427
11500
  }
11501
+ } finally {
11502
+ this._configuringMask = false;
11428
11503
  }
11429
11504
  }
11430
11505
 
@@ -11711,10 +11786,10 @@ class BaseInput extends AuroElement {
11711
11786
  },
11712
11787
  {
11713
11788
  name: 'Diners club',
11714
- regex: /^(?<num>36|38)\d{0}/u,
11789
+ regex: /^(?<num>30[0-5]|36|38)\d{0}/u,
11715
11790
  formatLength: 16,
11716
11791
  errorMessage: CreditCardValidationMessage,
11717
- cardIcon: 'credit-card',
11792
+ cardIcon: 'cc-dinersclub',
11718
11793
  maskFormat: "0000 000000 0000"
11719
11794
  },
11720
11795
  {
@@ -12124,7 +12199,7 @@ class AuroHelpText extends LitElement {
12124
12199
  }
12125
12200
  }
12126
12201
 
12127
- var formkitVersion = '202606151650';
12202
+ var formkitVersion = '202606171946';
12128
12203
 
12129
12204
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
12130
12205
  // See LICENSE in the project root for license information.
@@ -4490,7 +4490,7 @@ class AuroFormValidation {
4490
4490
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-input') && elem.errorMessage === '') {
4491
4491
  const input = elem.renderRoot.querySelector('input');
4492
4492
 
4493
- if (input.validationMessage.length > 0) {
4493
+ if (input && input.validationMessage.length > 0) {
4494
4494
  elem.errorMessage = input.validationMessage;
4495
4495
  }
4496
4496
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-combobox') && elem.errorMessage === '') {
@@ -10331,7 +10331,6 @@ class AuroInputUtilities {
10331
10331
  const maskOptions = this.getMaskOptions('date', normalizedFormat);
10332
10332
 
10333
10333
  if (!(valueObject instanceof Date) || Number.isNaN(valueObject.getTime()) || !maskOptions || typeof maskOptions.format !== 'function') {
10334
- console.debug('Invalid date object or mask options for formatting', { valueObject, maskOptions }); // eslint-disable-line no-console
10335
10334
  return undefined;
10336
10335
  }
10337
10336
 
@@ -11034,7 +11033,7 @@ class BaseInput extends AuroElement {
11034
11033
  * @returns {Date|undefined}
11035
11034
  */
11036
11035
  get valueObject() {
11037
- return this._valueObject;
11036
+ return this._valueObject || this._computeDateObjectFallback(this.value);
11038
11037
  }
11039
11038
 
11040
11039
  /**
@@ -11042,7 +11041,7 @@ class BaseInput extends AuroElement {
11042
11041
  * @returns {Date|undefined}
11043
11042
  */
11044
11043
  get minObject() {
11045
- return this._minObject;
11044
+ return this._minObject || this._computeDateObjectFallback(this.min);
11046
11045
  }
11047
11046
 
11048
11047
  /**
@@ -11050,7 +11049,31 @@ class BaseInput extends AuroElement {
11050
11049
  * @returns {Date|undefined}
11051
11050
  */
11052
11051
  get maxObject() {
11053
- return this._maxObject;
11052
+ return this._maxObject || this._computeDateObjectFallback(this.max);
11053
+ }
11054
+
11055
+ /**
11056
+ * Parses a date string into a Date object when the corresponding `_*Object`
11057
+ * field hasn't been synced yet by `updated()`. Returns undefined when the
11058
+ * input type/format isn't a full date or the string is not a valid date.
11059
+ *
11060
+ * Why this exists: a parent (datepicker) can call `inputN.validate()` from
11061
+ * inside its own `updated()` before this input's `updated()` has run
11062
+ * `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
11063
+ * and range checks would otherwise silently no-op (flipping the result to
11064
+ * `valid` or `patternMismatch`).
11065
+ * @private
11066
+ * @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
11067
+ * @returns {Date|undefined}
11068
+ */
11069
+ _computeDateObjectFallback(dateStr) {
11070
+ if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
11071
+ return undefined;
11072
+ }
11073
+ if (!dateFormatter.isValidDate(dateStr)) {
11074
+ return undefined;
11075
+ }
11076
+ return dateFormatter.stringToDateInstance(dateStr);
11054
11077
  }
11055
11078
 
11056
11079
  /**
@@ -11075,8 +11098,30 @@ class BaseInput extends AuroElement {
11075
11098
  connectedCallback() {
11076
11099
  super.connectedCallback();
11077
11100
 
11101
+ // Mark for query selectors when registered under a versioned tag (e.g. inside
11102
+ // a datepicker/combobox). Must run before parent components call validate() on
11103
+ // their inner inputs — the validation framework matches via tag-or-attribute,
11104
+ // and parents trigger validation during their own updated() cycle, which can
11105
+ // precede this input's firstUpdated().
11106
+ if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
11107
+ this.setAttribute('auro-input', '');
11108
+ }
11109
+
11078
11110
  this.locale = this.domHandler.getLocale(this);
11079
11111
  notifyOnLangChange(this);
11112
+
11113
+ // Pre-compute lengthForType and date-object fields so a parent (e.g. datepicker)
11114
+ // calling our validate() synchronously during its own updated() cycle sees
11115
+ // populated values. Without this, range validation silently no-ops because
11116
+ // `max.length === lengthForType` fails when lengthForType is still undefined.
11117
+ // Rebuild util here (constructor seeded with en-US default) so configureDataForType's
11118
+ // locale-derived format lookup uses the actual locale just resolved above.
11119
+ this.util = new AuroInputUtilities({
11120
+ locale: this.locale,
11121
+ format: this.format
11122
+ });
11123
+ this.configureDataForType();
11124
+ this.syncDateValues();
11080
11125
  }
11081
11126
 
11082
11127
  disconnectedCallback() {
@@ -11097,8 +11142,8 @@ class BaseInput extends AuroElement {
11097
11142
  }
11098
11143
 
11099
11144
  // add attribute for query selectors when auro-input is registered under a custom name
11100
- if (this.tagName.toLowerCase() !== 'auro-input') {
11101
- this.setAttribute('auro-input', true);
11145
+ if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
11146
+ this.setAttribute('auro-input', '');
11102
11147
  }
11103
11148
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
11104
11149
 
@@ -11146,6 +11191,12 @@ class BaseInput extends AuroElement {
11146
11191
  return;
11147
11192
  }
11148
11193
 
11194
+ // While configureAutoFormatting is running, imask's internal updateControl
11195
+ // writes el.value to align display with the masked value. A synthetic input
11196
+ // event from that write would re-enter handleInput → processCreditCard mid-setup
11197
+ // and clobber a Lit value that was just pushed by the parent combobox.
11198
+ if (component._configuringMask) return;
11199
+
11149
11200
  // If all guard clauses are passed, dispatch the event
11150
11201
  const inputEvent = new InputEvent('input', {
11151
11202
  bubbles: true,
@@ -11264,7 +11315,16 @@ class BaseInput extends AuroElement {
11264
11315
 
11265
11316
  if (formattedValue !== this.inputElement.value) {
11266
11317
  this.skipNextProgrammaticInputEvent = true;
11267
- if (formattedValue) {
11318
+ if (this.maskInstance && this.type === 'credit-card') {
11319
+ // Route through the mask so its _value and el.value stay in lock-step
11320
+ // (set value calls updateControl which writes el.value = displayValue).
11321
+ // Writing el.value directly leaves the mask thinking displayValue is
11322
+ // stale; _saveSelection on the next focus/click then warns. Scoped to
11323
+ // credit-card so date's own formattedValue (raw ISO when calendar-invalid)
11324
+ // isn't re-masked through the mm/dd/yyyy mask, which would truncate it
11325
+ // and flip validity from patternMismatch to tooShort.
11326
+ this.maskInstance.value = formattedValue || '';
11327
+ } else if (formattedValue) {
11268
11328
  this.inputElement.value = formattedValue;
11269
11329
  } else {
11270
11330
  this.inputElement.value = '';
@@ -11373,58 +11433,73 @@ class BaseInput extends AuroElement {
11373
11433
  * @returns {void}
11374
11434
  */
11375
11435
  configureAutoFormatting() {
11376
- if (this.maskInstance) {
11377
- this.maskInstance.destroy();
11378
- }
11379
-
11380
- // Pass new format to util
11381
- this.util.updateFormat(this.format);
11382
-
11383
- const maskOptions = this.util.getMaskOptions(this.type, this.format);
11384
-
11385
- if (this.inputElement && maskOptions.mask) {
11386
-
11387
- // Stash and clear any existing value before IMask init.
11388
- // IMask's constructor processes the current input value which requires
11389
- // selection state — clearing first avoids that scenario entirely.
11390
- // When the format changes (e.g. locale switch) and we have a valid ISO
11391
- // model value, compute the display string for the NEW format instead of
11392
- // re-using the old display string, which may be invalid in the new mask.
11393
- let existingValue = this.inputElement.value;
11394
- if (
11395
- this.util.isFullDateFormat(this.type, this.format) &&
11396
- this.value &&
11397
- dateFormatter.isValidDate(this.value) &&
11398
- this.valueObject instanceof Date &&
11399
- !Number.isNaN(this.valueObject.getTime()) &&
11400
- typeof maskOptions.format === 'function'
11401
- ) {
11402
- existingValue = maskOptions.format(this.valueObject);
11436
+ // Re-entrancy guard. The patched-setter's synthetic input event (suppressed
11437
+ // by _configuringMask above) could otherwise trigger handleInput →
11438
+ // processCreditCard → configureAutoFormatting before the outer call's
11439
+ // set value has finished its alignCursor pass.
11440
+ if (this._configuringMask) return;
11441
+ this._configuringMask = true;
11442
+ try {
11443
+ if (this.maskInstance) {
11444
+ this.maskInstance.destroy();
11403
11445
  }
11404
11446
 
11405
- this.skipNextProgrammaticInputEvent = true;
11406
- this.inputElement.value = '';
11407
-
11408
- this.maskInstance = IMask(this.inputElement, maskOptions);
11447
+ // Pass new format to util
11448
+ this.util.updateFormat(this.format);
11409
11449
 
11410
- this.maskInstance.on('accept', () => {
11411
- this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11412
- if (this.type === "date") {
11413
- this._rawMaskValue = this.maskInstance.value;
11450
+ const maskOptions = this.util.getMaskOptions(this.type, this.format);
11451
+
11452
+ if (this.inputElement && maskOptions.mask) {
11453
+
11454
+ // Stash and clear any existing value before IMask init.
11455
+ // IMask's constructor processes the current input value which requires
11456
+ // selection state — clearing first avoids that scenario entirely.
11457
+ // When the format changes (e.g. locale switch) and we have a valid ISO
11458
+ // model value, compute the display string for the NEW format instead of
11459
+ // re-using the old display string, which may be invalid in the new mask.
11460
+ let existingValue = this.inputElement.value;
11461
+ if (
11462
+ this.util.isFullDateFormat(this.type, this.format) &&
11463
+ this.value &&
11464
+ dateFormatter.isValidDate(this.value) &&
11465
+ this.valueObject instanceof Date &&
11466
+ !Number.isNaN(this.valueObject.getTime()) &&
11467
+ typeof maskOptions.format === 'function'
11468
+ ) {
11469
+ existingValue = maskOptions.format(this.valueObject);
11414
11470
  }
11415
- });
11416
11471
 
11417
- this.maskInstance.on('complete', () => {
11418
- this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11419
- if (this.type === "date") {
11420
- this._rawMaskValue = this.maskInstance.value;
11421
- }
11422
- });
11472
+ this.skipNextProgrammaticInputEvent = true;
11473
+ this.inputElement.value = '';
11474
+
11475
+ this.maskInstance = IMask(this.inputElement, maskOptions);
11476
+
11477
+ this.maskInstance.on('accept', () => {
11478
+ // Suppress propagation during configureAutoFormatting's own value-restoration
11479
+ // (line below) — the mask emits 'accept' on every value-set, including ours,
11480
+ // and we don't want to overwrite a value the parent just pushed.
11481
+ if (this._configuringMask) return;
11482
+ this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11483
+ if (this.type === "date") {
11484
+ this._rawMaskValue = this.maskInstance.value;
11485
+ }
11486
+ });
11423
11487
 
11424
- // Restore the stashed value through IMask so it's properly masked
11425
- if (existingValue) {
11426
- this.maskInstance.value = existingValue;
11488
+ this.maskInstance.on('complete', () => {
11489
+ if (this._configuringMask) return;
11490
+ this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11491
+ if (this.type === "date") {
11492
+ this._rawMaskValue = this.maskInstance.value;
11493
+ }
11494
+ });
11495
+
11496
+ // Restore the stashed value through IMask so it's properly masked
11497
+ if (existingValue) {
11498
+ this.maskInstance.value = existingValue;
11499
+ }
11427
11500
  }
11501
+ } finally {
11502
+ this._configuringMask = false;
11428
11503
  }
11429
11504
  }
11430
11505
 
@@ -11711,10 +11786,10 @@ class BaseInput extends AuroElement {
11711
11786
  },
11712
11787
  {
11713
11788
  name: 'Diners club',
11714
- regex: /^(?<num>36|38)\d{0}/u,
11789
+ regex: /^(?<num>30[0-5]|36|38)\d{0}/u,
11715
11790
  formatLength: 16,
11716
11791
  errorMessage: CreditCardValidationMessage,
11717
- cardIcon: 'credit-card',
11792
+ cardIcon: 'cc-dinersclub',
11718
11793
  maskFormat: "0000 000000 0000"
11719
11794
  },
11720
11795
  {
@@ -12124,7 +12199,7 @@ class AuroHelpText extends LitElement {
12124
12199
  }
12125
12200
  }
12126
12201
 
12127
- var formkitVersion = '202606151650';
12202
+ var formkitVersion = '202606171946';
12128
12203
 
12129
12204
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
12130
12205
  // See LICENSE in the project root for license information.