@aurodesignsystem-dev/auro-formkit 0.0.0-pr1504.0 → 0.0.0-pr1506.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 (60) 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 +1435 -1666
  8. package/components/combobox/demo/getting-started.min.js +1435 -1666
  9. package/components/combobox/demo/index.min.js +1435 -1666
  10. package/components/combobox/dist/auro-combobox.d.ts +14 -0
  11. package/components/combobox/dist/index.js +489 -145
  12. package/components/combobox/dist/registered.js +489 -145
  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/customize.min.js +140 -61
  18. package/components/datepicker/demo/index.min.js +140 -61
  19. package/components/datepicker/dist/index.js +140 -61
  20. package/components/datepicker/dist/registered.js +140 -61
  21. package/components/dropdown/demo/customize.min.js +1 -1
  22. package/components/dropdown/demo/getting-started.min.js +1 -1
  23. package/components/dropdown/demo/index.min.js +1 -1
  24. package/components/dropdown/dist/index.js +1 -1
  25. package/components/dropdown/dist/registered.js +1 -1
  26. package/components/form/demo/customize.min.js +2336 -2269
  27. package/components/form/demo/getting-started.min.js +2336 -2269
  28. package/components/form/demo/index.min.js +2336 -2269
  29. package/components/form/demo/registerDemoDeps.min.js +2334 -2267
  30. package/components/input/demo/customize.min.js +138 -57
  31. package/components/input/demo/getting-started.min.js +138 -57
  32. package/components/input/demo/index.min.js +138 -57
  33. package/components/input/dist/base-input.d.ts +16 -0
  34. package/components/input/dist/index.js +138 -57
  35. package/components/input/dist/registered.js +138 -57
  36. package/components/menu/demo/api.md +41 -45
  37. package/components/menu/demo/customize.md +0 -28
  38. package/components/menu/demo/index.min.js +779 -1354
  39. package/components/menu/dist/auro-menu.d.ts +95 -110
  40. package/components/menu/dist/auro-menuoption.d.ts +32 -138
  41. package/components/menu/dist/index.js +753 -1308
  42. package/components/menu/dist/registered.js +765 -1308
  43. package/components/radio/demo/customize.min.js +2 -2
  44. package/components/radio/demo/getting-started.min.js +2 -2
  45. package/components/radio/demo/index.min.js +2 -2
  46. package/components/radio/dist/index.js +2 -2
  47. package/components/radio/dist/registered.js +2 -2
  48. package/components/select/demo/api.md +1 -0
  49. package/components/select/demo/customize.md +78 -3
  50. package/components/select/demo/customize.min.js +1140 -1577
  51. package/components/select/demo/getting-started.min.js +1140 -1577
  52. package/components/select/demo/index.min.js +1140 -1577
  53. package/components/select/demo/keyboard-behavior.md +18 -0
  54. package/components/select/dist/auro-select.d.ts +38 -2
  55. package/components/select/dist/index.js +177 -39
  56. package/components/select/dist/registered.js +177 -39
  57. package/components/select/dist/selectUtils.d.ts +24 -0
  58. package/custom-elements.json +503 -1037
  59. package/package.json +1 -1
  60. package/components/menu/dist/auro-menu.context.d.ts +0 -238
@@ -4548,7 +4548,7 @@ class AuroFormValidation {
4548
4548
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-input') && elem.errorMessage === '') {
4549
4549
  const input = elem.renderRoot.querySelector('input');
4550
4550
 
4551
- if (input.validationMessage.length > 0) {
4551
+ if (input && input.validationMessage.length > 0) {
4552
4552
  elem.errorMessage = input.validationMessage;
4553
4553
  }
4554
4554
  } else if (this.runtimeUtils.elementMatch(elem, 'auro-combobox') && elem.errorMessage === '') {
@@ -11092,7 +11092,7 @@ class BaseInput extends AuroElement {
11092
11092
  * @returns {Date|undefined}
11093
11093
  */
11094
11094
  get valueObject() {
11095
- return this._valueObject;
11095
+ return this._valueObject || this._computeDateObjectFallback(this.value);
11096
11096
  }
11097
11097
 
11098
11098
  /**
@@ -11100,7 +11100,7 @@ class BaseInput extends AuroElement {
11100
11100
  * @returns {Date|undefined}
11101
11101
  */
11102
11102
  get minObject() {
11103
- return this._minObject;
11103
+ return this._minObject || this._computeDateObjectFallback(this.min);
11104
11104
  }
11105
11105
 
11106
11106
  /**
@@ -11108,7 +11108,31 @@ class BaseInput extends AuroElement {
11108
11108
  * @returns {Date|undefined}
11109
11109
  */
11110
11110
  get maxObject() {
11111
- return this._maxObject;
11111
+ return this._maxObject || this._computeDateObjectFallback(this.max);
11112
+ }
11113
+
11114
+ /**
11115
+ * Parses a date string into a Date object when the corresponding `_*Object`
11116
+ * field hasn't been synced yet by `updated()`. Returns undefined when the
11117
+ * input type/format isn't a full date or the string is not a valid date.
11118
+ *
11119
+ * Why this exists: a parent (datepicker) can call `inputN.validate()` from
11120
+ * inside its own `updated()` before this input's `updated()` has run
11121
+ * `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
11122
+ * and range checks would otherwise silently no-op (flipping the result to
11123
+ * `valid` or `patternMismatch`).
11124
+ * @private
11125
+ * @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
11126
+ * @returns {Date|undefined}
11127
+ */
11128
+ _computeDateObjectFallback(dateStr) {
11129
+ if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
11130
+ return undefined;
11131
+ }
11132
+ if (!dateFormatter.isValidDate(dateStr)) {
11133
+ return undefined;
11134
+ }
11135
+ return dateFormatter.stringToDateInstance(dateStr);
11112
11136
  }
11113
11137
 
11114
11138
  /**
@@ -11133,8 +11157,30 @@ class BaseInput extends AuroElement {
11133
11157
  connectedCallback() {
11134
11158
  super.connectedCallback();
11135
11159
 
11160
+ // Mark for query selectors when registered under a versioned tag (e.g. inside
11161
+ // a datepicker/combobox). Must run before parent components call validate() on
11162
+ // their inner inputs — the validation framework matches via tag-or-attribute,
11163
+ // and parents trigger validation during their own updated() cycle, which can
11164
+ // precede this input's firstUpdated().
11165
+ if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
11166
+ this.setAttribute('auro-input', '');
11167
+ }
11168
+
11136
11169
  this.locale = this.domHandler.getLocale(this);
11137
11170
  notifyOnLangChange(this);
11171
+
11172
+ // Pre-compute lengthForType and date-object fields so a parent (e.g. datepicker)
11173
+ // calling our validate() synchronously during its own updated() cycle sees
11174
+ // populated values. Without this, range validation silently no-ops because
11175
+ // `max.length === lengthForType` fails when lengthForType is still undefined.
11176
+ // Rebuild util here (constructor seeded with en-US default) so configureDataForType's
11177
+ // locale-derived format lookup uses the actual locale just resolved above.
11178
+ this.util = new AuroInputUtilities({
11179
+ locale: this.locale,
11180
+ format: this.format
11181
+ });
11182
+ this.configureDataForType();
11183
+ this.syncDateValues();
11138
11184
  }
11139
11185
 
11140
11186
  disconnectedCallback() {
@@ -11154,10 +11200,6 @@ class BaseInput extends AuroElement {
11154
11200
  this.wrapperElement.addEventListener('click', this.handleClick);
11155
11201
  }
11156
11202
 
11157
- // add attribute for query selectors when auro-input is registered under a custom name
11158
- if (this.tagName.toLowerCase() !== 'auro-input') {
11159
- this.setAttribute('auro-input', true);
11160
- }
11161
11203
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
11162
11204
 
11163
11205
  // use validity message override if declared when initializing the component
@@ -11204,6 +11246,12 @@ class BaseInput extends AuroElement {
11204
11246
  return;
11205
11247
  }
11206
11248
 
11249
+ // While configureAutoFormatting is running, imask's internal updateControl
11250
+ // writes el.value to align display with the masked value. A synthetic input
11251
+ // event from that write would re-enter handleInput → processCreditCard mid-setup
11252
+ // and clobber a Lit value that was just pushed by the parent combobox.
11253
+ if (component._configuringMask) return;
11254
+
11207
11255
  // If all guard clauses are passed, dispatch the event
11208
11256
  const inputEvent = new InputEvent('input', {
11209
11257
  bubbles: true,
@@ -11322,7 +11370,16 @@ class BaseInput extends AuroElement {
11322
11370
 
11323
11371
  if (formattedValue !== this.inputElement.value) {
11324
11372
  this.skipNextProgrammaticInputEvent = true;
11325
- if (formattedValue) {
11373
+ if (this.maskInstance && this.type !== 'date') {
11374
+ // Route through the mask so its _value and el.value stay in lock-step
11375
+ // (set value calls updateControl which writes el.value = displayValue).
11376
+ // Writing el.value directly leaves the mask thinking displayValue is
11377
+ // stale; _saveSelection on the next focus/click then warns. Date is
11378
+ // excluded because its formattedValue can be raw ISO when the calendar
11379
+ // is invalid, and re-masking through mm/dd/yyyy would truncate it and
11380
+ // flip validity from patternMismatch to tooShort.
11381
+ this.maskInstance.value = formattedValue || '';
11382
+ } else if (formattedValue) {
11326
11383
  this.inputElement.value = formattedValue;
11327
11384
  } else {
11328
11385
  this.inputElement.value = '';
@@ -11431,58 +11488,82 @@ class BaseInput extends AuroElement {
11431
11488
  * @returns {void}
11432
11489
  */
11433
11490
  configureAutoFormatting() {
11434
- if (this.maskInstance) {
11435
- this.maskInstance.destroy();
11436
- }
11437
-
11438
- // Pass new format to util
11439
- this.util.updateFormat(this.format);
11440
-
11441
- const maskOptions = this.util.getMaskOptions(this.type, this.format);
11442
-
11443
- if (this.inputElement && maskOptions.mask) {
11444
-
11445
- // Stash and clear any existing value before IMask init.
11446
- // IMask's constructor processes the current input value which requires
11447
- // selection state — clearing first avoids that scenario entirely.
11448
- // When the format changes (e.g. locale switch) and we have a valid ISO
11449
- // model value, compute the display string for the NEW format instead of
11450
- // re-using the old display string, which may be invalid in the new mask.
11451
- let existingValue = this.inputElement.value;
11452
- if (
11453
- this.util.isFullDateFormat(this.type, this.format) &&
11454
- this.value &&
11455
- dateFormatter.isValidDate(this.value) &&
11456
- this.valueObject instanceof Date &&
11457
- !Number.isNaN(this.valueObject.getTime()) &&
11458
- typeof maskOptions.format === 'function'
11459
- ) {
11460
- existingValue = maskOptions.format(this.valueObject);
11491
+ // _configuringMask gates two things: external re-entry into this method
11492
+ // while setup is mid-flight (from property changes that call back here),
11493
+ // and the accept/complete listeners below — both need to ignore the mask
11494
+ // events fired by our own value-restore step.
11495
+ if (this._configuringMask) return;
11496
+ this._configuringMask = true;
11497
+ try {
11498
+ // Destroy any prior mask so IMask can attach fresh under the new format.
11499
+ if (this.maskInstance) {
11500
+ this.maskInstance.destroy();
11461
11501
  }
11462
11502
 
11463
- this.skipNextProgrammaticInputEvent = true;
11464
- this.inputElement.value = '';
11465
-
11466
- this.maskInstance = IMask(this.inputElement, maskOptions);
11503
+ this.util.updateFormat(this.format);
11467
11504
 
11468
- this.maskInstance.on('accept', () => {
11469
- this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11470
- if (this.type === "date") {
11471
- this._rawMaskValue = this.maskInstance.value;
11505
+ const maskOptions = this.util.getMaskOptions(this.type, this.format);
11506
+
11507
+ if (this.inputElement && maskOptions.mask) {
11508
+ // Capture the current display so it can be re-applied after IMask
11509
+ // attaches. The restore at the bottom goes through maskInstance.value
11510
+ // (not inputElement.value directly) so the mask's internal state and
11511
+ // the input's displayed text stay in lock-step.
11512
+ let existingValue = this.inputElement.value;
11513
+
11514
+ // Format-change case (e.g. locale switch): existingValue is the OLD
11515
+ // mask's display string and may not parse under the new mask. When
11516
+ // we have a valid date model, rebuild the display from valueObject
11517
+ // (the canonical source) using the new mask's format function.
11518
+ if (
11519
+ this.util.isFullDateFormat(this.type, this.format) &&
11520
+ this.value &&
11521
+ dateFormatter.isValidDate(this.value) &&
11522
+ this.valueObject instanceof Date &&
11523
+ !Number.isNaN(this.valueObject.getTime()) &&
11524
+ typeof maskOptions.format === 'function'
11525
+ ) {
11526
+ existingValue = maskOptions.format(this.valueObject);
11472
11527
  }
11473
- });
11474
11528
 
11475
- this.maskInstance.on('complete', () => {
11476
- this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11477
- if (this.type === "date") {
11478
- this._rawMaskValue = this.maskInstance.value;
11479
- }
11480
- });
11529
+ // Clear before IMask attaches so the constructor seeds an empty
11530
+ // internal value. Otherwise IMask reads the stale unmasked string
11531
+ // and emits a spurious 'accept' before the restore below runs.
11532
+ this.skipNextProgrammaticInputEvent = true;
11533
+ this.inputElement.value = '';
11534
+
11535
+ this.maskInstance = IMask(this.inputElement, maskOptions);
11536
+
11537
+ // Mask fires 'accept' on every value change, including the restore
11538
+ // step below. Skip events fired during configureAutoFormatting so
11539
+ // we don't overwrite a value the parent just pushed.
11540
+ this.maskInstance.on('accept', () => {
11541
+ if (this._configuringMask) return;
11542
+ this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11543
+ if (this.type === "date") {
11544
+ this._rawMaskValue = this.maskInstance.value;
11545
+ }
11546
+ });
11481
11547
 
11482
- // Restore the stashed value through IMask so it's properly masked
11483
- if (existingValue) {
11484
- this.maskInstance.value = existingValue;
11548
+ // Mask fires 'complete' on the restore step below for any value that
11549
+ // happens to be a complete match. Same setup-suppression as 'accept'.
11550
+ this.maskInstance.on('complete', () => {
11551
+ if (this._configuringMask) return;
11552
+ this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11553
+ if (this.type === "date") {
11554
+ this._rawMaskValue = this.maskInstance.value;
11555
+ }
11556
+ });
11557
+
11558
+ // Write existingValue through the mask (not the input directly) so
11559
+ // the mask reformats it under the new rules and keeps its internal
11560
+ // _value aligned with the input's displayed text.
11561
+ if (existingValue) {
11562
+ this.maskInstance.value = existingValue;
11563
+ }
11485
11564
  }
11565
+ } finally {
11566
+ this._configuringMask = false;
11486
11567
  }
11487
11568
  }
11488
11569
 
@@ -11769,10 +11850,10 @@ class BaseInput extends AuroElement {
11769
11850
  },
11770
11851
  {
11771
11852
  name: 'Diners club',
11772
- regex: /^(?<num>36|38)\d{0}/u,
11853
+ regex: /^(?<num>30[0-5]|36|38)\d{0}/u,
11773
11854
  formatLength: 16,
11774
11855
  errorMessage: CreditCardValidationMessage,
11775
- cardIcon: 'credit-card',
11856
+ cardIcon: 'cc-dinersclub',
11776
11857
  maskFormat: "0000 000000 0000"
11777
11858
  },
11778
11859
  {
@@ -12182,7 +12263,7 @@ class AuroHelpText extends i$3 {
12182
12263
  }
12183
12264
  }
12184
12265
 
12185
- var formkitVersion = '202606141505';
12266
+ var formkitVersion = '202606190840';
12186
12267
 
12187
12268
  /**
12188
12269
  * @license
@@ -453,6 +453,21 @@ export default class BaseInput extends AuroElement {
453
453
  * @returns {Date|undefined}
454
454
  */
455
455
  get maxObject(): Date | undefined;
456
+ /**
457
+ * Parses a date string into a Date object when the corresponding `_*Object`
458
+ * field hasn't been synced yet by `updated()`. Returns undefined when the
459
+ * input type/format isn't a full date or the string is not a valid date.
460
+ *
461
+ * Why this exists: a parent (datepicker) can call `inputN.validate()` from
462
+ * inside its own `updated()` before this input's `updated()` has run
463
+ * `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
464
+ * and range checks would otherwise silently no-op (flipping the result to
465
+ * `valid` or `patternMismatch`).
466
+ * @private
467
+ * @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
468
+ * @returns {Date|undefined}
469
+ */
470
+ private _computeDateObjectFallback;
456
471
  /**
457
472
  * Internal setter for readonly date object properties.
458
473
  * @private
@@ -523,6 +538,7 @@ export default class BaseInput extends AuroElement {
523
538
  * @returns {void}
524
539
  */
525
540
  private configureAutoFormatting;
541
+ _configuringMask: boolean | undefined;
526
542
  maskInstance: import("imask").InputMask<any> | undefined;
527
543
  /**
528
544
  * Sends event notifying that the input has changed it's value.
@@ -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 === '') {
@@ -11034,7 +11034,7 @@ class BaseInput extends AuroElement {
11034
11034
  * @returns {Date|undefined}
11035
11035
  */
11036
11036
  get valueObject() {
11037
- return this._valueObject;
11037
+ return this._valueObject || this._computeDateObjectFallback(this.value);
11038
11038
  }
11039
11039
 
11040
11040
  /**
@@ -11042,7 +11042,7 @@ class BaseInput extends AuroElement {
11042
11042
  * @returns {Date|undefined}
11043
11043
  */
11044
11044
  get minObject() {
11045
- return this._minObject;
11045
+ return this._minObject || this._computeDateObjectFallback(this.min);
11046
11046
  }
11047
11047
 
11048
11048
  /**
@@ -11050,7 +11050,31 @@ class BaseInput extends AuroElement {
11050
11050
  * @returns {Date|undefined}
11051
11051
  */
11052
11052
  get maxObject() {
11053
- return this._maxObject;
11053
+ return this._maxObject || this._computeDateObjectFallback(this.max);
11054
+ }
11055
+
11056
+ /**
11057
+ * Parses a date string into a Date object when the corresponding `_*Object`
11058
+ * field hasn't been synced yet by `updated()`. Returns undefined when the
11059
+ * input type/format isn't a full date or the string is not a valid date.
11060
+ *
11061
+ * Why this exists: a parent (datepicker) can call `inputN.validate()` from
11062
+ * inside its own `updated()` before this input's `updated()` has run
11063
+ * `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
11064
+ * and range checks would otherwise silently no-op (flipping the result to
11065
+ * `valid` or `patternMismatch`).
11066
+ * @private
11067
+ * @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
11068
+ * @returns {Date|undefined}
11069
+ */
11070
+ _computeDateObjectFallback(dateStr) {
11071
+ if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
11072
+ return undefined;
11073
+ }
11074
+ if (!dateFormatter.isValidDate(dateStr)) {
11075
+ return undefined;
11076
+ }
11077
+ return dateFormatter.stringToDateInstance(dateStr);
11054
11078
  }
11055
11079
 
11056
11080
  /**
@@ -11075,8 +11099,30 @@ class BaseInput extends AuroElement {
11075
11099
  connectedCallback() {
11076
11100
  super.connectedCallback();
11077
11101
 
11102
+ // Mark for query selectors when registered under a versioned tag (e.g. inside
11103
+ // a datepicker/combobox). Must run before parent components call validate() on
11104
+ // their inner inputs — the validation framework matches via tag-or-attribute,
11105
+ // and parents trigger validation during their own updated() cycle, which can
11106
+ // precede this input's firstUpdated().
11107
+ if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
11108
+ this.setAttribute('auro-input', '');
11109
+ }
11110
+
11078
11111
  this.locale = this.domHandler.getLocale(this);
11079
11112
  notifyOnLangChange(this);
11113
+
11114
+ // Pre-compute lengthForType and date-object fields so a parent (e.g. datepicker)
11115
+ // calling our validate() synchronously during its own updated() cycle sees
11116
+ // populated values. Without this, range validation silently no-ops because
11117
+ // `max.length === lengthForType` fails when lengthForType is still undefined.
11118
+ // Rebuild util here (constructor seeded with en-US default) so configureDataForType's
11119
+ // locale-derived format lookup uses the actual locale just resolved above.
11120
+ this.util = new AuroInputUtilities({
11121
+ locale: this.locale,
11122
+ format: this.format
11123
+ });
11124
+ this.configureDataForType();
11125
+ this.syncDateValues();
11080
11126
  }
11081
11127
 
11082
11128
  disconnectedCallback() {
@@ -11096,10 +11142,6 @@ class BaseInput extends AuroElement {
11096
11142
  this.wrapperElement.addEventListener('click', this.handleClick);
11097
11143
  }
11098
11144
 
11099
- // 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);
11102
- }
11103
11145
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
11104
11146
 
11105
11147
  // use validity message override if declared when initializing the component
@@ -11146,6 +11188,12 @@ class BaseInput extends AuroElement {
11146
11188
  return;
11147
11189
  }
11148
11190
 
11191
+ // While configureAutoFormatting is running, imask's internal updateControl
11192
+ // writes el.value to align display with the masked value. A synthetic input
11193
+ // event from that write would re-enter handleInput → processCreditCard mid-setup
11194
+ // and clobber a Lit value that was just pushed by the parent combobox.
11195
+ if (component._configuringMask) return;
11196
+
11149
11197
  // If all guard clauses are passed, dispatch the event
11150
11198
  const inputEvent = new InputEvent('input', {
11151
11199
  bubbles: true,
@@ -11264,7 +11312,16 @@ class BaseInput extends AuroElement {
11264
11312
 
11265
11313
  if (formattedValue !== this.inputElement.value) {
11266
11314
  this.skipNextProgrammaticInputEvent = true;
11267
- if (formattedValue) {
11315
+ if (this.maskInstance && this.type !== 'date') {
11316
+ // Route through the mask so its _value and el.value stay in lock-step
11317
+ // (set value calls updateControl which writes el.value = displayValue).
11318
+ // Writing el.value directly leaves the mask thinking displayValue is
11319
+ // stale; _saveSelection on the next focus/click then warns. Date is
11320
+ // excluded because its formattedValue can be raw ISO when the calendar
11321
+ // is invalid, and re-masking through mm/dd/yyyy would truncate it and
11322
+ // flip validity from patternMismatch to tooShort.
11323
+ this.maskInstance.value = formattedValue || '';
11324
+ } else if (formattedValue) {
11268
11325
  this.inputElement.value = formattedValue;
11269
11326
  } else {
11270
11327
  this.inputElement.value = '';
@@ -11373,58 +11430,82 @@ class BaseInput extends AuroElement {
11373
11430
  * @returns {void}
11374
11431
  */
11375
11432
  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);
11433
+ // _configuringMask gates two things: external re-entry into this method
11434
+ // while setup is mid-flight (from property changes that call back here),
11435
+ // and the accept/complete listeners below — both need to ignore the mask
11436
+ // events fired by our own value-restore step.
11437
+ if (this._configuringMask) return;
11438
+ this._configuringMask = true;
11439
+ try {
11440
+ // Destroy any prior mask so IMask can attach fresh under the new format.
11441
+ if (this.maskInstance) {
11442
+ this.maskInstance.destroy();
11403
11443
  }
11404
11444
 
11405
- this.skipNextProgrammaticInputEvent = true;
11406
- this.inputElement.value = '';
11407
-
11408
- this.maskInstance = IMask(this.inputElement, maskOptions);
11445
+ this.util.updateFormat(this.format);
11409
11446
 
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;
11447
+ const maskOptions = this.util.getMaskOptions(this.type, this.format);
11448
+
11449
+ if (this.inputElement && maskOptions.mask) {
11450
+ // Capture the current display so it can be re-applied after IMask
11451
+ // attaches. The restore at the bottom goes through maskInstance.value
11452
+ // (not inputElement.value directly) so the mask's internal state and
11453
+ // the input's displayed text stay in lock-step.
11454
+ let existingValue = this.inputElement.value;
11455
+
11456
+ // Format-change case (e.g. locale switch): existingValue is the OLD
11457
+ // mask's display string and may not parse under the new mask. When
11458
+ // we have a valid date model, rebuild the display from valueObject
11459
+ // (the canonical source) using the new mask's format function.
11460
+ if (
11461
+ this.util.isFullDateFormat(this.type, this.format) &&
11462
+ this.value &&
11463
+ dateFormatter.isValidDate(this.value) &&
11464
+ this.valueObject instanceof Date &&
11465
+ !Number.isNaN(this.valueObject.getTime()) &&
11466
+ typeof maskOptions.format === 'function'
11467
+ ) {
11468
+ existingValue = maskOptions.format(this.valueObject);
11414
11469
  }
11415
- });
11416
11470
 
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
- });
11471
+ // Clear before IMask attaches so the constructor seeds an empty
11472
+ // internal value. Otherwise IMask reads the stale unmasked string
11473
+ // and emits a spurious 'accept' before the restore below runs.
11474
+ this.skipNextProgrammaticInputEvent = true;
11475
+ this.inputElement.value = '';
11476
+
11477
+ this.maskInstance = IMask(this.inputElement, maskOptions);
11478
+
11479
+ // Mask fires 'accept' on every value change, including the restore
11480
+ // step below. Skip events fired during configureAutoFormatting so
11481
+ // we don't overwrite a value the parent just pushed.
11482
+ this.maskInstance.on('accept', () => {
11483
+ if (this._configuringMask) return;
11484
+ this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11485
+ if (this.type === "date") {
11486
+ this._rawMaskValue = this.maskInstance.value;
11487
+ }
11488
+ });
11423
11489
 
11424
- // Restore the stashed value through IMask so it's properly masked
11425
- if (existingValue) {
11426
- this.maskInstance.value = existingValue;
11490
+ // Mask fires 'complete' on the restore step below for any value that
11491
+ // happens to be a complete match. Same setup-suppression as 'accept'.
11492
+ this.maskInstance.on('complete', () => {
11493
+ if (this._configuringMask) return;
11494
+ this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11495
+ if (this.type === "date") {
11496
+ this._rawMaskValue = this.maskInstance.value;
11497
+ }
11498
+ });
11499
+
11500
+ // Write existingValue through the mask (not the input directly) so
11501
+ // the mask reformats it under the new rules and keeps its internal
11502
+ // _value aligned with the input's displayed text.
11503
+ if (existingValue) {
11504
+ this.maskInstance.value = existingValue;
11505
+ }
11427
11506
  }
11507
+ } finally {
11508
+ this._configuringMask = false;
11428
11509
  }
11429
11510
  }
11430
11511
 
@@ -11711,10 +11792,10 @@ class BaseInput extends AuroElement {
11711
11792
  },
11712
11793
  {
11713
11794
  name: 'Diners club',
11714
- regex: /^(?<num>36|38)\d{0}/u,
11795
+ regex: /^(?<num>30[0-5]|36|38)\d{0}/u,
11715
11796
  formatLength: 16,
11716
11797
  errorMessage: CreditCardValidationMessage,
11717
- cardIcon: 'credit-card',
11798
+ cardIcon: 'cc-dinersclub',
11718
11799
  maskFormat: "0000 000000 0000"
11719
11800
  },
11720
11801
  {
@@ -12124,7 +12205,7 @@ class AuroHelpText extends LitElement {
12124
12205
  }
12125
12206
  }
12126
12207
 
12127
- var formkitVersion = '202606141505';
12208
+ var formkitVersion = '202606190840';
12128
12209
 
12129
12210
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
12130
12211
  // See LICENSE in the project root for license information.