@aurodesignsystem-dev/auro-formkit 0.0.0-pr1506.1 → 0.0.0-pr1507.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 (49) hide show
  1. package/components/checkbox/demo/customize.min.js +1 -1
  2. package/components/checkbox/demo/getting-started.min.js +1 -1
  3. package/components/checkbox/demo/index.min.js +1 -1
  4. package/components/checkbox/dist/index.js +1 -1
  5. package/components/checkbox/dist/registered.js +1 -1
  6. package/components/combobox/demo/customize.min.js +268 -140
  7. package/components/combobox/demo/getting-started.min.js +268 -140
  8. package/components/combobox/demo/index.min.js +268 -140
  9. package/components/combobox/dist/auro-combobox.d.ts +0 -9
  10. package/components/combobox/dist/index.js +266 -138
  11. package/components/combobox/dist/registered.js +266 -138
  12. package/components/counter/demo/customize.min.js +2 -2
  13. package/components/counter/demo/index.min.js +2 -2
  14. package/components/counter/dist/index.js +2 -2
  15. package/components/counter/dist/registered.js +2 -2
  16. package/components/datepicker/demo/customize.min.js +149 -39
  17. package/components/datepicker/demo/index.min.js +149 -39
  18. package/components/datepicker/dist/index.js +149 -39
  19. package/components/datepicker/dist/registered.js +149 -39
  20. package/components/dropdown/demo/customize.min.js +1 -1
  21. package/components/dropdown/demo/getting-started.min.js +1 -1
  22. package/components/dropdown/demo/index.min.js +1 -1
  23. package/components/dropdown/dist/index.js +1 -1
  24. package/components/dropdown/dist/registered.js +1 -1
  25. package/components/form/demo/customize.min.js +570 -221
  26. package/components/form/demo/getting-started.min.js +570 -221
  27. package/components/form/demo/index.min.js +570 -221
  28. package/components/form/demo/registerDemoDeps.min.js +570 -221
  29. package/components/input/demo/customize.min.js +147 -36
  30. package/components/input/demo/getting-started.min.js +147 -36
  31. package/components/input/demo/index.min.js +147 -36
  32. package/components/input/dist/base-input.d.ts +48 -0
  33. package/components/input/dist/index.js +147 -36
  34. package/components/input/dist/registered.js +147 -36
  35. package/components/menu/demo/index.min.js +2 -2
  36. package/components/menu/dist/index.js +2 -2
  37. package/components/menu/dist/registered.js +2 -2
  38. package/components/radio/demo/customize.min.js +1 -1
  39. package/components/radio/demo/getting-started.min.js +1 -1
  40. package/components/radio/demo/index.min.js +1 -1
  41. package/components/radio/dist/index.js +1 -1
  42. package/components/radio/dist/registered.js +1 -1
  43. package/components/select/demo/customize.min.js +4 -4
  44. package/components/select/demo/getting-started.min.js +4 -4
  45. package/components/select/demo/index.min.js +4 -4
  46. package/components/select/dist/index.js +2 -2
  47. package/components/select/dist/registered.js +2 -2
  48. package/custom-elements.json +264 -20
  49. package/package.json +1 -1
@@ -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
 
@@ -10483,8 +10482,10 @@ class BaseInput extends AuroElement {
10483
10482
  this.layout = 'classic';
10484
10483
  this.locale = 'en-US';
10485
10484
  this.max = undefined;
10485
+ this._maxObject = undefined;
10486
10486
  this.maxLength = undefined;
10487
10487
  this.min = undefined;
10488
+ this._minObject = undefined;
10488
10489
  this.minLength = undefined;
10489
10490
  this.required = false;
10490
10491
  this.onDark = false;
@@ -10492,6 +10493,7 @@ class BaseInput extends AuroElement {
10492
10493
  this.size = 'lg';
10493
10494
  this.shape = 'classic';
10494
10495
  this.value = undefined;
10496
+ this._valueObject = undefined;
10495
10497
 
10496
10498
  this._initializePrivateDefaults();
10497
10499
  }
@@ -11031,7 +11033,7 @@ class BaseInput extends AuroElement {
11031
11033
  * @returns {Date|undefined}
11032
11034
  */
11033
11035
  get valueObject() {
11034
- return this.value && dateFormatter.isValidDate(this.value) ? dateFormatter.stringToDateInstance(this.value) : undefined;
11036
+ return this._valueObject || this._computeDateObjectFallback(this.value);
11035
11037
  }
11036
11038
 
11037
11039
  /**
@@ -11039,7 +11041,7 @@ class BaseInput extends AuroElement {
11039
11041
  * @returns {Date|undefined}
11040
11042
  */
11041
11043
  get minObject() {
11042
- return this.min && dateFormatter.isValidDate(this.min) ? dateFormatter.stringToDateInstance(this.min) : undefined;
11044
+ return this._minObject || this._computeDateObjectFallback(this.min);
11043
11045
  }
11044
11046
 
11045
11047
  /**
@@ -11047,7 +11049,50 @@ class BaseInput extends AuroElement {
11047
11049
  * @returns {Date|undefined}
11048
11050
  */
11049
11051
  get maxObject() {
11050
- return this.max && dateFormatter.isValidDate(this.max) ? dateFormatter.stringToDateInstance(this.max) : undefined;
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);
11077
+ }
11078
+
11079
+ /**
11080
+ * Internal setter for readonly date object properties.
11081
+ * @private
11082
+ * @param {'valueObject'|'minObject'|'maxObject'} propertyName - Public object property name.
11083
+ * @param {Date|undefined} propertyValue - Value to assign.
11084
+ * @returns {void}
11085
+ */
11086
+ setDateObjectProperty(propertyName, propertyValue) {
11087
+ const internalPropertyName = `_${propertyName}`;
11088
+ const previousValue = this[internalPropertyName];
11089
+
11090
+ if (previousValue === propertyValue) {
11091
+ return;
11092
+ }
11093
+
11094
+ this[internalPropertyName] = propertyValue;
11095
+ this.requestUpdate(propertyName, previousValue);
11051
11096
  }
11052
11097
 
11053
11098
  connectedCallback() {
@@ -11076,6 +11121,7 @@ class BaseInput extends AuroElement {
11076
11121
  format: this.format
11077
11122
  });
11078
11123
  this.configureDataForType();
11124
+ this.syncDateValues();
11079
11125
  }
11080
11126
 
11081
11127
  disconnectedCallback() {
@@ -11095,6 +11141,10 @@ class BaseInput extends AuroElement {
11095
11141
  this.wrapperElement.addEventListener('click', this.handleClick);
11096
11142
  }
11097
11143
 
11144
+ // add attribute for query selectors when auro-input is registered under a custom name
11145
+ if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
11146
+ this.setAttribute('auro-input', '');
11147
+ }
11098
11148
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
11099
11149
 
11100
11150
  // use validity message override if declared when initializing the component
@@ -11105,6 +11155,7 @@ class BaseInput extends AuroElement {
11105
11155
  this.setCustomHelpTextMessage();
11106
11156
  this.configureAutoFormatting();
11107
11157
  this.configureDataForType();
11158
+ this.syncDateValues();
11108
11159
  }
11109
11160
 
11110
11161
  /**
@@ -11241,6 +11292,8 @@ class BaseInput extends AuroElement {
11241
11292
  this.configureDataForType();
11242
11293
  }
11243
11294
 
11295
+ this.syncDateValues(changedProperties);
11296
+
11244
11297
  if (changedProperties.has('value')) {
11245
11298
  if (this.value && this.value.length > 0) {
11246
11299
  this.hasValue = true;
@@ -11262,14 +11315,14 @@ class BaseInput extends AuroElement {
11262
11315
 
11263
11316
  if (formattedValue !== this.inputElement.value) {
11264
11317
  this.skipNextProgrammaticInputEvent = true;
11265
- if (this.maskInstance && this.type !== 'date') {
11318
+ if (this.maskInstance && this.type === 'credit-card') {
11266
11319
  // Route through the mask so its _value and el.value stay in lock-step
11267
11320
  // (set value calls updateControl which writes el.value = displayValue).
11268
11321
  // Writing el.value directly leaves the mask thinking displayValue is
11269
- // stale; _saveSelection on the next focus/click then warns. Date is
11270
- // excluded because its formattedValue can be raw ISO when the calendar
11271
- // is invalid, and re-masking through mm/dd/yyyy would truncate it and
11272
- // flip validity from patternMismatch to tooShort.
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.
11273
11326
  this.maskInstance.value = formattedValue || '';
11274
11327
  } else if (formattedValue) {
11275
11328
  this.inputElement.value = formattedValue;
@@ -11311,60 +11364,120 @@ class BaseInput extends AuroElement {
11311
11364
  }));
11312
11365
  }
11313
11366
 
11367
+
11368
+ /**
11369
+ * Synchronizes the ISO string values and Date object representations for date-related properties.
11370
+ * This keeps the model and display values aligned when either side changes.
11371
+ *
11372
+ * When a full date format is in use, this method updates `value`, `min`, and `max` from their corresponding
11373
+ * Date objects or vice versa, based on which properties have changed. It only runs when the current configuration
11374
+ * represents a full year/month/day date format.
11375
+ *
11376
+ * @param {Map<string, unknown>|undefined} [changedProperties=undefined] - Optional map of changed properties used to limit which values are synchronized.
11377
+ * @returns {void}
11378
+ * @private
11379
+ */
11380
+ syncDateValues(changedProperties = undefined) {
11381
+ if (!this.util.isFullDateFormat(this.type, this.format)) {
11382
+ return;
11383
+ }
11384
+
11385
+ this.syncSingleDateValue(changedProperties, 'valueObject', 'value');
11386
+ this.syncSingleDateValue(changedProperties, 'minObject', 'min');
11387
+ this.syncSingleDateValue(changedProperties, 'maxObject', 'max');
11388
+ }
11389
+
11390
+ /**
11391
+ * Synchronizes one date object/string property pair.
11392
+ * @private
11393
+ * @param {Map<string, unknown>|undefined} changedProperties - Map of changed properties from Lit.
11394
+ * @param {string} objectProperty - Date object property name.
11395
+ * @param {string} valueProperty - ISO string property name.
11396
+ * @returns {void}
11397
+ */
11398
+ syncSingleDateValue(changedProperties, objectProperty, valueProperty) {
11399
+ const objectPropertyChanged = !changedProperties || changedProperties.has(objectProperty);
11400
+
11401
+ // objectProperty wins over valueProperty when both changed
11402
+ if (objectPropertyChanged && this[objectProperty]) {
11403
+ this[valueProperty] = dateFormatter.toISOFormatString(this[objectProperty]);
11404
+ return;
11405
+ }
11406
+
11407
+ const valuePropertyChanged = !changedProperties || changedProperties.has(valueProperty);
11408
+ if (!valuePropertyChanged) {
11409
+ return;
11410
+ }
11411
+
11412
+ // 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)
11413
+ if (
11414
+ changedProperties &&
11415
+ valueProperty === 'value' &&
11416
+ changedProperties.get('value') === undefined &&
11417
+ this[objectProperty] instanceof Date &&
11418
+ this[valueProperty] === dateFormatter.toISOFormatString(this[objectProperty])
11419
+ ) {
11420
+ return;
11421
+ }
11422
+
11423
+ if (dateFormatter.isValidDate(this[valueProperty])) {
11424
+ this.setDateObjectProperty(objectProperty, dateFormatter.stringToDateInstance(this[valueProperty]));
11425
+ } else {
11426
+ this.setDateObjectProperty(objectProperty, undefined);
11427
+ }
11428
+ }
11429
+
11314
11430
  /**
11315
11431
  * Sets up IMasks and logic based on auto-formatting requirements.
11316
11432
  * @private
11317
11433
  * @returns {void}
11318
11434
  */
11319
11435
  configureAutoFormatting() {
11320
- // _configuringMask gates two things: external re-entry into this method
11321
- // while setup is mid-flight (from property changes that call back here),
11322
- // and the accept/complete listeners below both need to ignore the mask
11323
- // events fired by our own value-restore step.
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.
11324
11440
  if (this._configuringMask) return;
11325
11441
  this._configuringMask = true;
11326
11442
  try {
11327
- // Destroy any prior mask so IMask can attach fresh under the new format.
11328
11443
  if (this.maskInstance) {
11329
11444
  this.maskInstance.destroy();
11330
11445
  }
11331
11446
 
11447
+ // Pass new format to util
11332
11448
  this.util.updateFormat(this.format);
11333
11449
 
11334
11450
  const maskOptions = this.util.getMaskOptions(this.type, this.format);
11335
11451
 
11336
11452
  if (this.inputElement && maskOptions.mask) {
11337
- // Capture the current display so it can be re-applied after IMask
11338
- // attaches. The restore at the bottom goes through maskInstance.value
11339
- // (not inputElement.value directly) so the mask's internal state and
11340
- // the input's displayed text stay in lock-step.
11341
- let existingValue = this.inputElement.value;
11342
11453
 
11343
- // Format-change case (e.g. locale switch): existingValue is the OLD
11344
- // mask's display string and may not parse under the new mask. When
11345
- // we have a valid date model, rebuild the display from valueObject
11346
- // (the canonical source) using the new mask's format function.
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;
11347
11461
  if (
11348
11462
  this.util.isFullDateFormat(this.type, this.format) &&
11349
11463
  this.value &&
11350
- this.valueObject &&
11464
+ dateFormatter.isValidDate(this.value) &&
11465
+ this.valueObject instanceof Date &&
11466
+ !Number.isNaN(this.valueObject.getTime()) &&
11351
11467
  typeof maskOptions.format === 'function'
11352
11468
  ) {
11353
11469
  existingValue = maskOptions.format(this.valueObject);
11354
11470
  }
11355
11471
 
11356
- // Clear before IMask attaches so the constructor seeds an empty
11357
- // internal value. Otherwise IMask reads the stale unmasked string
11358
- // and emits a spurious 'accept' before the restore below runs.
11359
11472
  this.skipNextProgrammaticInputEvent = true;
11360
11473
  this.inputElement.value = '';
11361
11474
 
11362
11475
  this.maskInstance = IMask(this.inputElement, maskOptions);
11363
11476
 
11364
- // Mask fires 'accept' on every value change, including the restore
11365
- // step below. Skip events fired during configureAutoFormatting so
11366
- // we don't overwrite a value the parent just pushed.
11367
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.
11368
11481
  if (this._configuringMask) return;
11369
11482
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11370
11483
  if (this.type === "date") {
@@ -11372,8 +11485,6 @@ class BaseInput extends AuroElement {
11372
11485
  }
11373
11486
  });
11374
11487
 
11375
- // Mask fires 'complete' on the restore step below for any value that
11376
- // happens to be a complete match. Same setup-suppression as 'accept'.
11377
11488
  this.maskInstance.on('complete', () => {
11378
11489
  if (this._configuringMask) return;
11379
11490
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
@@ -11382,9 +11493,7 @@ class BaseInput extends AuroElement {
11382
11493
  }
11383
11494
  });
11384
11495
 
11385
- // Write existingValue through the mask (not the input directly) so
11386
- // the mask reformats it under the new rules and keeps its internal
11387
- // _value aligned with the input's displayed text.
11496
+ // Restore the stashed value through IMask so it's properly masked
11388
11497
  if (existingValue) {
11389
11498
  this.maskInstance.value = existingValue;
11390
11499
  }
@@ -11552,6 +11661,7 @@ class BaseInput extends AuroElement {
11552
11661
  */
11553
11662
  reset() {
11554
11663
  this.value = undefined;
11664
+ this.setDateObjectProperty('valueObject', undefined);
11555
11665
  this.validation.reset(this);
11556
11666
  }
11557
11667
 
@@ -11560,6 +11670,7 @@ class BaseInput extends AuroElement {
11560
11670
  */
11561
11671
  clear() {
11562
11672
  this.value = undefined;
11673
+ this.setDateObjectProperty('valueObject', undefined);
11563
11674
  }
11564
11675
 
11565
11676
  /**
@@ -12088,7 +12199,7 @@ class AuroHelpText extends LitElement {
12088
12199
  }
12089
12200
  }
12090
12201
 
12091
- var formkitVersion = '202606192004';
12202
+ var formkitVersion = '202606192121';
12092
12203
 
12093
12204
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
12094
12205
  // See LICENSE in the project root for license information.
@@ -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
 
@@ -10483,8 +10482,10 @@ class BaseInput extends AuroElement {
10483
10482
  this.layout = 'classic';
10484
10483
  this.locale = 'en-US';
10485
10484
  this.max = undefined;
10485
+ this._maxObject = undefined;
10486
10486
  this.maxLength = undefined;
10487
10487
  this.min = undefined;
10488
+ this._minObject = undefined;
10488
10489
  this.minLength = undefined;
10489
10490
  this.required = false;
10490
10491
  this.onDark = false;
@@ -10492,6 +10493,7 @@ class BaseInput extends AuroElement {
10492
10493
  this.size = 'lg';
10493
10494
  this.shape = 'classic';
10494
10495
  this.value = undefined;
10496
+ this._valueObject = undefined;
10495
10497
 
10496
10498
  this._initializePrivateDefaults();
10497
10499
  }
@@ -11031,7 +11033,7 @@ class BaseInput extends AuroElement {
11031
11033
  * @returns {Date|undefined}
11032
11034
  */
11033
11035
  get valueObject() {
11034
- return this.value && dateFormatter.isValidDate(this.value) ? dateFormatter.stringToDateInstance(this.value) : undefined;
11036
+ return this._valueObject || this._computeDateObjectFallback(this.value);
11035
11037
  }
11036
11038
 
11037
11039
  /**
@@ -11039,7 +11041,7 @@ class BaseInput extends AuroElement {
11039
11041
  * @returns {Date|undefined}
11040
11042
  */
11041
11043
  get minObject() {
11042
- return this.min && dateFormatter.isValidDate(this.min) ? dateFormatter.stringToDateInstance(this.min) : undefined;
11044
+ return this._minObject || this._computeDateObjectFallback(this.min);
11043
11045
  }
11044
11046
 
11045
11047
  /**
@@ -11047,7 +11049,50 @@ class BaseInput extends AuroElement {
11047
11049
  * @returns {Date|undefined}
11048
11050
  */
11049
11051
  get maxObject() {
11050
- return this.max && dateFormatter.isValidDate(this.max) ? dateFormatter.stringToDateInstance(this.max) : undefined;
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);
11077
+ }
11078
+
11079
+ /**
11080
+ * Internal setter for readonly date object properties.
11081
+ * @private
11082
+ * @param {'valueObject'|'minObject'|'maxObject'} propertyName - Public object property name.
11083
+ * @param {Date|undefined} propertyValue - Value to assign.
11084
+ * @returns {void}
11085
+ */
11086
+ setDateObjectProperty(propertyName, propertyValue) {
11087
+ const internalPropertyName = `_${propertyName}`;
11088
+ const previousValue = this[internalPropertyName];
11089
+
11090
+ if (previousValue === propertyValue) {
11091
+ return;
11092
+ }
11093
+
11094
+ this[internalPropertyName] = propertyValue;
11095
+ this.requestUpdate(propertyName, previousValue);
11051
11096
  }
11052
11097
 
11053
11098
  connectedCallback() {
@@ -11076,6 +11121,7 @@ class BaseInput extends AuroElement {
11076
11121
  format: this.format
11077
11122
  });
11078
11123
  this.configureDataForType();
11124
+ this.syncDateValues();
11079
11125
  }
11080
11126
 
11081
11127
  disconnectedCallback() {
@@ -11095,6 +11141,10 @@ class BaseInput extends AuroElement {
11095
11141
  this.wrapperElement.addEventListener('click', this.handleClick);
11096
11142
  }
11097
11143
 
11144
+ // add attribute for query selectors when auro-input is registered under a custom name
11145
+ if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
11146
+ this.setAttribute('auro-input', '');
11147
+ }
11098
11148
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
11099
11149
 
11100
11150
  // use validity message override if declared when initializing the component
@@ -11105,6 +11155,7 @@ class BaseInput extends AuroElement {
11105
11155
  this.setCustomHelpTextMessage();
11106
11156
  this.configureAutoFormatting();
11107
11157
  this.configureDataForType();
11158
+ this.syncDateValues();
11108
11159
  }
11109
11160
 
11110
11161
  /**
@@ -11241,6 +11292,8 @@ class BaseInput extends AuroElement {
11241
11292
  this.configureDataForType();
11242
11293
  }
11243
11294
 
11295
+ this.syncDateValues(changedProperties);
11296
+
11244
11297
  if (changedProperties.has('value')) {
11245
11298
  if (this.value && this.value.length > 0) {
11246
11299
  this.hasValue = true;
@@ -11262,14 +11315,14 @@ class BaseInput extends AuroElement {
11262
11315
 
11263
11316
  if (formattedValue !== this.inputElement.value) {
11264
11317
  this.skipNextProgrammaticInputEvent = true;
11265
- if (this.maskInstance && this.type !== 'date') {
11318
+ if (this.maskInstance && this.type === 'credit-card') {
11266
11319
  // Route through the mask so its _value and el.value stay in lock-step
11267
11320
  // (set value calls updateControl which writes el.value = displayValue).
11268
11321
  // Writing el.value directly leaves the mask thinking displayValue is
11269
- // stale; _saveSelection on the next focus/click then warns. Date is
11270
- // excluded because its formattedValue can be raw ISO when the calendar
11271
- // is invalid, and re-masking through mm/dd/yyyy would truncate it and
11272
- // flip validity from patternMismatch to tooShort.
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.
11273
11326
  this.maskInstance.value = formattedValue || '';
11274
11327
  } else if (formattedValue) {
11275
11328
  this.inputElement.value = formattedValue;
@@ -11311,60 +11364,120 @@ class BaseInput extends AuroElement {
11311
11364
  }));
11312
11365
  }
11313
11366
 
11367
+
11368
+ /**
11369
+ * Synchronizes the ISO string values and Date object representations for date-related properties.
11370
+ * This keeps the model and display values aligned when either side changes.
11371
+ *
11372
+ * When a full date format is in use, this method updates `value`, `min`, and `max` from their corresponding
11373
+ * Date objects or vice versa, based on which properties have changed. It only runs when the current configuration
11374
+ * represents a full year/month/day date format.
11375
+ *
11376
+ * @param {Map<string, unknown>|undefined} [changedProperties=undefined] - Optional map of changed properties used to limit which values are synchronized.
11377
+ * @returns {void}
11378
+ * @private
11379
+ */
11380
+ syncDateValues(changedProperties = undefined) {
11381
+ if (!this.util.isFullDateFormat(this.type, this.format)) {
11382
+ return;
11383
+ }
11384
+
11385
+ this.syncSingleDateValue(changedProperties, 'valueObject', 'value');
11386
+ this.syncSingleDateValue(changedProperties, 'minObject', 'min');
11387
+ this.syncSingleDateValue(changedProperties, 'maxObject', 'max');
11388
+ }
11389
+
11390
+ /**
11391
+ * Synchronizes one date object/string property pair.
11392
+ * @private
11393
+ * @param {Map<string, unknown>|undefined} changedProperties - Map of changed properties from Lit.
11394
+ * @param {string} objectProperty - Date object property name.
11395
+ * @param {string} valueProperty - ISO string property name.
11396
+ * @returns {void}
11397
+ */
11398
+ syncSingleDateValue(changedProperties, objectProperty, valueProperty) {
11399
+ const objectPropertyChanged = !changedProperties || changedProperties.has(objectProperty);
11400
+
11401
+ // objectProperty wins over valueProperty when both changed
11402
+ if (objectPropertyChanged && this[objectProperty]) {
11403
+ this[valueProperty] = dateFormatter.toISOFormatString(this[objectProperty]);
11404
+ return;
11405
+ }
11406
+
11407
+ const valuePropertyChanged = !changedProperties || changedProperties.has(valueProperty);
11408
+ if (!valuePropertyChanged) {
11409
+ return;
11410
+ }
11411
+
11412
+ // 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)
11413
+ if (
11414
+ changedProperties &&
11415
+ valueProperty === 'value' &&
11416
+ changedProperties.get('value') === undefined &&
11417
+ this[objectProperty] instanceof Date &&
11418
+ this[valueProperty] === dateFormatter.toISOFormatString(this[objectProperty])
11419
+ ) {
11420
+ return;
11421
+ }
11422
+
11423
+ if (dateFormatter.isValidDate(this[valueProperty])) {
11424
+ this.setDateObjectProperty(objectProperty, dateFormatter.stringToDateInstance(this[valueProperty]));
11425
+ } else {
11426
+ this.setDateObjectProperty(objectProperty, undefined);
11427
+ }
11428
+ }
11429
+
11314
11430
  /**
11315
11431
  * Sets up IMasks and logic based on auto-formatting requirements.
11316
11432
  * @private
11317
11433
  * @returns {void}
11318
11434
  */
11319
11435
  configureAutoFormatting() {
11320
- // _configuringMask gates two things: external re-entry into this method
11321
- // while setup is mid-flight (from property changes that call back here),
11322
- // and the accept/complete listeners below both need to ignore the mask
11323
- // events fired by our own value-restore step.
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.
11324
11440
  if (this._configuringMask) return;
11325
11441
  this._configuringMask = true;
11326
11442
  try {
11327
- // Destroy any prior mask so IMask can attach fresh under the new format.
11328
11443
  if (this.maskInstance) {
11329
11444
  this.maskInstance.destroy();
11330
11445
  }
11331
11446
 
11447
+ // Pass new format to util
11332
11448
  this.util.updateFormat(this.format);
11333
11449
 
11334
11450
  const maskOptions = this.util.getMaskOptions(this.type, this.format);
11335
11451
 
11336
11452
  if (this.inputElement && maskOptions.mask) {
11337
- // Capture the current display so it can be re-applied after IMask
11338
- // attaches. The restore at the bottom goes through maskInstance.value
11339
- // (not inputElement.value directly) so the mask's internal state and
11340
- // the input's displayed text stay in lock-step.
11341
- let existingValue = this.inputElement.value;
11342
11453
 
11343
- // Format-change case (e.g. locale switch): existingValue is the OLD
11344
- // mask's display string and may not parse under the new mask. When
11345
- // we have a valid date model, rebuild the display from valueObject
11346
- // (the canonical source) using the new mask's format function.
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;
11347
11461
  if (
11348
11462
  this.util.isFullDateFormat(this.type, this.format) &&
11349
11463
  this.value &&
11350
- this.valueObject &&
11464
+ dateFormatter.isValidDate(this.value) &&
11465
+ this.valueObject instanceof Date &&
11466
+ !Number.isNaN(this.valueObject.getTime()) &&
11351
11467
  typeof maskOptions.format === 'function'
11352
11468
  ) {
11353
11469
  existingValue = maskOptions.format(this.valueObject);
11354
11470
  }
11355
11471
 
11356
- // Clear before IMask attaches so the constructor seeds an empty
11357
- // internal value. Otherwise IMask reads the stale unmasked string
11358
- // and emits a spurious 'accept' before the restore below runs.
11359
11472
  this.skipNextProgrammaticInputEvent = true;
11360
11473
  this.inputElement.value = '';
11361
11474
 
11362
11475
  this.maskInstance = IMask(this.inputElement, maskOptions);
11363
11476
 
11364
- // Mask fires 'accept' on every value change, including the restore
11365
- // step below. Skip events fired during configureAutoFormatting so
11366
- // we don't overwrite a value the parent just pushed.
11367
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.
11368
11481
  if (this._configuringMask) return;
11369
11482
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
11370
11483
  if (this.type === "date") {
@@ -11372,8 +11485,6 @@ class BaseInput extends AuroElement {
11372
11485
  }
11373
11486
  });
11374
11487
 
11375
- // Mask fires 'complete' on the restore step below for any value that
11376
- // happens to be a complete match. Same setup-suppression as 'accept'.
11377
11488
  this.maskInstance.on('complete', () => {
11378
11489
  if (this._configuringMask) return;
11379
11490
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
@@ -11382,9 +11493,7 @@ class BaseInput extends AuroElement {
11382
11493
  }
11383
11494
  });
11384
11495
 
11385
- // Write existingValue through the mask (not the input directly) so
11386
- // the mask reformats it under the new rules and keeps its internal
11387
- // _value aligned with the input's displayed text.
11496
+ // Restore the stashed value through IMask so it's properly masked
11388
11497
  if (existingValue) {
11389
11498
  this.maskInstance.value = existingValue;
11390
11499
  }
@@ -11552,6 +11661,7 @@ class BaseInput extends AuroElement {
11552
11661
  */
11553
11662
  reset() {
11554
11663
  this.value = undefined;
11664
+ this.setDateObjectProperty('valueObject', undefined);
11555
11665
  this.validation.reset(this);
11556
11666
  }
11557
11667
 
@@ -11560,6 +11670,7 @@ class BaseInput extends AuroElement {
11560
11670
  */
11561
11671
  clear() {
11562
11672
  this.value = undefined;
11673
+ this.setDateObjectProperty('valueObject', undefined);
11563
11674
  }
11564
11675
 
11565
11676
  /**
@@ -12088,7 +12199,7 @@ class AuroHelpText extends LitElement {
12088
12199
  }
12089
12200
  }
12090
12201
 
12091
- var formkitVersion = '202606192004';
12202
+ var formkitVersion = '202606192121';
12092
12203
 
12093
12204
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
12094
12205
  // See LICENSE in the project root for license information.