@aurodesignsystem-dev/auro-formkit 0.0.0-pr1507.0 → 0.0.0-pr1508.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/components/checkbox/demo/customize.html +1 -2
  2. package/components/checkbox/demo/customize.min.js +1 -1
  3. package/components/checkbox/demo/getting-started.min.js +1 -1
  4. package/components/checkbox/demo/index.min.js +1 -1
  5. package/components/checkbox/dist/index.js +1 -1
  6. package/components/checkbox/dist/registered.js +1 -1
  7. package/components/combobox/demo/customize.html +1 -2
  8. package/components/combobox/demo/customize.md +108 -132
  9. package/components/combobox/demo/customize.min.js +150 -293
  10. package/components/combobox/demo/getting-started.min.js +150 -293
  11. package/components/combobox/demo/index.min.js +150 -293
  12. package/components/combobox/dist/auro-combobox.d.ts +9 -0
  13. package/components/combobox/dist/index.js +150 -293
  14. package/components/combobox/dist/registered.js +150 -293
  15. package/components/counter/demo/customize.min.js +10 -2
  16. package/components/counter/demo/index.min.js +10 -2
  17. package/components/counter/dist/index.js +10 -2
  18. package/components/counter/dist/registered.js +10 -2
  19. package/components/datepicker/demo/api.md +2 -0
  20. package/components/datepicker/demo/customize.js +0 -2
  21. package/components/datepicker/demo/customize.md +138 -38
  22. package/components/datepicker/demo/customize.min.js +59 -168
  23. package/components/datepicker/demo/index.min.js +59 -150
  24. package/components/datepicker/dist/index.js +59 -150
  25. package/components/datepicker/dist/registered.js +59 -150
  26. package/components/datepicker/dist/src/auro-datepicker.d.ts +2 -0
  27. package/components/dropdown/demo/customize.min.js +9 -1
  28. package/components/dropdown/demo/getting-started.min.js +9 -1
  29. package/components/dropdown/demo/index.min.js +9 -1
  30. package/components/dropdown/dist/auro-dropdown.d.ts +1 -0
  31. package/components/dropdown/dist/index.js +9 -1
  32. package/components/dropdown/dist/registered.js +9 -1
  33. package/components/form/demo/customize.html +6 -6
  34. package/components/form/demo/customize.js +19 -0
  35. package/components/form/demo/customize.md +203 -51
  36. package/components/form/demo/customize.min.js +707 -633
  37. package/components/form/demo/getting-started.min.js +635 -633
  38. package/components/form/demo/index.min.js +635 -633
  39. package/components/form/demo/registerDemoDeps.min.js +272 -596
  40. package/components/form/dist/auro-form.d.ts +122 -4
  41. package/components/form/dist/index.js +363 -37
  42. package/components/form/dist/registered.js +363 -37
  43. package/components/input/demo/customize.html +1 -2
  44. package/components/input/demo/customize.md +54 -53
  45. package/components/input/demo/customize.min.js +41 -147
  46. package/components/input/demo/getting-started.min.js +41 -147
  47. package/components/input/demo/index.min.js +41 -147
  48. package/components/input/dist/base-input.d.ts +1 -49
  49. package/components/input/dist/index.js +41 -147
  50. package/components/input/dist/registered.js +41 -147
  51. package/components/radio/demo/customize.min.js +1 -1
  52. package/components/radio/demo/getting-started.min.js +1 -1
  53. package/components/radio/demo/index.min.js +1 -1
  54. package/components/radio/dist/index.js +1 -1
  55. package/components/radio/dist/registered.js +1 -1
  56. package/components/select/demo/customize.html +1 -2
  57. package/components/select/demo/customize.min.js +10 -2
  58. package/components/select/demo/getting-started.min.js +10 -2
  59. package/components/select/demo/index.min.js +10 -2
  60. package/components/select/dist/index.js +10 -2
  61. package/components/select/dist/registered.js +10 -2
  62. package/custom-elements.json +1592 -1707
  63. package/package.json +1 -1
@@ -109,7 +109,7 @@ let AuroLibraryRuntimeUtils$b = class AuroLibraryRuntimeUtils {
109
109
  }
110
110
  };
111
111
 
112
- /* eslint-disable no-underscore-dangle, max-lines, object-property-newline */
112
+ /* eslint-disable no-underscore-dangle, max-lines, object-property-newline, dot-location */
113
113
 
114
114
 
115
115
  /**
@@ -117,7 +117,7 @@ let AuroLibraryRuntimeUtils$b = class AuroLibraryRuntimeUtils {
117
117
  * @property {string | number | boolean | string[] | null} value - The value of the form element.
118
118
  * @property {ValidityState} validity - The validity state of the form element, stored when fired from the form element.
119
119
  * @property {boolean} required - Whether the form element is required or not.
120
- * @property {HTMLElement} element - Whether the form element is required or not.
120
+ * @property {boolean} disabled - Whether the form element is currently disabled. Cached from the live attribute via the MutationObserver in `connectedCallback` and refreshed from `_handleAttributeMutations`.
121
121
  */
122
122
 
123
123
  /**
@@ -201,6 +201,31 @@ class AuroForm extends i$3 {
201
201
  */
202
202
  this.mutationObservers = [];
203
203
 
204
+ /**
205
+ * Captured initial (default) value per field `name`. Populated on first
206
+ * sight of each name in `_addElementToState` and preserved across
207
+ * subsequent `initializeState` cycles (slot change, rename, reset) so
208
+ * `_setInitialState` can detect user edits as `current !== initial`,
209
+ * matching HTML's `dirtyValueFlag` semantics.
210
+ * @private
211
+ * @type {Record<string, string | number | boolean | string[] | null | undefined>}
212
+ */
213
+ this._initialValues = {};
214
+
215
+ // Single subtree observer that watches `disabled` and `name` attribute
216
+ // changes across all tracked form elements. The `name` watch is required:
217
+ // without it, renaming a tracked field at runtime leaves a stale key in
218
+ // `formState` that `_isNameDisabled` cannot resolve, so a renamed-but-
219
+ // disabled field would re-appear in `.value`.
220
+ /**
221
+ * @private
222
+ * @type {MutationObserver | null}
223
+ */
224
+ this._attributeObserver = null;
225
+
226
+ /** @private */
227
+ this._handleAttributeMutations = this._handleAttributeMutations.bind(this);
228
+
204
229
  // Bind listeners
205
230
  /** @private */
206
231
  this.reset = this.reset.bind(this);
@@ -269,6 +294,45 @@ class AuroForm extends i$3 {
269
294
  return this._isInElementCollection(AuroForm.formElementTags, element);
270
295
  }
271
296
 
297
+ /**
298
+ * Whether a given element is currently disabled. Disabled controls are excluded
299
+ * from submission, validity, and initial-state checks per the HTML spec
300
+ * (section 4.10.19.2 "Enabling and disabling form controls":
301
+ * https://www.w3.org/TR/2011/WD-html5-20110113/association-of-controls-and-forms.html).
302
+ *
303
+ * Implementation note: we deliberately read only the attribute. Every Auro
304
+ * form element in `formElementTags` declares `disabled` with `reflect: true`,
305
+ * so the attribute and property stay in sync. Reading the attribute also
306
+ * lets the MutationObserver in `connectedCallback` (which is filtered to
307
+ * `['disabled', 'name']`) be the single source of truth for re-renders.
308
+ * If a future form-element type ships without attribute reflection, expand
309
+ * this helper to also read `element.disabled`.
310
+ * @param {HTMLElement | undefined | null} element - The element to check.
311
+ * @returns {boolean}
312
+ * @private
313
+ */
314
+ _isDisabled(element) {
315
+ return Boolean(element?.hasAttribute('disabled'));
316
+ }
317
+
318
+ /**
319
+ * Whether the tracked form element registered under `name` is currently disabled.
320
+ * See `_isDisabled` for the HTML-spec rationale behind excluding disabled
321
+ * controls from form state.
322
+ *
323
+ * Reads a cached flag on `formState[name]` populated by `_addElementToState`
324
+ * at registration and refreshed by `_handleAttributeMutations` whenever the
325
+ * element's `disabled` attribute toggles. The cache is fed by the same
326
+ * `hasAttribute('disabled')` read as `_isDisabled`, so the "future form-element
327
+ * type without attribute reflection" caveat documented there applies here too.
328
+ * @param {string} name - The `name` attribute used to register the element.
329
+ * @returns {boolean}
330
+ * @private
331
+ */
332
+ _isNameDisabled(name) {
333
+ return Boolean(this.formState[name]?.disabled);
334
+ }
335
+
272
336
  /**
273
337
  * Validates if an event is from a valid form element with a name.
274
338
  * @param {Event} event - The event to validate.
@@ -308,6 +372,10 @@ class AuroForm extends i$3 {
308
372
  */
309
373
  get value() {
310
374
  return Object.keys(this.formState).reduce((acc, key) => {
375
+ if (this._isNameDisabled(key)) {
376
+ return acc;
377
+ }
378
+
311
379
  acc[key] = this.formState[key].value;
312
380
  return acc;
313
381
  }, {});
@@ -331,23 +399,113 @@ class AuroForm extends i$3 {
331
399
  return this._resetElements;
332
400
  }
333
401
 
402
+ /**
403
+ * Raw constraint-validation check. Returns `true` when no enabled field
404
+ * has a validity error. Unlike the public `validity` getter, this does
405
+ * NOT gate on `isInitialState` — callers that need to make a decision
406
+ * based on the actual constraint state (submit-button enablement, the
407
+ * internal `submit()` gate) read this so a pre-filled valid form is
408
+ * correctly recognized as submittable at first render.
409
+ * @returns {boolean}
410
+ * @private
411
+ */
412
+ _isFormValid() {
413
+ return !Object.keys(this.formState).some((key) => {
414
+ if (this._isNameDisabled(key)) {
415
+ return false;
416
+ }
417
+
418
+ const formKey = this.formState[key];
419
+ // `null` validity means "not yet validated" — auro-input doesn't
420
+ // re-validate on every keystroke, so validity stays `null` between
421
+ // input events until blur. Treating `null` as invalid disables Submit
422
+ // the moment a user types into any field, which is the wrong UX.
423
+ // We treat a field as invalid in two cases:
424
+ // 1. validity is known-bad (non-null, non-'valid')
425
+ // 2. it's `required` and structurally empty — `valueMissing` is
426
+ // certain even without a validation pass.
427
+ // `submit()` calls `validate(true)` on every enabled field before
428
+ // reading `_isFormValid()`, so any not-yet-validated field that turns
429
+ // out to fail another constraint still blocks dispatch.
430
+ const knownInvalid = formKey.validity !== null && formKey.validity !== 'valid';
431
+ const requiredAndEmpty = formKey.required && this._normalizeEmpty(formKey.value) === null;
432
+ return knownInvalid || requiredAndEmpty;
433
+ });
434
+ }
435
+
436
+ /**
437
+ * Whether the reset button should be enabled. True when the form has
438
+ * diverged from its initial state (so the user can always return to
439
+ * defaults — even if the dirty value lives behind a now-disabled field),
440
+ * OR when any non-disabled field has a current value or captured initial
441
+ * value (covers pre-filled forms and user-cleared-back-to-empty cases).
442
+ * @returns {boolean}
443
+ * @private
444
+ */
445
+ _hasResetableState() {
446
+ // Form is dirty — always allow Reset, even if the dirt is on a field
447
+ // that has since been disabled. Without this branch, the user would
448
+ // have no UI path to return the form to its initial state.
449
+ if (!this._isInitialState) {
450
+ return true;
451
+ }
452
+
453
+ return Object.keys(this.formState).some((key) => {
454
+ if (this._isNameDisabled(key)) {
455
+ return false;
456
+ }
457
+ const current = this._normalizeEmpty(this.formState[key].value);
458
+ const initial = this._normalizeEmpty(this._initialValues[key]);
459
+ return current !== null || initial !== null;
460
+ });
461
+ }
462
+
463
+ /**
464
+ * Collapse empty representations to a single canonical `null`.
465
+ *
466
+ * `_addElementToState` captures `null` for a field that mounts without a
467
+ * `value` attribute (`element.value || element.getAttribute('value')` is
468
+ * falsy → resolves to `null`), but `sharedInputListener` later stores the
469
+ * raw `event.target.value` — which is `''` for a user-cleared text input.
470
+ * Without this normalization, backspacing back to empty would taint the
471
+ * form forever (`'' !== null`) and Reset would stay enabled with nothing
472
+ * to actually reset.
473
+ *
474
+ * `''`, `undefined`, and `[]` all collapse to `null`. The empty-array case
475
+ * covers checkbox-group, radio-group, and multiselect, where `[]` means
476
+ * "no selection" — semantically the same as `null`/`''`. Genuine values
477
+ * — including `0`, `false`, non-empty strings, and non-empty arrays —
478
+ * pass through unchanged so number, boolean, and populated multi-value
479
+ * fields still compare correctly.
480
+ * @param {*} value - Value to normalize.
481
+ * @returns {*}
482
+ * @private
483
+ */
484
+ _normalizeEmpty(value) {
485
+ if (value === '' || value === undefined) {
486
+ return null;
487
+ }
488
+ if (Array.isArray(value) && value.length === 0) {
489
+ return null;
490
+ }
491
+ return value;
492
+ }
493
+
334
494
  /**
335
495
  * Infer validity status based on current formState.
496
+ *
497
+ * Validity stays `null` while the form is in its initial state — this is
498
+ * the "stay quiet until the user interacts" UX contract that consumers
499
+ * depend on to delay error indicators. Code that needs the raw
500
+ * constraint-validation result regardless of interaction (e.g.,
501
+ * submit-button enablement) should call `_isFormValid()` directly.
336
502
  * @private
337
503
  */
338
504
  _calculateValidity() {
339
505
  if (this.isInitialState) {
340
506
  this._validity = null;
341
507
  } else {
342
- // go through validity states and return the first invalid state (if any)
343
- const invalidKey = Object.keys(this.formState).
344
- find((key) => {
345
- const formKey = this.formState[key];
346
- // these are NOT extra parens
347
- // eslint-disable-next-line no-extra-parens
348
- return (formKey.validity !== 'valid' && formKey.required) || (formKey.validity !== 'valid' && formKey.value !== null);
349
- });
350
- this._validity = invalidKey ? 'invalid' : 'valid';
508
+ this._validity = this._isFormValid() ? 'valid' : 'invalid';
351
509
  }
352
510
  }
353
511
 
@@ -363,20 +521,38 @@ class AuroForm extends i$3 {
363
521
  }
364
522
 
365
523
  /**
366
- * Determines whether the form is in its initial (untouched) state and updates `_isInitialState` accordingly.
524
+ * Determines whether the form is in its initial (untouched) state.
525
+ *
526
+ * A field is tainted if either:
527
+ * - its value differs from the value captured on first render, OR
528
+ * - its validity is failing (anything other than `null` or `'valid'`).
529
+ *
530
+ * Validity acts as a backup signal: it catches users who interact with a
531
+ * field without changing its value (e.g., focusing and blurring a required
532
+ * field). We skip `null` (not yet validated) and `'valid'` (the default
533
+ * after Auro's auto-validation on mount) because neither proves the user
534
+ * touched anything.
367
535
  * @returns {void}
368
536
  * @private
369
537
  */
370
538
  _setInitialState() {
371
- const anyTainted = Object.keys(this.formState).some((key) => this.formState[key].validity !== null || this.formState[key].value !== null);
539
+ const anyTainted = Object.keys(this.formState).some((key) => {
540
+ // Normalize empty values so a freshly-captured `null` (no `value`
541
+ // attribute at mount) and a user-cleared `''` (input emptied via
542
+ // backspace) compare equal. Without this, backspacing back to an
543
+ // empty field leaves the form permanently tainted.
544
+ const initialValue = this._normalizeEmpty(this._initialValues[key]);
545
+ const currentValue = this._normalizeEmpty(this.formState[key].value);
546
+ const fieldValidity = this.formState[key].validity;
547
+ // eslint-disable-next-line no-extra-parens
548
+ return currentValue !== initialValue || (fieldValidity !== null && fieldValidity !== 'valid');
549
+ });
372
550
 
373
551
  this._isInitialState = !anyTainted;
374
-
375
- this._resetElements.forEach((resetElement) => {
376
- if (resetElement.hasAttribute("disabled")) {
377
- resetElement.removeAttribute("disabled");
378
- }
379
- });
552
+ // Button state is owned by setDisabledStateOnButtons (called from updated()
553
+ // and reset()). Touching resetElement.disabled here causes a visible flicker
554
+ // in reset(), where the final setDisabledStateOnButtons is deferred behind
555
+ // an extra updateComplete.
380
556
  }
381
557
 
382
558
  /**
@@ -394,19 +570,19 @@ class AuroForm extends i$3 {
394
570
  */
395
571
  setDisabledStateOnButtons() {
396
572
  this._resetElements.forEach((element) => {
397
- if (this.isInitialState) {
398
- element.setAttribute("disabled", "");
399
- } else {
400
- element.removeAttribute("disabled");
401
- }
573
+ // Reset is meaningful whenever any non-disabled field has a current
574
+ // value OR a captured default value — i.e., whenever the click would
575
+ // either clear something or restore a default.
576
+ element.disabled = !this._hasResetableState();
402
577
  });
403
578
 
404
579
  this._submitElements.forEach((element) => {
405
- if (this.isInitialState || this.validity !== "valid") {
406
- element.setAttribute("disabled", "");
407
- } else {
408
- element.removeAttribute("disabled");
409
- }
580
+ // Submit enablement reads raw validity (not the gated public getter)
581
+ // so a pre-filled valid form is submittable at first render — the
582
+ // public `validity` stays `null` during initial state to keep error
583
+ // indicators quiet until the user interacts, but the button decision
584
+ // bypasses that gate.
585
+ element.disabled = !this._isFormValid();
410
586
  });
411
587
  }
412
588
 
@@ -452,9 +628,18 @@ class AuroForm extends i$3 {
452
628
  value: element.value || element.getAttribute('value'),
453
629
  validity: element.validity || null,
454
630
  required: element.hasAttribute('required'),
455
- // element
631
+ disabled: element.hasAttribute('disabled'),
456
632
  };
457
633
 
634
+ // Capture the initial (default) value once per name. Use `in` rather
635
+ // than `??=` so a captured `null` (an empty field at first sight) is
636
+ // preserved across rename/slot/reset cycles — `??=` would treat the
637
+ // stored `null` as nullish and overwrite it with whatever value the
638
+ // field has now, defeating the `current !== initial` taint check.
639
+ if (!(targetName in this._initialValues)) {
640
+ this._initialValues[targetName] = this.formState[targetName].value;
641
+ }
642
+
458
643
  this._elements.push(element);
459
644
  }
460
645
 
@@ -491,6 +676,15 @@ class AuroForm extends i$3 {
491
676
  }
492
677
  });
493
678
 
679
+ // Drop captured initial values for fields that no longer exist in the form.
680
+ // Rename migration in _handleAttributeMutations has already re-keyed surviving
681
+ // fields, so anything left here is a field that was removed from the DOM.
682
+ for (const key of Object.keys(this._initialValues)) {
683
+ if (!(key in this.formState)) {
684
+ delete this._initialValues[key];
685
+ }
686
+ }
687
+
494
688
  this.dispatchEvent(new Event('change', {
495
689
  bubbles: true,
496
690
  composed: true,
@@ -535,16 +729,21 @@ class AuroForm extends i$3 {
535
729
  * @returns {Promise<void>}
536
730
  */
537
731
  async submit() {
538
- // Force validation on ALL elements
539
- this._elements.forEach((element) => {
540
- element.validate(true);
541
- });
732
+ // Force validation on all enabled elements. Disabled fields are skipped
733
+ // because disabled controls are not validated nor submitted per the HTML spec.
734
+ this._elements.
735
+ filter((element) => !this._isDisabled(element)).
736
+ forEach((element) => {
737
+ element.validate(true);
738
+ });
542
739
 
543
740
  // Wait for validation to complete and formState to update
544
741
  await this.updateComplete;
545
742
 
546
- // Only dispatch submit event if form is valid
547
- if (this.validity === 'valid') {
743
+ // Gate on raw constraint-validation rather than the public `validity`
744
+ // getter (which is `null` while in initial state). A pre-filled valid
745
+ // form should be submittable without a prior user edit.
746
+ if (this._isFormValid()) {
548
747
  this.dispatchEvent(new CustomEvent('submit', {
549
748
  bubbles: true,
550
749
  composed: true,
@@ -614,7 +813,14 @@ class AuroForm extends i$3 {
614
813
  this._addElementToState(event.target);
615
814
  }
616
815
 
617
- this.formState[targetName].validity = event.detail.validity;
816
+ // `auroFormElement-validated` can fire with `detail.validity === undefined`
817
+ // when auro-input's updated() lifecycle invokes `validate()` mid-edit but
818
+ // the validation framework's gating conditions (not focused, touched-or-
819
+ // has-value) aren't met — the dispatch still fires, just with the current
820
+ // (untouched) validity. Normalize to `null` so the rest of the form treats
821
+ // "no known status" identically whether it came from `_addElementToState`
822
+ // at mount or from this passthrough mid-typing.
823
+ this.formState[targetName].validity = event.detail.validity ?? null;
618
824
  this._calculateValidity();
619
825
  this.requestUpdate('formState');
620
826
  }
@@ -626,6 +832,11 @@ class AuroForm extends i$3 {
626
832
  */
627
833
  handleKeyDown(event) {
628
834
  if (event.key === 'Enter' && this.isFormElement(event.target)) {
835
+ // Disabled controls do not submit a form natively.
836
+ if (this._isDisabled(event.target)) {
837
+ return;
838
+ }
839
+
629
840
  // Don't submit if it's a textarea (allow new lines)
630
841
  if (event.target.tagName.toLowerCase() === 'textarea' ||
631
842
  event.target.hasAttribute('textarea')) {
@@ -657,6 +868,121 @@ class AuroForm extends i$3 {
657
868
  });
658
869
  }
659
870
 
871
+ /**
872
+ * Handle batched MutationObserver records for `disabled` and `name`
873
+ * attribute changes on tracked form elements. A `name` change invalidates
874
+ * the formState keying — we resolve it by re-initializing state. A `disabled`
875
+ * change simply needs a re-render (so `value` / `validity` getters re-evaluate)
876
+ * and a refresh of the submit/reset button enablement.
877
+ * @param {MutationRecord[]} mutations - The batched mutation records.
878
+ * @returns {void}
879
+ * @private
880
+ */
881
+ _handleAttributeMutations(mutations) {
882
+ // Only mutations on tracked FORM elements matter here. The same observer
883
+ // also sees attribute changes on the submit/reset buttons (which this
884
+ // component itself toggles via `setDisabledStateOnButtons`); reacting to
885
+ // those would create an infinite observer/update loop.
886
+ const relevant = mutations.filter((mutation) => this.isFormElement(mutation.target));
887
+ if (relevant.length === 0) {
888
+ return;
889
+ }
890
+
891
+ const renameMutations = relevant.filter((mutation) => mutation.attributeName === 'name');
892
+ if (renameMutations.length > 0) {
893
+ // Migrate each renamed field's captured initial value from the old key
894
+ // to the new key before `initializeState()` re-runs `_addElementToState`.
895
+ // Without this, the new-name lookup in `_initialValues` would miss, the
896
+ // field's current (possibly user-edited) value would be captured as the
897
+ // new initial, and the form would incorrectly flip back to its initial
898
+ // state. The old key would also leak in `_initialValues` indefinitely.
899
+ renameMutations.forEach((mutation) => {
900
+ const oldName = mutation.oldValue;
901
+ const newName = mutation.target.getAttribute('name');
902
+ if (!oldName || oldName === newName) {
903
+ return;
904
+ }
905
+ if (newName === null) {
906
+ // `name` attribute removed — field will fall out of formState on re-init.
907
+ // Drop its captured initial so it doesn't leak in _initialValues.
908
+ delete this._initialValues[oldName];
909
+ return;
910
+ }
911
+ if (oldName in this._initialValues) {
912
+ this._initialValues[newName] = this._initialValues[oldName];
913
+ delete this._initialValues[oldName];
914
+ }
915
+ });
916
+ // initializeState() rebuilds formState from scratch (re-keying any
917
+ // renamed element) and also dispatches `change` + refreshes button state.
918
+ // We also re-run _attachEventListeners() because elements that previously
919
+ // had no `name` were skipped by queryAuroElements() (which selects
920
+ // `[name]`) and therefore never received input/validation/keydown
921
+ // listeners. Re-attaching is safe — the listener-removal step inside
922
+ // _attachEventListeners() prevents duplicates on already-wired elements.
923
+ this.initializeState();
924
+ this._attachEventListeners();
925
+ return;
926
+ }
927
+
928
+ // Refresh the cached `disabled` flag on each affected formState entry
929
+ // before the re-render, so getters that read `_isNameDisabled` see the
930
+ // current attribute state in the same tick.
931
+ relevant
932
+ .filter((mutation) => mutation.attributeName === 'disabled')
933
+ .forEach((mutation) => {
934
+ const name = mutation.target.getAttribute('name');
935
+ if (name && this.formState[name]) {
936
+ this.formState[name].disabled = mutation.target.hasAttribute('disabled');
937
+ }
938
+ });
939
+
940
+ this.requestUpdate('formState');
941
+ this.setDisabledStateOnButtons();
942
+ }
943
+
944
+ /**
945
+ * @returns {void}
946
+ */
947
+ connectedCallback() {
948
+ super.connectedCallback();
949
+
950
+ // One observer rooted at the host catches `disabled` / `name` changes on
951
+ // any tracked form element (light-DOM children, including those nested in
952
+ // wrapper elements). Cheaper than allocating an observer per element and
953
+ // resilient to runtime DOM mutations.
954
+ if (!this._attributeObserver) {
955
+ this._attributeObserver = new MutationObserver(this._handleAttributeMutations);
956
+ }
957
+
958
+ this._attributeObserver.observe(this, {
959
+ attributes: true,
960
+ subtree: true,
961
+ attributeOldValue: true,
962
+ attributeFilter: [
963
+ 'disabled',
964
+ 'name'
965
+ ]
966
+ });
967
+ }
968
+
969
+ /**
970
+ * @returns {void}
971
+ */
972
+ disconnectedCallback() {
973
+ // Disconnect everything we own to avoid leaking observers (and the strong
974
+ // refs they hold to DOM nodes) past the form's lifetime.
975
+ this._attributeObserver?.disconnect();
976
+ this.mutationObservers.forEach((observer) => observer.disconnect());
977
+ this.mutationObservers = [];
978
+ // Intentionally do NOT clear _initialValues here. Slot-moves trigger a
979
+ // disconnect/reconnect cycle; clearing would cause the next initializeState
980
+ // to capture the user's edited values as the new "initial" and silently
981
+ // flip the form back to its initial state.
982
+
983
+ super.disconnectedCallback();
984
+ }
985
+
660
986
  /**
661
987
  * @param {import('lit').PropertyValues} _changedProperties - Map of changed properties with their previous values.
662
988
  * @returns {void}
@@ -11175,6 +11501,7 @@ let AuroInputUtilities$3 = class AuroInputUtilities {
11175
11501
  const maskOptions = this.getMaskOptions('date', normalizedFormat);
11176
11502
 
11177
11503
  if (!(valueObject instanceof Date) || Number.isNaN(valueObject.getTime()) || !maskOptions || typeof maskOptions.format !== 'function') {
11504
+ console.debug('Invalid date object or mask options for formatting', { valueObject, maskOptions }); // eslint-disable-line no-console
11178
11505
  return undefined;
11179
11506
  }
11180
11507
 
@@ -11326,10 +11653,8 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
11326
11653
  this.layout = 'classic';
11327
11654
  this.locale = 'en-US';
11328
11655
  this.max = undefined;
11329
- this._maxObject = undefined;
11330
11656
  this.maxLength = undefined;
11331
11657
  this.min = undefined;
11332
- this._minObject = undefined;
11333
11658
  this.minLength = undefined;
11334
11659
  this.required = false;
11335
11660
  this.onDark = false;
@@ -11337,7 +11662,6 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
11337
11662
  this.size = 'lg';
11338
11663
  this.shape = 'classic';
11339
11664
  this.value = undefined;
11340
- this._valueObject = undefined;
11341
11665
 
11342
11666
  this._initializePrivateDefaults();
11343
11667
  }
@@ -11877,7 +12201,7 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
11877
12201
  * @returns {Date|undefined}
11878
12202
  */
11879
12203
  get valueObject() {
11880
- return this._valueObject || this._computeDateObjectFallback(this.value);
12204
+ return this.value && dateFormatter$3.isValidDate(this.value) ? dateFormatter$3.stringToDateInstance(this.value) : undefined;
11881
12205
  }
11882
12206
 
11883
12207
  /**
@@ -11885,7 +12209,7 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
11885
12209
  * @returns {Date|undefined}
11886
12210
  */
11887
12211
  get minObject() {
11888
- return this._minObject || this._computeDateObjectFallback(this.min);
12212
+ return this.min && dateFormatter$3.isValidDate(this.min) ? dateFormatter$3.stringToDateInstance(this.min) : undefined;
11889
12213
  }
11890
12214
 
11891
12215
  /**
@@ -11893,50 +12217,7 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
11893
12217
  * @returns {Date|undefined}
11894
12218
  */
11895
12219
  get maxObject() {
11896
- return this._maxObject || this._computeDateObjectFallback(this.max);
11897
- }
11898
-
11899
- /**
11900
- * Parses a date string into a Date object when the corresponding `_*Object`
11901
- * field hasn't been synced yet by `updated()`. Returns undefined when the
11902
- * input type/format isn't a full date or the string is not a valid date.
11903
- *
11904
- * Why this exists: a parent (datepicker) can call `inputN.validate()` from
11905
- * inside its own `updated()` before this input's `updated()` has run
11906
- * `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
11907
- * and range checks would otherwise silently no-op (flipping the result to
11908
- * `valid` or `patternMismatch`).
11909
- * @private
11910
- * @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
11911
- * @returns {Date|undefined}
11912
- */
11913
- _computeDateObjectFallback(dateStr) {
11914
- if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
11915
- return undefined;
11916
- }
11917
- if (!dateFormatter$3.isValidDate(dateStr)) {
11918
- return undefined;
11919
- }
11920
- return dateFormatter$3.stringToDateInstance(dateStr);
11921
- }
11922
-
11923
- /**
11924
- * Internal setter for readonly date object properties.
11925
- * @private
11926
- * @param {'valueObject'|'minObject'|'maxObject'} propertyName - Public object property name.
11927
- * @param {Date|undefined} propertyValue - Value to assign.
11928
- * @returns {void}
11929
- */
11930
- setDateObjectProperty(propertyName, propertyValue) {
11931
- const internalPropertyName = `_${propertyName}`;
11932
- const previousValue = this[internalPropertyName];
11933
-
11934
- if (previousValue === propertyValue) {
11935
- return;
11936
- }
11937
-
11938
- this[internalPropertyName] = propertyValue;
11939
- this.requestUpdate(propertyName, previousValue);
12220
+ return this.max && dateFormatter$3.isValidDate(this.max) ? dateFormatter$3.stringToDateInstance(this.max) : undefined;
11940
12221
  }
11941
12222
 
11942
12223
  connectedCallback() {
@@ -11965,7 +12246,6 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
11965
12246
  format: this.format
11966
12247
  });
11967
12248
  this.configureDataForType();
11968
- this.syncDateValues();
11969
12249
  }
11970
12250
 
11971
12251
  disconnectedCallback() {
@@ -11985,10 +12265,6 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
11985
12265
  this.wrapperElement.addEventListener('click', this.handleClick);
11986
12266
  }
11987
12267
 
11988
- // add attribute for query selectors when auro-input is registered under a custom name
11989
- if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
11990
- this.setAttribute('auro-input', '');
11991
- }
11992
12268
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
11993
12269
 
11994
12270
  // use validity message override if declared when initializing the component
@@ -11999,7 +12275,6 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
11999
12275
  this.setCustomHelpTextMessage();
12000
12276
  this.configureAutoFormatting();
12001
12277
  this.configureDataForType();
12002
- this.syncDateValues();
12003
12278
  }
12004
12279
 
12005
12280
  /**
@@ -12136,8 +12411,6 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
12136
12411
  this.configureDataForType();
12137
12412
  }
12138
12413
 
12139
- this.syncDateValues(changedProperties);
12140
-
12141
12414
  if (changedProperties.has('value')) {
12142
12415
  if (this.value && this.value.length > 0) {
12143
12416
  this.hasValue = true;
@@ -12159,14 +12432,14 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
12159
12432
 
12160
12433
  if (formattedValue !== this.inputElement.value) {
12161
12434
  this.skipNextProgrammaticInputEvent = true;
12162
- if (this.maskInstance && this.type === 'credit-card') {
12435
+ if (this.maskInstance && this.type !== 'date') {
12163
12436
  // Route through the mask so its _value and el.value stay in lock-step
12164
12437
  // (set value calls updateControl which writes el.value = displayValue).
12165
12438
  // Writing el.value directly leaves the mask thinking displayValue is
12166
- // stale; _saveSelection on the next focus/click then warns. Scoped to
12167
- // credit-card so date's own formattedValue (raw ISO when calendar-invalid)
12168
- // isn't re-masked through the mm/dd/yyyy mask, which would truncate it
12169
- // and flip validity from patternMismatch to tooShort.
12439
+ // stale; _saveSelection on the next focus/click then warns. Date is
12440
+ // excluded because its formattedValue can be raw ISO when the calendar
12441
+ // is invalid, and re-masking through mm/dd/yyyy would truncate it and
12442
+ // flip validity from patternMismatch to tooShort.
12170
12443
  this.maskInstance.value = formattedValue || '';
12171
12444
  } else if (formattedValue) {
12172
12445
  this.inputElement.value = formattedValue;
@@ -12208,120 +12481,65 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
12208
12481
  }));
12209
12482
  }
12210
12483
 
12211
-
12212
- /**
12213
- * Synchronizes the ISO string values and Date object representations for date-related properties.
12214
- * This keeps the model and display values aligned when either side changes.
12215
- *
12216
- * When a full date format is in use, this method updates `value`, `min`, and `max` from their corresponding
12217
- * Date objects or vice versa, based on which properties have changed. It only runs when the current configuration
12218
- * represents a full year/month/day date format.
12219
- *
12220
- * @param {Map<string, unknown>|undefined} [changedProperties=undefined] - Optional map of changed properties used to limit which values are synchronized.
12221
- * @returns {void}
12222
- * @private
12223
- */
12224
- syncDateValues(changedProperties = undefined) {
12225
- if (!this.util.isFullDateFormat(this.type, this.format)) {
12226
- return;
12227
- }
12228
-
12229
- this.syncSingleDateValue(changedProperties, 'valueObject', 'value');
12230
- this.syncSingleDateValue(changedProperties, 'minObject', 'min');
12231
- this.syncSingleDateValue(changedProperties, 'maxObject', 'max');
12232
- }
12233
-
12234
- /**
12235
- * Synchronizes one date object/string property pair.
12236
- * @private
12237
- * @param {Map<string, unknown>|undefined} changedProperties - Map of changed properties from Lit.
12238
- * @param {string} objectProperty - Date object property name.
12239
- * @param {string} valueProperty - ISO string property name.
12240
- * @returns {void}
12241
- */
12242
- syncSingleDateValue(changedProperties, objectProperty, valueProperty) {
12243
- const objectPropertyChanged = !changedProperties || changedProperties.has(objectProperty);
12244
-
12245
- // objectProperty wins over valueProperty when both changed
12246
- if (objectPropertyChanged && this[objectProperty]) {
12247
- this[valueProperty] = dateFormatter$3.toISOFormatString(this[objectProperty]);
12248
- return;
12249
- }
12250
-
12251
- const valuePropertyChanged = !changedProperties || changedProperties.has(valueProperty);
12252
- if (!valuePropertyChanged) {
12253
- return;
12254
- }
12255
-
12256
- // 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)
12257
- if (
12258
- changedProperties &&
12259
- valueProperty === 'value' &&
12260
- changedProperties.get('value') === undefined &&
12261
- this[objectProperty] instanceof Date &&
12262
- this[valueProperty] === dateFormatter$3.toISOFormatString(this[objectProperty])
12263
- ) {
12264
- return;
12265
- }
12266
-
12267
- if (dateFormatter$3.isValidDate(this[valueProperty])) {
12268
- this.setDateObjectProperty(objectProperty, dateFormatter$3.stringToDateInstance(this[valueProperty]));
12269
- } else {
12270
- this.setDateObjectProperty(objectProperty, undefined);
12271
- }
12272
- }
12273
-
12274
12484
  /**
12275
12485
  * Sets up IMasks and logic based on auto-formatting requirements.
12276
12486
  * @private
12277
12487
  * @returns {void}
12278
12488
  */
12279
12489
  configureAutoFormatting() {
12280
- // Re-entrancy guard. The patched-setter's synthetic input event (suppressed
12281
- // by _configuringMask above) could otherwise trigger handleInput
12282
- // processCreditCard configureAutoFormatting before the outer call's
12283
- // set value has finished its alignCursor pass.
12490
+ // _configuringMask gates two things: external re-entry into this method
12491
+ // while setup is mid-flight (from property changes that call back here),
12492
+ // and the accept/complete listeners below — both need to ignore the mask
12493
+ // events fired by our own value-restore step.
12284
12494
  if (this._configuringMask) return;
12285
12495
  this._configuringMask = true;
12286
12496
  try {
12497
+ // Destroy any prior mask so IMask can attach fresh under the new format.
12498
+ // Null the reference too — if the new maskOptions.mask is falsy (e.g.
12499
+ // type switched from credit-card to text) the IMask() reassignment
12500
+ // below is skipped, and downstream writes at line ~823 would otherwise
12501
+ // route through the destroyed instance.
12287
12502
  if (this.maskInstance) {
12288
12503
  this.maskInstance.destroy();
12504
+ this.maskInstance = null;
12289
12505
  }
12290
12506
 
12291
- // Pass new format to util
12292
12507
  this.util.updateFormat(this.format);
12293
12508
 
12294
12509
  const maskOptions = this.util.getMaskOptions(this.type, this.format);
12295
12510
 
12296
12511
  if (this.inputElement && maskOptions.mask) {
12297
-
12298
- // Stash and clear any existing value before IMask init.
12299
- // IMask's constructor processes the current input value which requires
12300
- // selection state clearing first avoids that scenario entirely.
12301
- // When the format changes (e.g. locale switch) and we have a valid ISO
12302
- // model value, compute the display string for the NEW format instead of
12303
- // re-using the old display string, which may be invalid in the new mask.
12512
+ // Capture the current display so it can be re-applied after IMask
12513
+ // attaches. The restore at the bottom goes through maskInstance.value
12514
+ // (not inputElement.value directly) so the mask's internal state and
12515
+ // the input's displayed text stay in lock-step.
12304
12516
  let existingValue = this.inputElement.value;
12517
+
12518
+ // Format-change case (e.g. locale switch): existingValue is the OLD
12519
+ // mask's display string and may not parse under the new mask. When
12520
+ // we have a valid date model, rebuild the display from valueObject
12521
+ // (the canonical source) using the new mask's format function.
12305
12522
  if (
12306
12523
  this.util.isFullDateFormat(this.type, this.format) &&
12307
12524
  this.value &&
12308
- dateFormatter$3.isValidDate(this.value) &&
12309
- this.valueObject instanceof Date &&
12310
- !Number.isNaN(this.valueObject.getTime()) &&
12525
+ this.valueObject &&
12311
12526
  typeof maskOptions.format === 'function'
12312
12527
  ) {
12313
12528
  existingValue = maskOptions.format(this.valueObject);
12314
12529
  }
12315
12530
 
12531
+ // Clear before IMask attaches so the constructor seeds an empty
12532
+ // internal value. Otherwise IMask reads the stale unmasked string
12533
+ // and emits a spurious 'accept' before the restore below runs.
12316
12534
  this.skipNextProgrammaticInputEvent = true;
12317
12535
  this.inputElement.value = '';
12318
12536
 
12319
12537
  this.maskInstance = IMask$3(this.inputElement, maskOptions);
12320
12538
 
12539
+ // Mask fires 'accept' on every value change, including the restore
12540
+ // step below. Skip events fired during configureAutoFormatting so
12541
+ // we don't overwrite a value the parent just pushed.
12321
12542
  this.maskInstance.on('accept', () => {
12322
- // Suppress propagation during configureAutoFormatting's own value-restoration
12323
- // (line below) — the mask emits 'accept' on every value-set, including ours,
12324
- // and we don't want to overwrite a value the parent just pushed.
12325
12543
  if (this._configuringMask) return;
12326
12544
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
12327
12545
  if (this.type === "date") {
@@ -12329,6 +12547,8 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
12329
12547
  }
12330
12548
  });
12331
12549
 
12550
+ // Mask fires 'complete' on the restore step below for any value that
12551
+ // happens to be a complete match. Same setup-suppression as 'accept'.
12332
12552
  this.maskInstance.on('complete', () => {
12333
12553
  if (this._configuringMask) return;
12334
12554
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
@@ -12337,7 +12557,9 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
12337
12557
  }
12338
12558
  });
12339
12559
 
12340
- // Restore the stashed value through IMask so it's properly masked
12560
+ // Write existingValue through the mask (not the input directly) so
12561
+ // the mask reformats it under the new rules and keeps its internal
12562
+ // _value aligned with the input's displayed text.
12341
12563
  if (existingValue) {
12342
12564
  this.maskInstance.value = existingValue;
12343
12565
  }
@@ -12505,7 +12727,6 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
12505
12727
  */
12506
12728
  reset() {
12507
12729
  this.value = undefined;
12508
- this.setDateObjectProperty('valueObject', undefined);
12509
12730
  this.validation.reset(this);
12510
12731
  }
12511
12732
 
@@ -12514,7 +12735,6 @@ let BaseInput$2 = class BaseInput extends AuroElement$6 {
12514
12735
  */
12515
12736
  clear() {
12516
12737
  this.value = undefined;
12517
- this.setDateObjectProperty('valueObject', undefined);
12518
12738
  }
12519
12739
 
12520
12740
  /**
@@ -13043,7 +13263,7 @@ let AuroHelpText$8 = class AuroHelpText extends i$3 {
13043
13263
  }
13044
13264
  };
13045
13265
 
13046
- var formkitVersion$8 = '202606192121';
13266
+ var formkitVersion$8 = '202606231948';
13047
13267
 
13048
13268
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
13049
13269
  // See LICENSE in the project root for license information.
@@ -24282,6 +24502,7 @@ let AuroInputUtilities$1 = class AuroInputUtilities {
24282
24502
  const maskOptions = this.getMaskOptions('date', normalizedFormat);
24283
24503
 
24284
24504
  if (!(valueObject instanceof Date) || Number.isNaN(valueObject.getTime()) || !maskOptions || typeof maskOptions.format !== 'function') {
24505
+ console.debug('Invalid date object or mask options for formatting', { valueObject, maskOptions }); // eslint-disable-line no-console
24285
24506
  return undefined;
24286
24507
  }
24287
24508
 
@@ -27162,7 +27383,7 @@ let AuroBibtemplate$3 = class AuroBibtemplate extends i$3 {
27162
27383
  }
27163
27384
  };
27164
27385
 
27165
- var formkitVersion$2$1 = '202606192121';
27386
+ var formkitVersion$2$1 = '202606231948';
27166
27387
 
27167
27388
  let l$1$2 = class l{generateElementName(t,e){let o=t;return o+="-",o+=e.replace(/[.]/g,"_"),o}generateTag(o,s,a){const r=this.generateElementName(o,s),i=i$2`${s$3(r)}`;return customElements.get(r)||customElements.define(r,class extends a{}),i}};let d$1$2 = class d{registerComponent(t,e){customElements.get(t)||customElements.define(t,class extends e{});}closestElement(t,e=this,o=(e,s=e&&e.closest(t))=>e&&e!==document&&e!==window?s||o(e.getRootNode().host):null){return o(e)}handleComponentTagRename(t,e){const o=e.toLowerCase();t.tagName.toLowerCase()!==o&&t.setAttribute(o,true);}elementMatch(t,e){const o=e.toLowerCase();return t.tagName.toLowerCase()===o||t.hasAttribute(o)}getSlotText(t,e){const o=t.shadowRoot?.querySelector(`slot[name="${e}"]`),s=(o?.assignedNodes({flatten:true})||[]).map(t=>t.textContent?.trim()).join(" ").trim();return s||null}};let h$1$2 = class h{registerComponent(t,e){customElements.get(t)||customElements.define(t,class extends e{});}closestElement(t,e=this,o=(e,s=e&&e.closest(t))=>e&&e!==document&&e!==window?s||o(e.getRootNode().host):null){return o(e)}handleComponentTagRename(t,e){const o=e.toLowerCase();t.tagName.toLowerCase()!==o&&t.setAttribute(o,true);}elementMatch(t,e){const o=e.toLowerCase();return t.tagName.toLowerCase()===o||t.hasAttribute(o)}};var c$1$3=i$6`:host{color:var(--ds-auro-loader-color)}:host>span{background-color:var(--ds-auro-loader-background-color);border-color:var(--ds-auro-loader-border-color)}:host([onlight]),:host([appearance=brand]){--ds-auro-loader-color: var(--ds-basic-color-brand-primary, #01426a)}:host([ondark]),:host([appearance=inverse]){--ds-auro-loader-color: var(--ds-basic-color-texticon-inverse, #ffffff)}:host([orbit])>span{--ds-auro-loader-background-color: transparent}:host([orbit])>span:nth-child(1){--ds-auro-loader-border-color: currentcolor;opacity:.25}:host([orbit])>span:nth-child(2){--ds-auro-loader-border-color: currentcolor;border-right-color:transparent;border-bottom-color:transparent;border-left-color:transparent}
27168
27389
  `,u$4$2=i$6`.body-default{font-size:var(--wcss-body-default-font-size, 1rem);line-height:var(--wcss-body-default-line-height, 1.5rem)}.body-default,.body-lg{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-weight, 450);letter-spacing:var(--wcss-body-letter-spacing, 0)}.body-lg{font-size:var(--wcss-body-lg-font-size, 1.125rem);line-height:var(--wcss-body-lg-line-height, 1.625rem)}.body-sm{font-size:var(--wcss-body-sm-font-size, .875rem);line-height:var(--wcss-body-sm-line-height, 1.25rem)}.body-sm,.body-xs{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-weight:var(--wcss-body-weight, 450);letter-spacing:var(--wcss-body-letter-spacing, 0)}.body-xs{font-size:var(--wcss-body-xs-font-size, .75rem);line-height:var(--wcss-body-xs-line-height, 1rem)}.body-2xs{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:var(--wcss-body-2xs-font-size, .625rem);font-weight:var(--wcss-body-weight, 450);letter-spacing:var(--wcss-body-letter-spacing, 0);line-height:var(--wcss-body-2xs-line-height, .875rem)}.display-2xl{font-family:var(--wcss-display-2xl-family, "AS Circular"),var(--wcss-display-2xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-2xl-font-size, clamp(3.5rem, 6vw, 5.375rem));font-weight:var(--wcss-display-2xl-weight, 300);letter-spacing:var(--wcss-display-2xl-letter-spacing, 0);line-height:var(--wcss-display-2xl-line-height, 1.3)}.display-xl{font-family:var(--wcss-display-xl-family, "AS Circular"),var(--wcss-display-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-xl-font-size, clamp(3rem, 5.3333333333vw, 4.5rem));font-weight:var(--wcss-display-xl-weight, 300);letter-spacing:var(--wcss-display-xl-letter-spacing, 0);line-height:var(--wcss-display-xl-line-height, 1.3)}.display-lg{font-family:var(--wcss-display-lg-family, "AS Circular"),var(--wcss-display-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-lg-font-size, clamp(2.75rem, 4.6666666667vw, 4rem));font-weight:var(--wcss-display-lg-weight, 300);letter-spacing:var(--wcss-display-lg-letter-spacing, 0);line-height:var(--wcss-display-lg-line-height, 1.3)}.display-md{font-family:var(--wcss-display-md-family, "AS Circular"),var(--wcss-display-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-md-font-size, clamp(2.5rem, 4vw, 3.5rem));font-weight:var(--wcss-display-md-weight, 300);letter-spacing:var(--wcss-display-md-letter-spacing, 0);line-height:var(--wcss-display-md-line-height, 1.3)}.display-sm{font-family:var(--wcss-display-sm-family, "AS Circular"),var(--wcss-display-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-sm-font-size, clamp(2rem, 3.6666666667vw, 3rem));font-weight:var(--wcss-display-sm-weight, 300);letter-spacing:var(--wcss-display-sm-letter-spacing, 0);line-height:var(--wcss-display-sm-line-height, 1.3)}.display-xs{font-family:var(--wcss-display-xs-family, "AS Circular"),var(--wcss-display-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-xs-font-size, clamp(1.75rem, 3vw, 2.375rem));font-weight:var(--wcss-display-xs-weight, 300);letter-spacing:var(--wcss-display-xs-letter-spacing, 0);line-height:var(--wcss-display-xs-line-height, 1.3)}.heading-xl{font-family:var(--wcss-heading-xl-family, "AS Circular"),var(--wcss-heading-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-xl-font-size, clamp(2rem, 3vw, 2.5rem));font-weight:var(--wcss-heading-xl-weight, 300);letter-spacing:var(--wcss-heading-xl-letter-spacing, 0);line-height:var(--wcss-heading-xl-line-height, 1.3)}.heading-lg{font-family:var(--wcss-heading-lg-family, "AS Circular"),var(--wcss-heading-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-lg-font-size, clamp(1.75rem, 2.6666666667vw, 2.25rem));font-weight:var(--wcss-heading-lg-weight, 300);letter-spacing:var(--wcss-heading-lg-letter-spacing, 0);line-height:var(--wcss-heading-lg-line-height, 1.3)}.heading-md{font-family:var(--wcss-heading-md-family, "AS Circular"),var(--wcss-heading-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-md-font-size, clamp(1.625rem, 2.3333333333vw, 1.75rem));font-weight:var(--wcss-heading-md-weight, 300);letter-spacing:var(--wcss-heading-md-letter-spacing, 0);line-height:var(--wcss-heading-md-line-height, 1.3)}.heading-sm{font-family:var(--wcss-heading-sm-family, "AS Circular"),var(--wcss-heading-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-sm-font-size, clamp(1.375rem, 2vw, 1.5rem));font-weight:var(--wcss-heading-sm-weight, 300);letter-spacing:var(--wcss-heading-sm-letter-spacing, 0);line-height:var(--wcss-heading-sm-line-height, 1.3)}.heading-xs{font-family:var(--wcss-heading-xs-family, "AS Circular"),var(--wcss-heading-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-xs-font-size, clamp(1.25rem, 1.6666666667vw, 1.25rem));font-weight:var(--wcss-heading-xs-weight, 450);letter-spacing:var(--wcss-heading-xs-letter-spacing, 0);line-height:var(--wcss-heading-xs-line-height, 1.3)}.heading-2xs{font-family:var(--wcss-heading-2xs-family, "AS Circular"),var(--wcss-heading-2xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-2xs-font-size, clamp(1.125rem, 1.5vw, 1.125rem));font-weight:var(--wcss-heading-2xs-weight, 450);letter-spacing:var(--wcss-heading-2xs-letter-spacing, 0);line-height:var(--wcss-heading-2xs-line-height, 1.3)}.accent-2xl{font-family:var(--wcss-accent-2xl-family, "Good OT"),var(--wcss-accent-2xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-2xl-font-size, clamp(2rem, 3.1666666667vw, 2.375rem));font-weight:var(--wcss-accent-2xl-weight, 450);letter-spacing:var(--wcss-accent-2xl-letter-spacing, .05em);line-height:var(--wcss-accent-2xl-line-height, 1)}.accent-2xl,.accent-xl{text-transform:uppercase}.accent-xl{font-family:var(--wcss-accent-xl-family, "Good OT"),var(--wcss-accent-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-xl-font-size, clamp(1.625rem, 2.3333333333vw, 2rem));font-weight:var(--wcss-accent-xl-weight, 450);letter-spacing:var(--wcss-accent-xl-letter-spacing, .05em);line-height:var(--wcss-accent-xl-line-height, 1.3)}.accent-lg{font-family:var(--wcss-accent-lg-family, "Good OT"),var(--wcss-accent-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-lg-font-size, clamp(1.5rem, 2.1666666667vw, 1.75rem));font-weight:var(--wcss-accent-lg-weight, 450);letter-spacing:var(--wcss-accent-lg-letter-spacing, .05em);line-height:var(--wcss-accent-lg-line-height, 1.3)}.accent-lg,.accent-md{text-transform:uppercase}.accent-md{font-family:var(--wcss-accent-md-family, "Good OT"),var(--wcss-accent-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-md-font-size, clamp(1.375rem, 1.8333333333vw, 1.5rem));font-weight:var(--wcss-accent-md-weight, 500);letter-spacing:var(--wcss-accent-md-letter-spacing, .05em);line-height:var(--wcss-accent-md-line-height, 1.3)}.accent-sm{font-family:var(--wcss-accent-sm-family, "Good OT"),var(--wcss-accent-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-sm-font-size, clamp(1.125rem, 1.5vw, 1.25rem));font-weight:var(--wcss-accent-sm-weight, 500);letter-spacing:var(--wcss-accent-sm-letter-spacing, .05em);line-height:var(--wcss-accent-sm-line-height, 1.3)}.accent-sm,.accent-xs{text-transform:uppercase}.accent-xs{font-family:var(--wcss-accent-xs-family, "Good OT"),var(--wcss-accent-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-xs-font-size, clamp(1rem, 1.3333333333vw, 1rem));font-weight:var(--wcss-accent-xs-weight, 500);letter-spacing:var(--wcss-accent-xs-letter-spacing, .1em);line-height:var(--wcss-accent-xs-line-height, 1.3)}.accent-2xs{font-family:var(--wcss-accent-2xs-family, "Good OT"),var(--wcss-accent-2xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-2xs-font-size, clamp(.875rem, 1.1666666667vw, .875rem));font-weight:var(--wcss-accent-2xs-weight, 450);letter-spacing:var(--wcss-accent-2xs-letter-spacing, .1em);line-height:var(--wcss-accent-2xs-line-height, 1.3);text-transform:uppercase}:focus:not(:focus-visible){outline:3px solid transparent}:host,:host>span{position:relative}:host{width:2rem;height:2rem;display:inline-block;font-size:0}:host>span{position:absolute;display:inline-block;float:none;top:0;left:0;width:2rem;height:2rem;border-radius:100%;border-style:solid;border-width:0;box-sizing:border-box}:host([xs]),:host([xs])>span{width:1.2rem;height:1.2rem}:host([sm]),:host([sm])>span{width:3rem;height:3rem}:host([md]),:host([md])>span{width:5rem;height:5rem}:host([lg]),:host([lg])>span{width:8rem;height:8rem}:host{--margin: .375rem;--margin-xs: .2rem;--margin-sm: .5rem;--margin-md: .75rem;--margin-lg: 1rem}:host([pulse]),:host([pulse])>span{position:relative}:host([pulse]){width:calc(3rem + var(--margin) * 6);height:calc(1rem + var(--margin) * 2)}:host([pulse])>span{width:1rem;height:1rem;margin:var(--margin);animation:pulse 1.5s ease infinite}:host([pulse][xs]){width:calc(1.95rem + var(--margin-xs) * 6);height:calc(.65rem + var(--margin-xs) * 2)}:host([pulse][xs])>span{margin:var(--margin-xs);width:.65rem;height:.65rem}:host([pulse][sm]){width:calc(6rem + var(--margin-sm) * 6);height:calc(2rem + var(--margin-sm) * 2)}:host([pulse][sm])>span{margin:var(--margin-sm);width:2rem;height:2rem}:host([pulse][md]){width:calc(9rem + var(--margin-md) * 6);height:calc(3rem + var(--margin-md) * 2)}:host([pulse][md])>span{margin:var(--margin-md);width:3rem;height:3rem}:host([pulse][lg]){width:calc(15rem + var(--margin-lg) * 6);height:calc(5rem + var(--margin-lg) * 2)}:host([pulse][lg])>span{margin:var(--margin-lg);width:5rem;height:5rem}:host([pulse])>span:nth-child(1){animation-delay:-.4s}:host([pulse])>span:nth-child(2){animation-delay:-.2s}:host([pulse])>span:nth-child(3){animation-delay:0ms}@keyframes pulse{0%,to{opacity:.1;transform:scale(.9)}50%{opacity:1;transform:scale(1.1)}}:host([orbit]),:host([orbit])>span{opacity:1}:host([orbit])>span{border-width:5px}:host([orbit])>span:nth-child(2){animation:orbit 2s linear infinite}:host([orbit][sm])>span{border-width:8px}:host([orbit][md])>span{border-width:13px}:host([orbit][lg])>span{border-width:21px}@keyframes orbit{0%{transform:rotate(0)}to{transform:rotate(360deg)}}:host([ringworm])>svg{animation:rotate 2s linear infinite;height:100%;width:100%;stroke:currentcolor;stroke-width:8}:host([ringworm]) .path{stroke-dashoffset:0;animation:ringworm 1.5s ease-in-out infinite;stroke-linecap:round}@keyframes rotate{to{transform:rotate(360deg)}}@keyframes ringworm{0%{stroke-dasharray:1,200;stroke-dashoffset:0}50%{stroke-dasharray:89,200;stroke-dashoffset:-35px}to{stroke-dasharray:89,200;stroke-dashoffset:-124px}}:host([laser]){position:static;width:100%;display:block;height:0;overflow:hidden;font-size:unset}:host([laser])>span{position:fixed;width:100%;height:.25rem;border-radius:0;z-index:100}:host([laser])>span:nth-child(1){border-color:currentcolor;opacity:.25}:host([laser])>span:nth-child(2){border-color:currentcolor;animation:laser 2s linear infinite;opacity:1;width:50%}:host([laser][sm])>span:nth-child(2){width:20%}:host([laser][md])>span:nth-child(2){width:30%}:host([laser][lg])>span:nth-child(2){width:50%;animation-duration:1.5s}:host([laser][xl])>span:nth-child(2){width:80%;animation-duration:1.5s}@keyframes laser{0%{left:-100%}to{left:110%}}:host>.no-animation{display:none}@media (prefers-reduced-motion: reduce){:host{display:flex;align-items:center;justify-content:center}:host>span{opacity:1}:host>.loader{display:none}:host>svg{display:none}:host>.no-animation{display:block}}
@@ -32673,7 +32894,7 @@ let AuroHelpText$2$1 = class AuroHelpText extends i$3 {
32673
32894
  }
32674
32895
  };
32675
32896
 
32676
- var formkitVersion$1$3 = '202606192121';
32897
+ var formkitVersion$1$3 = '202606231948';
32677
32898
 
32678
32899
  let AuroElement$2$2 = class AuroElement extends i$3 {
32679
32900
  static get properties() {
@@ -33624,6 +33845,14 @@ let AuroDropdown$3 = class AuroDropdown extends AuroElement$2$2 {
33624
33845
  };
33625
33846
  this.addEventListener('keydown', this._bibTabHandler);
33626
33847
 
33848
+ // Suppress AuroFloatingUI's auto-hide-on-focus-loss while the
33849
+ // desktopModal trap owns focus management. Without this, the very
33850
+ // first focus move into the bib (from the RAF below) triggers
33851
+ // handleFocusLoss → hideBib, tearing down the trap before the
33852
+ // user can press Tab.
33853
+ this._priorNoHideOnFocusLoss = this.noHideOnThisFocusLoss;
33854
+ this.noHideOnThisFocusLoss = true;
33855
+
33627
33856
  // Move initial focus into the bib content, matching FocusTrap behavior
33628
33857
  requestAnimationFrame(() => {
33629
33858
  const focusables = getFocusableElements$3(this.bibContent);
@@ -44387,6 +44616,7 @@ let AuroInputUtilities$2 = class AuroInputUtilities {
44387
44616
  const maskOptions = this.getMaskOptions('date', normalizedFormat);
44388
44617
 
44389
44618
  if (!(valueObject instanceof Date) || Number.isNaN(valueObject.getTime()) || !maskOptions || typeof maskOptions.format !== 'function') {
44619
+ console.debug('Invalid date object or mask options for formatting', { valueObject, maskOptions }); // eslint-disable-line no-console
44390
44620
  return undefined;
44391
44621
  }
44392
44622
 
@@ -44538,10 +44768,8 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
44538
44768
  this.layout = 'classic';
44539
44769
  this.locale = 'en-US';
44540
44770
  this.max = undefined;
44541
- this._maxObject = undefined;
44542
44771
  this.maxLength = undefined;
44543
44772
  this.min = undefined;
44544
- this._minObject = undefined;
44545
44773
  this.minLength = undefined;
44546
44774
  this.required = false;
44547
44775
  this.onDark = false;
@@ -44549,7 +44777,6 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
44549
44777
  this.size = 'lg';
44550
44778
  this.shape = 'classic';
44551
44779
  this.value = undefined;
44552
- this._valueObject = undefined;
44553
44780
 
44554
44781
  this._initializePrivateDefaults();
44555
44782
  }
@@ -45089,7 +45316,7 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45089
45316
  * @returns {Date|undefined}
45090
45317
  */
45091
45318
  get valueObject() {
45092
- return this._valueObject || this._computeDateObjectFallback(this.value);
45319
+ return this.value && dateFormatter$2.isValidDate(this.value) ? dateFormatter$2.stringToDateInstance(this.value) : undefined;
45093
45320
  }
45094
45321
 
45095
45322
  /**
@@ -45097,7 +45324,7 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45097
45324
  * @returns {Date|undefined}
45098
45325
  */
45099
45326
  get minObject() {
45100
- return this._minObject || this._computeDateObjectFallback(this.min);
45327
+ return this.min && dateFormatter$2.isValidDate(this.min) ? dateFormatter$2.stringToDateInstance(this.min) : undefined;
45101
45328
  }
45102
45329
 
45103
45330
  /**
@@ -45105,50 +45332,7 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45105
45332
  * @returns {Date|undefined}
45106
45333
  */
45107
45334
  get maxObject() {
45108
- return this._maxObject || this._computeDateObjectFallback(this.max);
45109
- }
45110
-
45111
- /**
45112
- * Parses a date string into a Date object when the corresponding `_*Object`
45113
- * field hasn't been synced yet by `updated()`. Returns undefined when the
45114
- * input type/format isn't a full date or the string is not a valid date.
45115
- *
45116
- * Why this exists: a parent (datepicker) can call `inputN.validate()` from
45117
- * inside its own `updated()` before this input's `updated()` has run
45118
- * `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
45119
- * and range checks would otherwise silently no-op (flipping the result to
45120
- * `valid` or `patternMismatch`).
45121
- * @private
45122
- * @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
45123
- * @returns {Date|undefined}
45124
- */
45125
- _computeDateObjectFallback(dateStr) {
45126
- if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
45127
- return undefined;
45128
- }
45129
- if (!dateFormatter$2.isValidDate(dateStr)) {
45130
- return undefined;
45131
- }
45132
- return dateFormatter$2.stringToDateInstance(dateStr);
45133
- }
45134
-
45135
- /**
45136
- * Internal setter for readonly date object properties.
45137
- * @private
45138
- * @param {'valueObject'|'minObject'|'maxObject'} propertyName - Public object property name.
45139
- * @param {Date|undefined} propertyValue - Value to assign.
45140
- * @returns {void}
45141
- */
45142
- setDateObjectProperty(propertyName, propertyValue) {
45143
- const internalPropertyName = `_${propertyName}`;
45144
- const previousValue = this[internalPropertyName];
45145
-
45146
- if (previousValue === propertyValue) {
45147
- return;
45148
- }
45149
-
45150
- this[internalPropertyName] = propertyValue;
45151
- this.requestUpdate(propertyName, previousValue);
45335
+ return this.max && dateFormatter$2.isValidDate(this.max) ? dateFormatter$2.stringToDateInstance(this.max) : undefined;
45152
45336
  }
45153
45337
 
45154
45338
  connectedCallback() {
@@ -45177,7 +45361,6 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45177
45361
  format: this.format
45178
45362
  });
45179
45363
  this.configureDataForType();
45180
- this.syncDateValues();
45181
45364
  }
45182
45365
 
45183
45366
  disconnectedCallback() {
@@ -45197,10 +45380,6 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45197
45380
  this.wrapperElement.addEventListener('click', this.handleClick);
45198
45381
  }
45199
45382
 
45200
- // add attribute for query selectors when auro-input is registered under a custom name
45201
- if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
45202
- this.setAttribute('auro-input', '');
45203
- }
45204
45383
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
45205
45384
 
45206
45385
  // use validity message override if declared when initializing the component
@@ -45211,7 +45390,6 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45211
45390
  this.setCustomHelpTextMessage();
45212
45391
  this.configureAutoFormatting();
45213
45392
  this.configureDataForType();
45214
- this.syncDateValues();
45215
45393
  }
45216
45394
 
45217
45395
  /**
@@ -45348,8 +45526,6 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45348
45526
  this.configureDataForType();
45349
45527
  }
45350
45528
 
45351
- this.syncDateValues(changedProperties);
45352
-
45353
45529
  if (changedProperties.has('value')) {
45354
45530
  if (this.value && this.value.length > 0) {
45355
45531
  this.hasValue = true;
@@ -45371,14 +45547,14 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45371
45547
 
45372
45548
  if (formattedValue !== this.inputElement.value) {
45373
45549
  this.skipNextProgrammaticInputEvent = true;
45374
- if (this.maskInstance && this.type === 'credit-card') {
45550
+ if (this.maskInstance && this.type !== 'date') {
45375
45551
  // Route through the mask so its _value and el.value stay in lock-step
45376
45552
  // (set value calls updateControl which writes el.value = displayValue).
45377
45553
  // Writing el.value directly leaves the mask thinking displayValue is
45378
- // stale; _saveSelection on the next focus/click then warns. Scoped to
45379
- // credit-card so date's own formattedValue (raw ISO when calendar-invalid)
45380
- // isn't re-masked through the mm/dd/yyyy mask, which would truncate it
45381
- // and flip validity from patternMismatch to tooShort.
45554
+ // stale; _saveSelection on the next focus/click then warns. Date is
45555
+ // excluded because its formattedValue can be raw ISO when the calendar
45556
+ // is invalid, and re-masking through mm/dd/yyyy would truncate it and
45557
+ // flip validity from patternMismatch to tooShort.
45382
45558
  this.maskInstance.value = formattedValue || '';
45383
45559
  } else if (formattedValue) {
45384
45560
  this.inputElement.value = formattedValue;
@@ -45420,120 +45596,65 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45420
45596
  }));
45421
45597
  }
45422
45598
 
45423
-
45424
- /**
45425
- * Synchronizes the ISO string values and Date object representations for date-related properties.
45426
- * This keeps the model and display values aligned when either side changes.
45427
- *
45428
- * When a full date format is in use, this method updates `value`, `min`, and `max` from their corresponding
45429
- * Date objects or vice versa, based on which properties have changed. It only runs when the current configuration
45430
- * represents a full year/month/day date format.
45431
- *
45432
- * @param {Map<string, unknown>|undefined} [changedProperties=undefined] - Optional map of changed properties used to limit which values are synchronized.
45433
- * @returns {void}
45434
- * @private
45435
- */
45436
- syncDateValues(changedProperties = undefined) {
45437
- if (!this.util.isFullDateFormat(this.type, this.format)) {
45438
- return;
45439
- }
45440
-
45441
- this.syncSingleDateValue(changedProperties, 'valueObject', 'value');
45442
- this.syncSingleDateValue(changedProperties, 'minObject', 'min');
45443
- this.syncSingleDateValue(changedProperties, 'maxObject', 'max');
45444
- }
45445
-
45446
- /**
45447
- * Synchronizes one date object/string property pair.
45448
- * @private
45449
- * @param {Map<string, unknown>|undefined} changedProperties - Map of changed properties from Lit.
45450
- * @param {string} objectProperty - Date object property name.
45451
- * @param {string} valueProperty - ISO string property name.
45452
- * @returns {void}
45453
- */
45454
- syncSingleDateValue(changedProperties, objectProperty, valueProperty) {
45455
- const objectPropertyChanged = !changedProperties || changedProperties.has(objectProperty);
45456
-
45457
- // objectProperty wins over valueProperty when both changed
45458
- if (objectPropertyChanged && this[objectProperty]) {
45459
- this[valueProperty] = dateFormatter$2.toISOFormatString(this[objectProperty]);
45460
- return;
45461
- }
45462
-
45463
- const valuePropertyChanged = !changedProperties || changedProperties.has(valueProperty);
45464
- if (!valuePropertyChanged) {
45465
- return;
45466
- }
45467
-
45468
- // 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)
45469
- if (
45470
- changedProperties &&
45471
- valueProperty === 'value' &&
45472
- changedProperties.get('value') === undefined &&
45473
- this[objectProperty] instanceof Date &&
45474
- this[valueProperty] === dateFormatter$2.toISOFormatString(this[objectProperty])
45475
- ) {
45476
- return;
45477
- }
45478
-
45479
- if (dateFormatter$2.isValidDate(this[valueProperty])) {
45480
- this.setDateObjectProperty(objectProperty, dateFormatter$2.stringToDateInstance(this[valueProperty]));
45481
- } else {
45482
- this.setDateObjectProperty(objectProperty, undefined);
45483
- }
45484
- }
45485
-
45486
45599
  /**
45487
45600
  * Sets up IMasks and logic based on auto-formatting requirements.
45488
45601
  * @private
45489
45602
  * @returns {void}
45490
45603
  */
45491
45604
  configureAutoFormatting() {
45492
- // Re-entrancy guard. The patched-setter's synthetic input event (suppressed
45493
- // by _configuringMask above) could otherwise trigger handleInput
45494
- // processCreditCard configureAutoFormatting before the outer call's
45495
- // set value has finished its alignCursor pass.
45605
+ // _configuringMask gates two things: external re-entry into this method
45606
+ // while setup is mid-flight (from property changes that call back here),
45607
+ // and the accept/complete listeners below — both need to ignore the mask
45608
+ // events fired by our own value-restore step.
45496
45609
  if (this._configuringMask) return;
45497
45610
  this._configuringMask = true;
45498
45611
  try {
45612
+ // Destroy any prior mask so IMask can attach fresh under the new format.
45613
+ // Null the reference too — if the new maskOptions.mask is falsy (e.g.
45614
+ // type switched from credit-card to text) the IMask() reassignment
45615
+ // below is skipped, and downstream writes at line ~823 would otherwise
45616
+ // route through the destroyed instance.
45499
45617
  if (this.maskInstance) {
45500
45618
  this.maskInstance.destroy();
45619
+ this.maskInstance = null;
45501
45620
  }
45502
45621
 
45503
- // Pass new format to util
45504
45622
  this.util.updateFormat(this.format);
45505
45623
 
45506
45624
  const maskOptions = this.util.getMaskOptions(this.type, this.format);
45507
45625
 
45508
45626
  if (this.inputElement && maskOptions.mask) {
45509
-
45510
- // Stash and clear any existing value before IMask init.
45511
- // IMask's constructor processes the current input value which requires
45512
- // selection state clearing first avoids that scenario entirely.
45513
- // When the format changes (e.g. locale switch) and we have a valid ISO
45514
- // model value, compute the display string for the NEW format instead of
45515
- // re-using the old display string, which may be invalid in the new mask.
45627
+ // Capture the current display so it can be re-applied after IMask
45628
+ // attaches. The restore at the bottom goes through maskInstance.value
45629
+ // (not inputElement.value directly) so the mask's internal state and
45630
+ // the input's displayed text stay in lock-step.
45516
45631
  let existingValue = this.inputElement.value;
45632
+
45633
+ // Format-change case (e.g. locale switch): existingValue is the OLD
45634
+ // mask's display string and may not parse under the new mask. When
45635
+ // we have a valid date model, rebuild the display from valueObject
45636
+ // (the canonical source) using the new mask's format function.
45517
45637
  if (
45518
45638
  this.util.isFullDateFormat(this.type, this.format) &&
45519
45639
  this.value &&
45520
- dateFormatter$2.isValidDate(this.value) &&
45521
- this.valueObject instanceof Date &&
45522
- !Number.isNaN(this.valueObject.getTime()) &&
45640
+ this.valueObject &&
45523
45641
  typeof maskOptions.format === 'function'
45524
45642
  ) {
45525
45643
  existingValue = maskOptions.format(this.valueObject);
45526
45644
  }
45527
45645
 
45646
+ // Clear before IMask attaches so the constructor seeds an empty
45647
+ // internal value. Otherwise IMask reads the stale unmasked string
45648
+ // and emits a spurious 'accept' before the restore below runs.
45528
45649
  this.skipNextProgrammaticInputEvent = true;
45529
45650
  this.inputElement.value = '';
45530
45651
 
45531
45652
  this.maskInstance = IMask$2(this.inputElement, maskOptions);
45532
45653
 
45654
+ // Mask fires 'accept' on every value change, including the restore
45655
+ // step below. Skip events fired during configureAutoFormatting so
45656
+ // we don't overwrite a value the parent just pushed.
45533
45657
  this.maskInstance.on('accept', () => {
45534
- // Suppress propagation during configureAutoFormatting's own value-restoration
45535
- // (line below) — the mask emits 'accept' on every value-set, including ours,
45536
- // and we don't want to overwrite a value the parent just pushed.
45537
45658
  if (this._configuringMask) return;
45538
45659
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
45539
45660
  if (this.type === "date") {
@@ -45541,6 +45662,8 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45541
45662
  }
45542
45663
  });
45543
45664
 
45665
+ // Mask fires 'complete' on the restore step below for any value that
45666
+ // happens to be a complete match. Same setup-suppression as 'accept'.
45544
45667
  this.maskInstance.on('complete', () => {
45545
45668
  if (this._configuringMask) return;
45546
45669
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
@@ -45549,7 +45672,9 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45549
45672
  }
45550
45673
  });
45551
45674
 
45552
- // Restore the stashed value through IMask so it's properly masked
45675
+ // Write existingValue through the mask (not the input directly) so
45676
+ // the mask reformats it under the new rules and keeps its internal
45677
+ // _value aligned with the input's displayed text.
45553
45678
  if (existingValue) {
45554
45679
  this.maskInstance.value = existingValue;
45555
45680
  }
@@ -45717,7 +45842,6 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45717
45842
  */
45718
45843
  reset() {
45719
45844
  this.value = undefined;
45720
- this.setDateObjectProperty('valueObject', undefined);
45721
45845
  this.validation.reset(this);
45722
45846
  }
45723
45847
 
@@ -45726,7 +45850,6 @@ let BaseInput$1 = class BaseInput extends AuroElement$1$3 {
45726
45850
  */
45727
45851
  clear() {
45728
45852
  this.value = undefined;
45729
- this.setDateObjectProperty('valueObject', undefined);
45730
45853
  }
45731
45854
 
45732
45855
  /**
@@ -46255,7 +46378,7 @@ let AuroHelpText$1$3 = class AuroHelpText extends i$3 {
46255
46378
  }
46256
46379
  };
46257
46380
 
46258
- var formkitVersion$7 = '202606192121';
46381
+ var formkitVersion$7 = '202606231948';
46259
46382
 
46260
46383
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
46261
46384
  // See LICENSE in the project root for license information.
@@ -47675,6 +47798,8 @@ const datepickerKeyboardStrategy = {
47675
47798
  * @slot label - Defines the label content for the entire datepicker when `layout="snowflake"`.
47676
47799
  * @slot toLabel - Defines the label content for the second input when the `range` attribute is used.
47677
47800
  * @slot fromLabel - Defines the label content for the first input.
47801
+ * @slot optionalFromLabel - Overrides the "(optional)" text rendered next to the first input's label when the datepicker is not `required`.
47802
+ * @slot optionalToLabel - Overrides the "(optional)" text rendered next to the second input's label when `range` is set and the datepicker is not `required`.
47678
47803
  * @slot date_YYYY_MM_DD - Defines the content to display in the auro-calendar-cell for the specified date. The content text is colored using the success state token when the `highlight` attribute is applied to the slot.
47679
47804
  * @slot popover_YYYY_MM_DD - Defines the content to display in the auro-calendar-cell popover for the specified date.
47680
47805
  * @csspart dropdown - Use for customizing the style of the dropdown.
@@ -49270,7 +49395,9 @@ class AuroDatePicker extends AuroElement$5 {
49270
49395
  // update the calendar
49271
49396
  if (this.valueEndObject) {
49272
49397
  this.calendar.dateTo = this.convertToWcValidTime(this.valueEndObject);
49273
- this.calendarRenderUtil.updateCentralDate(this, this.valueEndObject);
49398
+ if (!this.wasCellClick) {
49399
+ this.calendarRenderUtil.updateCentralDate(this, this.valueEndObject);
49400
+ }
49274
49401
  } else {
49275
49402
  if (this.inputList[1].value !== this.valueEnd) {
49276
49403
  this.inputList[1].value = this.valueEnd || '';
@@ -49665,6 +49792,7 @@ class AuroDatePicker extends AuroElement$5 {
49665
49792
  }
49666
49793
  <span slot="ariaLabel.clear">${this.runtimeUtils.getSlotText(this, 'ariaLabel.input.clear') || i18n$2(this.lang, 'clearInput')}</span>
49667
49794
  <span slot="label"><slot name="fromLabel"></slot></span>
49795
+ <slot name="optionalFromLabel" slot="optionalLabel"> (optional)</slot>
49668
49796
  </${this.inputTag}>
49669
49797
  </div>
49670
49798
 
@@ -49709,6 +49837,7 @@ class AuroDatePicker extends AuroElement$5 {
49709
49837
  }
49710
49838
  <span slot="ariaLabel.clear">${this.runtimeUtils.getSlotText(this, 'ariaLabel.input.clear') || this.runtimeUtils.getSlotText(this, 'ariaLabel.input.clear') || i18n$2(this.lang, 'clearInput')}</span>
49711
49839
  <span slot="label"><slot name="toLabel"></slot></span>
49840
+ <slot name="optionalToLabel" slot="optionalLabel"> (optional)</slot>
49712
49841
  </${this.inputTag}>
49713
49842
  </div>
49714
49843
  ` : undefined}
@@ -50952,7 +51081,7 @@ let AuroHelpText$1$2 = class AuroHelpText extends i$3 {
50952
51081
  }
50953
51082
  };
50954
51083
 
50955
- var formkitVersion$1$2 = '202606192121';
51084
+ var formkitVersion$1$2 = '202606231948';
50956
51085
 
50957
51086
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
50958
51087
  // See LICENSE in the project root for license information.
@@ -55280,7 +55409,7 @@ let AuroHelpText$6 = class AuroHelpText extends i$3 {
55280
55409
  }
55281
55410
  };
55282
55411
 
55283
- var formkitVersion$6 = '202606192121';
55412
+ var formkitVersion$6 = '202606231948';
55284
55413
 
55285
55414
  let AuroElement$1$2 = class AuroElement extends i$3 {
55286
55415
  static get properties() {
@@ -56231,6 +56360,14 @@ let AuroDropdown$2 = class AuroDropdown extends AuroElement$1$2 {
56231
56360
  };
56232
56361
  this.addEventListener('keydown', this._bibTabHandler);
56233
56362
 
56363
+ // Suppress AuroFloatingUI's auto-hide-on-focus-loss while the
56364
+ // desktopModal trap owns focus management. Without this, the very
56365
+ // first focus move into the bib (from the RAF below) triggers
56366
+ // handleFocusLoss → hideBib, tearing down the trap before the
56367
+ // user can press Tab.
56368
+ this._priorNoHideOnFocusLoss = this.noHideOnThisFocusLoss;
56369
+ this.noHideOnThisFocusLoss = true;
56370
+
56234
56371
  // Move initial focus into the bib content, matching FocusTrap behavior
56235
56372
  requestAnimationFrame(() => {
56236
56373
  const focusables = getFocusableElements$2(this.bibContent);
@@ -59212,7 +59349,7 @@ let AuroHelpText$5 = class AuroHelpText extends i$3 {
59212
59349
  }
59213
59350
  };
59214
59351
 
59215
- var formkitVersion$5 = '202606192121';
59352
+ var formkitVersion$5 = '202606231948';
59216
59353
 
59217
59354
  // Copyright (c) Alaska Air. All right reserved. Licensed under the Apache-2.0 license
59218
59355
  // See LICENSE in the project root for license information.
@@ -60962,7 +61099,7 @@ let AuroHelpText$4 = class AuroHelpText extends i$3 {
60962
61099
  }
60963
61100
  };
60964
61101
 
60965
- var formkitVersion$4 = '202606192121';
61102
+ var formkitVersion$4 = '202606231948';
60966
61103
 
60967
61104
  // Copyright (c) 2026 Alaska Airlines. All rights reserved. Licensed under the Apache-2.0 license
60968
61105
  // See LICENSE in the project root for license information.
@@ -62138,6 +62275,8 @@ function navigateArrow$1(component, direction, options = {}) {
62138
62275
  }
62139
62276
  }
62140
62277
 
62278
+ /* eslint-disable no-underscore-dangle */
62279
+
62141
62280
  /**
62142
62281
  * Returns the clear button element from the active input's shadow
62143
62282
  * DOM, if available.
@@ -62173,11 +62312,9 @@ function isClearBtnFocused(ctx, clearBtn = getClearBtn(ctx)) {
62173
62312
  * @param {Object} menu - The menu component.
62174
62313
  */
62175
62314
  function reconcileMenuIndex(menu) {
62176
- // eslint-disable-next-line no-underscore-dangle
62177
62315
  if (menu._index < 0 && menu.optionActive && menu.items) {
62178
62316
  const idx = menu.items.indexOf(menu.optionActive);
62179
62317
  if (idx >= 0) {
62180
- // eslint-disable-next-line no-underscore-dangle
62181
62318
  menu._index = idx;
62182
62319
  }
62183
62320
  }
@@ -66177,7 +66314,7 @@ let AuroHelpText$2 = class AuroHelpText extends i$3 {
66177
66314
  }
66178
66315
  };
66179
66316
 
66180
- var formkitVersion$2 = '202606192121';
66317
+ var formkitVersion$2 = '202606231948';
66181
66318
 
66182
66319
  let AuroElement$2$1 = class AuroElement extends i$3 {
66183
66320
  static get properties() {
@@ -67128,6 +67265,14 @@ let AuroDropdown$1 = class AuroDropdown extends AuroElement$2$1 {
67128
67265
  };
67129
67266
  this.addEventListener('keydown', this._bibTabHandler);
67130
67267
 
67268
+ // Suppress AuroFloatingUI's auto-hide-on-focus-loss while the
67269
+ // desktopModal trap owns focus management. Without this, the very
67270
+ // first focus move into the bib (from the RAF below) triggers
67271
+ // handleFocusLoss → hideBib, tearing down the trap before the
67272
+ // user can press Tab.
67273
+ this._priorNoHideOnFocusLoss = this.noHideOnThisFocusLoss;
67274
+ this.noHideOnThisFocusLoss = true;
67275
+
67131
67276
  // Move initial focus into the bib content, matching FocusTrap behavior
67132
67277
  requestAnimationFrame(() => {
67133
67278
  const focusables = getFocusableElements$1(this.bibContent);
@@ -77891,6 +78036,7 @@ class AuroInputUtilities {
77891
78036
  const maskOptions = this.getMaskOptions('date', normalizedFormat);
77892
78037
 
77893
78038
  if (!(valueObject instanceof Date) || Number.isNaN(valueObject.getTime()) || !maskOptions || typeof maskOptions.format !== 'function') {
78039
+ console.debug('Invalid date object or mask options for formatting', { valueObject, maskOptions }); // eslint-disable-line no-console
77894
78040
  return undefined;
77895
78041
  }
77896
78042
 
@@ -78042,10 +78188,8 @@ class BaseInput extends AuroElement$1$1 {
78042
78188
  this.layout = 'classic';
78043
78189
  this.locale = 'en-US';
78044
78190
  this.max = undefined;
78045
- this._maxObject = undefined;
78046
78191
  this.maxLength = undefined;
78047
78192
  this.min = undefined;
78048
- this._minObject = undefined;
78049
78193
  this.minLength = undefined;
78050
78194
  this.required = false;
78051
78195
  this.onDark = false;
@@ -78053,7 +78197,6 @@ class BaseInput extends AuroElement$1$1 {
78053
78197
  this.size = 'lg';
78054
78198
  this.shape = 'classic';
78055
78199
  this.value = undefined;
78056
- this._valueObject = undefined;
78057
78200
 
78058
78201
  this._initializePrivateDefaults();
78059
78202
  }
@@ -78593,7 +78736,7 @@ class BaseInput extends AuroElement$1$1 {
78593
78736
  * @returns {Date|undefined}
78594
78737
  */
78595
78738
  get valueObject() {
78596
- return this._valueObject || this._computeDateObjectFallback(this.value);
78739
+ return this.value && dateFormatter.isValidDate(this.value) ? dateFormatter.stringToDateInstance(this.value) : undefined;
78597
78740
  }
78598
78741
 
78599
78742
  /**
@@ -78601,7 +78744,7 @@ class BaseInput extends AuroElement$1$1 {
78601
78744
  * @returns {Date|undefined}
78602
78745
  */
78603
78746
  get minObject() {
78604
- return this._minObject || this._computeDateObjectFallback(this.min);
78747
+ return this.min && dateFormatter.isValidDate(this.min) ? dateFormatter.stringToDateInstance(this.min) : undefined;
78605
78748
  }
78606
78749
 
78607
78750
  /**
@@ -78609,50 +78752,7 @@ class BaseInput extends AuroElement$1$1 {
78609
78752
  * @returns {Date|undefined}
78610
78753
  */
78611
78754
  get maxObject() {
78612
- return this._maxObject || this._computeDateObjectFallback(this.max);
78613
- }
78614
-
78615
- /**
78616
- * Parses a date string into a Date object when the corresponding `_*Object`
78617
- * field hasn't been synced yet by `updated()`. Returns undefined when the
78618
- * input type/format isn't a full date or the string is not a valid date.
78619
- *
78620
- * Why this exists: a parent (datepicker) can call `inputN.validate()` from
78621
- * inside its own `updated()` before this input's `updated()` has run
78622
- * `syncDateValues()` — so `_valueObject`/`_maxObject` are still `undefined`
78623
- * and range checks would otherwise silently no-op (flipping the result to
78624
- * `valid` or `patternMismatch`).
78625
- * @private
78626
- * @param {string|undefined} dateStr - ISO date string from `value`/`min`/`max`.
78627
- * @returns {Date|undefined}
78628
- */
78629
- _computeDateObjectFallback(dateStr) {
78630
- if (!dateStr || !this.util || !this.util.isFullDateFormat(this.type, this.format)) {
78631
- return undefined;
78632
- }
78633
- if (!dateFormatter.isValidDate(dateStr)) {
78634
- return undefined;
78635
- }
78636
- return dateFormatter.stringToDateInstance(dateStr);
78637
- }
78638
-
78639
- /**
78640
- * Internal setter for readonly date object properties.
78641
- * @private
78642
- * @param {'valueObject'|'minObject'|'maxObject'} propertyName - Public object property name.
78643
- * @param {Date|undefined} propertyValue - Value to assign.
78644
- * @returns {void}
78645
- */
78646
- setDateObjectProperty(propertyName, propertyValue) {
78647
- const internalPropertyName = `_${propertyName}`;
78648
- const previousValue = this[internalPropertyName];
78649
-
78650
- if (previousValue === propertyValue) {
78651
- return;
78652
- }
78653
-
78654
- this[internalPropertyName] = propertyValue;
78655
- this.requestUpdate(propertyName, previousValue);
78755
+ return this.max && dateFormatter.isValidDate(this.max) ? dateFormatter.stringToDateInstance(this.max) : undefined;
78656
78756
  }
78657
78757
 
78658
78758
  connectedCallback() {
@@ -78681,7 +78781,6 @@ class BaseInput extends AuroElement$1$1 {
78681
78781
  format: this.format
78682
78782
  });
78683
78783
  this.configureDataForType();
78684
- this.syncDateValues();
78685
78784
  }
78686
78785
 
78687
78786
  disconnectedCallback() {
@@ -78701,10 +78800,6 @@ class BaseInput extends AuroElement$1$1 {
78701
78800
  this.wrapperElement.addEventListener('click', this.handleClick);
78702
78801
  }
78703
78802
 
78704
- // add attribute for query selectors when auro-input is registered under a custom name
78705
- if (this.tagName.toLowerCase() !== 'auro-input' && !this.hasAttribute('auro-input')) {
78706
- this.setAttribute('auro-input', '');
78707
- }
78708
78803
  this.inputId = this.id ? `${this.id}-input` : window.crypto.randomUUID();
78709
78804
 
78710
78805
  // use validity message override if declared when initializing the component
@@ -78715,7 +78810,6 @@ class BaseInput extends AuroElement$1$1 {
78715
78810
  this.setCustomHelpTextMessage();
78716
78811
  this.configureAutoFormatting();
78717
78812
  this.configureDataForType();
78718
- this.syncDateValues();
78719
78813
  }
78720
78814
 
78721
78815
  /**
@@ -78852,8 +78946,6 @@ class BaseInput extends AuroElement$1$1 {
78852
78946
  this.configureDataForType();
78853
78947
  }
78854
78948
 
78855
- this.syncDateValues(changedProperties);
78856
-
78857
78949
  if (changedProperties.has('value')) {
78858
78950
  if (this.value && this.value.length > 0) {
78859
78951
  this.hasValue = true;
@@ -78875,14 +78967,14 @@ class BaseInput extends AuroElement$1$1 {
78875
78967
 
78876
78968
  if (formattedValue !== this.inputElement.value) {
78877
78969
  this.skipNextProgrammaticInputEvent = true;
78878
- if (this.maskInstance && this.type === 'credit-card') {
78970
+ if (this.maskInstance && this.type !== 'date') {
78879
78971
  // Route through the mask so its _value and el.value stay in lock-step
78880
78972
  // (set value calls updateControl which writes el.value = displayValue).
78881
78973
  // Writing el.value directly leaves the mask thinking displayValue is
78882
- // stale; _saveSelection on the next focus/click then warns. Scoped to
78883
- // credit-card so date's own formattedValue (raw ISO when calendar-invalid)
78884
- // isn't re-masked through the mm/dd/yyyy mask, which would truncate it
78885
- // and flip validity from patternMismatch to tooShort.
78974
+ // stale; _saveSelection on the next focus/click then warns. Date is
78975
+ // excluded because its formattedValue can be raw ISO when the calendar
78976
+ // is invalid, and re-masking through mm/dd/yyyy would truncate it and
78977
+ // flip validity from patternMismatch to tooShort.
78886
78978
  this.maskInstance.value = formattedValue || '';
78887
78979
  } else if (formattedValue) {
78888
78980
  this.inputElement.value = formattedValue;
@@ -78924,120 +79016,65 @@ class BaseInput extends AuroElement$1$1 {
78924
79016
  }));
78925
79017
  }
78926
79018
 
78927
-
78928
- /**
78929
- * Synchronizes the ISO string values and Date object representations for date-related properties.
78930
- * This keeps the model and display values aligned when either side changes.
78931
- *
78932
- * When a full date format is in use, this method updates `value`, `min`, and `max` from their corresponding
78933
- * Date objects or vice versa, based on which properties have changed. It only runs when the current configuration
78934
- * represents a full year/month/day date format.
78935
- *
78936
- * @param {Map<string, unknown>|undefined} [changedProperties=undefined] - Optional map of changed properties used to limit which values are synchronized.
78937
- * @returns {void}
78938
- * @private
78939
- */
78940
- syncDateValues(changedProperties = undefined) {
78941
- if (!this.util.isFullDateFormat(this.type, this.format)) {
78942
- return;
78943
- }
78944
-
78945
- this.syncSingleDateValue(changedProperties, 'valueObject', 'value');
78946
- this.syncSingleDateValue(changedProperties, 'minObject', 'min');
78947
- this.syncSingleDateValue(changedProperties, 'maxObject', 'max');
78948
- }
78949
-
78950
- /**
78951
- * Synchronizes one date object/string property pair.
78952
- * @private
78953
- * @param {Map<string, unknown>|undefined} changedProperties - Map of changed properties from Lit.
78954
- * @param {string} objectProperty - Date object property name.
78955
- * @param {string} valueProperty - ISO string property name.
78956
- * @returns {void}
78957
- */
78958
- syncSingleDateValue(changedProperties, objectProperty, valueProperty) {
78959
- const objectPropertyChanged = !changedProperties || changedProperties.has(objectProperty);
78960
-
78961
- // objectProperty wins over valueProperty when both changed
78962
- if (objectPropertyChanged && this[objectProperty]) {
78963
- this[valueProperty] = dateFormatter.toISOFormatString(this[objectProperty]);
78964
- return;
78965
- }
78966
-
78967
- const valuePropertyChanged = !changedProperties || changedProperties.has(valueProperty);
78968
- if (!valuePropertyChanged) {
78969
- return;
78970
- }
78971
-
78972
- // 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)
78973
- if (
78974
- changedProperties &&
78975
- valueProperty === 'value' &&
78976
- changedProperties.get('value') === undefined &&
78977
- this[objectProperty] instanceof Date &&
78978
- this[valueProperty] === dateFormatter.toISOFormatString(this[objectProperty])
78979
- ) {
78980
- return;
78981
- }
78982
-
78983
- if (dateFormatter.isValidDate(this[valueProperty])) {
78984
- this.setDateObjectProperty(objectProperty, dateFormatter.stringToDateInstance(this[valueProperty]));
78985
- } else {
78986
- this.setDateObjectProperty(objectProperty, undefined);
78987
- }
78988
- }
78989
-
78990
79019
  /**
78991
79020
  * Sets up IMasks and logic based on auto-formatting requirements.
78992
79021
  * @private
78993
79022
  * @returns {void}
78994
79023
  */
78995
79024
  configureAutoFormatting() {
78996
- // Re-entrancy guard. The patched-setter's synthetic input event (suppressed
78997
- // by _configuringMask above) could otherwise trigger handleInput
78998
- // processCreditCard configureAutoFormatting before the outer call's
78999
- // set value has finished its alignCursor pass.
79025
+ // _configuringMask gates two things: external re-entry into this method
79026
+ // while setup is mid-flight (from property changes that call back here),
79027
+ // and the accept/complete listeners below — both need to ignore the mask
79028
+ // events fired by our own value-restore step.
79000
79029
  if (this._configuringMask) return;
79001
79030
  this._configuringMask = true;
79002
79031
  try {
79032
+ // Destroy any prior mask so IMask can attach fresh under the new format.
79033
+ // Null the reference too — if the new maskOptions.mask is falsy (e.g.
79034
+ // type switched from credit-card to text) the IMask() reassignment
79035
+ // below is skipped, and downstream writes at line ~823 would otherwise
79036
+ // route through the destroyed instance.
79003
79037
  if (this.maskInstance) {
79004
79038
  this.maskInstance.destroy();
79039
+ this.maskInstance = null;
79005
79040
  }
79006
79041
 
79007
- // Pass new format to util
79008
79042
  this.util.updateFormat(this.format);
79009
79043
 
79010
79044
  const maskOptions = this.util.getMaskOptions(this.type, this.format);
79011
79045
 
79012
79046
  if (this.inputElement && maskOptions.mask) {
79013
-
79014
- // Stash and clear any existing value before IMask init.
79015
- // IMask's constructor processes the current input value which requires
79016
- // selection state clearing first avoids that scenario entirely.
79017
- // When the format changes (e.g. locale switch) and we have a valid ISO
79018
- // model value, compute the display string for the NEW format instead of
79019
- // re-using the old display string, which may be invalid in the new mask.
79047
+ // Capture the current display so it can be re-applied after IMask
79048
+ // attaches. The restore at the bottom goes through maskInstance.value
79049
+ // (not inputElement.value directly) so the mask's internal state and
79050
+ // the input's displayed text stay in lock-step.
79020
79051
  let existingValue = this.inputElement.value;
79052
+
79053
+ // Format-change case (e.g. locale switch): existingValue is the OLD
79054
+ // mask's display string and may not parse under the new mask. When
79055
+ // we have a valid date model, rebuild the display from valueObject
79056
+ // (the canonical source) using the new mask's format function.
79021
79057
  if (
79022
79058
  this.util.isFullDateFormat(this.type, this.format) &&
79023
79059
  this.value &&
79024
- dateFormatter.isValidDate(this.value) &&
79025
- this.valueObject instanceof Date &&
79026
- !Number.isNaN(this.valueObject.getTime()) &&
79060
+ this.valueObject &&
79027
79061
  typeof maskOptions.format === 'function'
79028
79062
  ) {
79029
79063
  existingValue = maskOptions.format(this.valueObject);
79030
79064
  }
79031
79065
 
79066
+ // Clear before IMask attaches so the constructor seeds an empty
79067
+ // internal value. Otherwise IMask reads the stale unmasked string
79068
+ // and emits a spurious 'accept' before the restore below runs.
79032
79069
  this.skipNextProgrammaticInputEvent = true;
79033
79070
  this.inputElement.value = '';
79034
79071
 
79035
79072
  this.maskInstance = IMask(this.inputElement, maskOptions);
79036
79073
 
79074
+ // Mask fires 'accept' on every value change, including the restore
79075
+ // step below. Skip events fired during configureAutoFormatting so
79076
+ // we don't overwrite a value the parent just pushed.
79037
79077
  this.maskInstance.on('accept', () => {
79038
- // Suppress propagation during configureAutoFormatting's own value-restoration
79039
- // (line below) — the mask emits 'accept' on every value-set, including ours,
79040
- // and we don't want to overwrite a value the parent just pushed.
79041
79078
  if (this._configuringMask) return;
79042
79079
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
79043
79080
  if (this.type === "date") {
@@ -79045,6 +79082,8 @@ class BaseInput extends AuroElement$1$1 {
79045
79082
  }
79046
79083
  });
79047
79084
 
79085
+ // Mask fires 'complete' on the restore step below for any value that
79086
+ // happens to be a complete match. Same setup-suppression as 'accept'.
79048
79087
  this.maskInstance.on('complete', () => {
79049
79088
  if (this._configuringMask) return;
79050
79089
  this.value = this.util.toModelValue(this.maskInstance.value, this.format);
@@ -79053,7 +79092,9 @@ class BaseInput extends AuroElement$1$1 {
79053
79092
  }
79054
79093
  });
79055
79094
 
79056
- // Restore the stashed value through IMask so it's properly masked
79095
+ // Write existingValue through the mask (not the input directly) so
79096
+ // the mask reformats it under the new rules and keeps its internal
79097
+ // _value aligned with the input's displayed text.
79057
79098
  if (existingValue) {
79058
79099
  this.maskInstance.value = existingValue;
79059
79100
  }
@@ -79221,7 +79262,6 @@ class BaseInput extends AuroElement$1$1 {
79221
79262
  */
79222
79263
  reset() {
79223
79264
  this.value = undefined;
79224
- this.setDateObjectProperty('valueObject', undefined);
79225
79265
  this.validation.reset(this);
79226
79266
  }
79227
79267
 
@@ -79230,7 +79270,6 @@ class BaseInput extends AuroElement$1$1 {
79230
79270
  */
79231
79271
  clear() {
79232
79272
  this.value = undefined;
79233
- this.setDateObjectProperty('valueObject', undefined);
79234
79273
  }
79235
79274
 
79236
79275
  /**
@@ -79759,7 +79798,7 @@ let AuroHelpText$1$1 = class AuroHelpText extends i$3 {
79759
79798
  }
79760
79799
  };
79761
79800
 
79762
- var formkitVersion$1$1 = '202606192121';
79801
+ var formkitVersion$1$1 = '202606231948';
79763
79802
 
79764
79803
  // Copyright (c) 2025 Alaska Airlines. All right reserved. Licensed under the Apache-2.0 license
79765
79804
  // See LICENSE in the project root for license information.
@@ -80880,7 +80919,7 @@ let AuroBibtemplate$1 = class AuroBibtemplate extends i$3 {
80880
80919
  }
80881
80920
  };
80882
80921
 
80883
- var formkitVersion$3 = '202606192121';
80922
+ var formkitVersion$3 = '202606231948';
80884
80923
 
80885
80924
  var styleCss$1$3 = i$6`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}:host{display:block;text-align:left}:host [auro-dropdown]{--ds-auro-dropdown-trigger-background-color: transparent}:host #inputInBib::part(wrapper){box-shadow:none}:host #inputInBib::part(accent-left){display:none}:host([layout*=classic]) [auro-input]{width:100%}:host([layout*=classic]) [auro-input]::part(helpText){display:none}:host([layout*=classic]) #slotHolder{display:none}`;
80886
80925
 
@@ -81867,7 +81906,7 @@ class AuroCombobox extends AuroElement$3 {
81867
81906
  if (this.menu) {
81868
81907
  this.menu.matchWord = normalizeFilterValue(this.input.value);
81869
81908
  }
81870
- const label = getOptionLabel(this.menu.optionSelected) || this.menu.currentLabel;
81909
+ const label = getOptionLabel(this.menu.optionSelected);
81871
81910
  this.updateTriggerTextDisplay(label || this.value);
81872
81911
  }
81873
81912
 
@@ -81881,40 +81920,9 @@ class AuroCombobox extends AuroElement$3 {
81881
81920
  // in suggestion mode, do not override input value if no selection has been made and the input currently has focus
81882
81921
  const isInputFocusedWithNoSelection = !this.menu.value && (this.input.matches(':focus-within') || (this.inputInBib && this.inputInBib.matches(':focus-within')));
81883
81922
 
81884
- if (!this.persistInput && !(this.behavior === 'suggestion' && isInputFocusedWithNoSelection)) {
81885
- const nextValue = label || this.value;
81886
- // Only set the flag when there's an actual write to suppress —
81887
- // syncValuesAndStates re-enters here during typing when both inputs
81888
- // already match, and a no-op flag flip would make the bib branch's
81889
- // bail eat legitimate user-input events.
81890
- const triggerNeedsSync = this.input.value !== nextValue;
81891
- const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
81892
- if (triggerNeedsSync || bibNeedsSync) {
81893
- this._syncingDisplayValue = true;
81894
- if (triggerNeedsSync) {
81895
- this.input.value = nextValue;
81896
- }
81897
- if (bibNeedsSync) {
81898
- this.inputInBib.value = nextValue;
81899
- }
81900
- const pending = [];
81901
- if (triggerNeedsSync) {
81902
- pending.push(this.input.updateComplete);
81903
- }
81904
- if (bibNeedsSync) {
81905
- pending.push(this.inputInBib.updateComplete);
81906
- }
81907
- Promise.all(pending).then(() => {
81908
- // imask reasserts otherwise, and handleBlur reverts to its stale value.
81909
- if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
81910
- this.input.maskInstance.updateValue();
81911
- }
81912
- if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
81913
- this.inputInBib.maskInstance.updateValue();
81914
- }
81915
- this._syncingDisplayValue = false;
81916
- });
81917
- }
81923
+ const suppressed = this.persistInput || (this.behavior === 'suggestion' && isInputFocusedWithNoSelection);
81924
+ if (!suppressed) {
81925
+ this.syncInputValuesAcrossTriggerAndBib(label || this.value);
81918
81926
  }
81919
81927
 
81920
81928
  // update the displayValue in the trigger if displayValue slot content is present
@@ -81942,6 +81950,48 @@ class AuroCombobox extends AuroElement$3 {
81942
81950
  this.requestUpdate();
81943
81951
  }
81944
81952
 
81953
+ /**
81954
+ * Writes nextValue to the trigger input and the bib input when their current
81955
+ * value differs, then re-asserts imask after Lit's update flushes.
81956
+ * @param {string} nextValue - The value to write to both inputs.
81957
+ * @returns {Promise<void> | null} Promise that resolves after the inputs flush,
81958
+ * or null when no sync was needed (so callers can skip post-flush work).
81959
+ * @private
81960
+ */
81961
+ syncInputValuesAcrossTriggerAndBib(nextValue) {
81962
+ // Only set the flag when there's an actual write to suppress —
81963
+ // syncValuesAndStates re-enters here during typing when both inputs
81964
+ // already match, and a no-op flag flip would make the bib branch's
81965
+ // bail eat legitimate user-input events.
81966
+ const triggerNeedsSync = this.input.value !== nextValue;
81967
+ const bibNeedsSync = this.inputInBib.value !== nextValue;
81968
+ if (!triggerNeedsSync && !bibNeedsSync) {
81969
+ return null;
81970
+ }
81971
+
81972
+ this._syncingDisplayValue = true;
81973
+
81974
+ const pending = [];
81975
+ if (triggerNeedsSync) {
81976
+ this.input.value = nextValue;
81977
+ pending.push(this.input.updateComplete);
81978
+ }
81979
+ if (bibNeedsSync) {
81980
+ this.inputInBib.value = nextValue;
81981
+ pending.push(this.inputInBib.updateComplete);
81982
+ }
81983
+ return Promise.all(pending).then(() => {
81984
+ // imask reasserts otherwise, and handleBlur reverts to its stale value.
81985
+ if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
81986
+ this.input.maskInstance.updateValue();
81987
+ }
81988
+ if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
81989
+ this.inputInBib.maskInstance.updateValue();
81990
+ }
81991
+ this._syncingDisplayValue = false;
81992
+ });
81993
+ }
81994
+
81945
81995
  /**
81946
81996
  * Processes hidden state of all menu options and determines if there are any available options not hidden.
81947
81997
  * @private
@@ -81969,9 +82019,9 @@ class AuroCombobox extends AuroElement$3 {
81969
82019
  this.syncValuesAndStates();
81970
82020
  }
81971
82021
 
81972
- // Re-activate when optionActive is no longer visible, or when _index has
81973
- // been reset (e.g. by clearSelection in an async update) so that
81974
- // makeSelection() can find the correct option.
82022
+ // Re-activate when optionActive is not in the current scrollable viewport,
82023
+ // or when _index has been reset (e.g. by clearSelection in an async update)
82024
+ // so that makeSelection() can find the correct option.
81975
82025
  if (!this.availableOptions.includes(this.menu.optionActive) || this.menu._index < 0) {
81976
82026
  this.activateFirstEnabledAvailableOption();
81977
82027
  }
@@ -82105,28 +82155,25 @@ class AuroCombobox extends AuroElement$3 {
82105
82155
 
82106
82156
  guardTouchPassthrough$1(this.menu);
82107
82157
 
82108
- // The dialog's showModal() steals focus from the trigger.
82109
- // A single rAF is enough for the dialog DOM to be painted and
82110
- // focusable, then doubleRaf finalizes the transition.
82158
+ // showModal() takes focus away from the trigger. Early focus once
82159
+ // the dialog has painted; the shared doubleRaf below is the fallback
82160
+ // for when the dialog needs an extra frame and also clears the
82161
+ // validation guard.
82111
82162
  requestAnimationFrame(() => {
82112
82163
  this.setInputFocus();
82113
82164
  });
82114
-
82115
- doubleRaf$1(() => {
82116
- this.setInputFocus();
82117
- this._inFullscreenTransition = false;
82118
- });
82119
- } else {
82120
- // Desktop popover-open: restore the trigger caret to end-of-text.
82121
- // Clicking the trigger lands on auro-input's floating <label for="…">
82122
- // overlay; Chrome resets the native input's selection to [0, 0] on
82123
- // label-focus before any JS runs. setInputFocus()'s non-fullscreen
82124
- // branch parks the caret at end. doubleRaf lets the dropdown layout
82125
- // settle first, matching the fullscreen branch timing.
82126
- doubleRaf$1(() => {
82127
- this.setInputFocus();
82128
- });
82129
82165
  }
82166
+ // else (desktop popover-open): Chrome resets the trigger caret to
82167
+ // [0, 0] when its floating <label for="…"> overlay receives focus.
82168
+ // The shared doubleRaf below parks the caret back at end-of-text
82169
+ // after the dropdown layout settles.
82170
+
82171
+ doubleRaf$1(() => {
82172
+ this.setInputFocus();
82173
+ if (this._inFullscreenTransition) {
82174
+ this._inFullscreenTransition = false;
82175
+ }
82176
+ });
82130
82177
  }
82131
82178
  });
82132
82179
 
@@ -82231,24 +82278,7 @@ class AuroCombobox extends AuroElement$3 {
82231
82278
  nativeInput.setSelectionRange(len, len);
82232
82279
  }
82233
82280
  } else {
82234
- // Safety net for the strategy-change setTimeout: a viewport-crossing
82235
- // fullscreen→floating switch can close the dialog and leave focus on
82236
- // body. Every other Branch 2 callsite already has focus on the input
82237
- // via the focusin → this.focus() redirect (L1448-1452), so this is a
82238
- // no-op in the common case.
82239
- if (!this.input.componentHasFocus) {
82240
- this.input.focus();
82241
- }
82242
-
82243
- // Park the trigger native input's caret at end-of-text every time the
82244
- // popover opens. Chrome focuses the native via the floating <label for="…">
82245
- // overlay on click and resets selection to [0, 0]; no JS fires between
82246
- // mousedown and selectionchange, so we restore the caret here.
82247
- const triggerNativeInput = this.input.inputElement;
82248
- if (triggerNativeInput && triggerNativeInput.value) {
82249
- const len = triggerNativeInput.value.length;
82250
- triggerNativeInput.setSelectionRange(len, len);
82251
- }
82281
+ this.input.focus();
82252
82282
  }
82253
82283
  }
82254
82284
 
@@ -82354,7 +82384,7 @@ class AuroCombobox extends AuroElement$3 {
82354
82384
  if (this.menu.optionSelected) {
82355
82385
  const selected = this.menu.optionSelected;
82356
82386
 
82357
- if (!this.optionSelected || this.optionSelected !== selected) {
82387
+ if (this.optionSelected !== selected) {
82358
82388
  this.optionSelected = selected;
82359
82389
  }
82360
82390
 
@@ -82367,7 +82397,7 @@ class AuroCombobox extends AuroElement$3 {
82367
82397
  }
82368
82398
 
82369
82399
  // Update display
82370
- this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected) || this.menu.value);
82400
+ this.updateTriggerTextDisplay(getOptionLabel(this.optionSelected));
82371
82401
 
82372
82402
  // Update match word for filtering
82373
82403
  const trimmedInput = normalizeFilterValue(this.input.value);
@@ -82383,13 +82413,22 @@ class AuroCombobox extends AuroElement$3 {
82383
82413
  this.hideBib();
82384
82414
  }
82385
82415
 
82416
+ // Move focus to the clear button when the user makes a selection.
82417
+ if (!isEcho && this.menu.value !== undefined) {
82418
+ this.setClearBtnFocus();
82419
+ }
82420
+
82386
82421
  // Announce the selection after the dropdown closes so it isn't
82387
82422
  // overridden by VoiceOver's "collapsed" announcement from aria-expanded.
82423
+ // Skip when there's no selected value (e.g. menu.clearSelection() from
82424
+ // the unmatched-value path), otherwise VoiceOver reads "undefined".
82388
82425
  const selectedValue = this.menu.value;
82389
- const announcementDelay = 300;
82390
- setTimeout(() => {
82391
- announceToScreenReader$1(this._getAnnouncementRoot(), `${selectedValue}, selected`);
82392
- }, announcementDelay);
82426
+ if (selectedValue) {
82427
+ const announcementDelay = 300;
82428
+ setTimeout(() => {
82429
+ announceToScreenReader$1(this._getAnnouncementRoot(), `${selectedValue}, selected`);
82430
+ }, announcementDelay);
82431
+ }
82393
82432
  }
82394
82433
 
82395
82434
  // Programmatic value syncs leave availableOptions stale because
@@ -82403,18 +82442,6 @@ class AuroCombobox extends AuroElement$3 {
82403
82442
  this._programmaticFilterRefresh = false;
82404
82443
  }, 0);
82405
82444
  }
82406
-
82407
- // base-input skips auto-validate when focus is inside its own shadow,
82408
- // which it is after setTriggerInputFocus — re-run so prior tooShort
82409
- // clears. processCreditCard reasserts errorMessage on every render.
82410
- if (!isEcho && this.menu.optionSelected && this.input.validate) {
82411
- this.input.updateComplete.then(() => {
82412
- this.input.validate(true);
82413
- if (this.input.validity === 'valid') {
82414
- this.input.errorMessage = '';
82415
- }
82416
- });
82417
- }
82418
82445
  });
82419
82446
 
82420
82447
  this.menu.addEventListener('auroMenu-customEventFired', () => {
@@ -82749,15 +82776,7 @@ class AuroCombobox extends AuroElement$3 {
82749
82776
  this.menu.value = undefined;
82750
82777
  this.validation.reset(this);
82751
82778
  this.touched = false;
82752
- // Force validity back to the cleared state. validation.reset() calls
82753
- // validate() at the end, and (post-credit-card-fix) the trigger input's
82754
- // residual validity may still be copied into the combobox by the
82755
- // auroInputElements loop. Explicitly clear here so reset() actually
82756
- // leaves the component in a "no validity" state.
82757
82779
  this.validity = undefined;
82758
- this.errorMessage = '';
82759
- this.input.validity = undefined;
82760
- this.input.errorMessage = '';
82761
82780
  }
82762
82781
 
82763
82782
  /**
@@ -82810,52 +82829,27 @@ class AuroCombobox extends AuroElement$3 {
82810
82829
  this.input.value = this.value;
82811
82830
  }
82812
82831
 
82813
- // Sync menu.value only when an option actually matches; otherwise clear it.
82832
+ // Sync menu.value only when an option actually matches, or when filter
82833
+ // mode needs setMenuValue to dispatch auroMenu-selectValueFailure (the
82834
+ // listener at line 1206 clears combobox.value, mirroring select's
82835
+ // pattern). Otherwise sync the input display for freeform values.
82814
82836
  if (this.menu.options && this.menu.options.length > 0) {
82815
- if (this.menu.options.some((opt) => opt.value === this.value)) {
82816
- this.setMenuValue(this.value);
82817
- } else if (this.behavior === 'filter') {
82818
- // In filter mode, freeform values aren't allowed. Push the unmatched
82819
- // value through setMenuValue so menu dispatches auroMenu-selectValueFailure
82820
- // and the listener at line 1206 clears combobox.value (mirrors select's
82821
- // pattern). Bypassing setMenuValue here would skip the failure cascade.
82837
+ if (this.menu.options.some((opt) => opt.value === this.value) || this.behavior === 'filter') {
82822
82838
  this.setMenuValue(this.value);
82823
82839
  } else {
82824
- if (this.menu.value) {
82825
- this.menu.value = undefined;
82840
+ // Clear menu.value AND menu.optionSelected together. Clearing only
82841
+ // menu.value leaves the previously-selected option element pinned
82842
+ // as menu.optionSelected; a later auroMenu-selectedOption event
82843
+ // would then write its stale .value back into combobox.value.
82844
+ if (this.menu.value || this.menu.optionSelected) {
82845
+ this.menu.clearSelection();
82826
82846
  }
82827
- // Sync the display for programmatic freeform value changes (e.g. the
82828
- // swap-values pattern). Guard with _syncingDisplayValue so that the
82829
- // re-entrant input event from base-input.notifyValueChanged() is
82830
- // ignored by handleInputValueChange. Skip the sync when input already
82831
- // matches to avoid spurious events during the normal typing path.
82832
- const nextValue = this.value || '';
82833
- const triggerNeedsSync = this.input.value !== nextValue;
82834
- const bibNeedsSync = Boolean(this.inputInBib && this.inputInBib !== this.input && this.inputInBib.value !== nextValue);
82835
- if (triggerNeedsSync || bibNeedsSync) {
82836
- this._syncingDisplayValue = true;
82837
- if (triggerNeedsSync) {
82838
- this.input.value = nextValue;
82839
- }
82840
- if (bibNeedsSync) {
82841
- this.inputInBib.value = nextValue;
82842
- }
82843
- const pending = [];
82844
- if (triggerNeedsSync) {
82845
- pending.push(this.input.updateComplete);
82846
- }
82847
- if (bibNeedsSync) {
82848
- pending.push(this.inputInBib.updateComplete);
82849
- }
82850
- Promise.all(pending).then(() => {
82851
- if (triggerNeedsSync && this.input.maskInstance && typeof this.input.maskInstance.updateValue === 'function') {
82852
- this.input.maskInstance.updateValue();
82853
- }
82854
- if (bibNeedsSync && this.inputInBib && this.inputInBib.maskInstance && typeof this.inputInBib.maskInstance.updateValue === 'function') {
82855
- this.inputInBib.maskInstance.updateValue();
82856
- }
82857
- this._syncingDisplayValue = false;
82858
- // handleInputValueChange bailed on the flag — refresh the filter.
82847
+ // Suggestion-mode freeform value: sync the trigger + bib to show it,
82848
+ // then refresh the filter once the inputs flush (handleInputValueChange
82849
+ // bailed on _syncingDisplayValue).
82850
+ const syncPromise = this.syncInputValuesAcrossTriggerAndBib(this.value || '');
82851
+ if (syncPromise) {
82852
+ syncPromise.then(() => {
82859
82853
  this._programmaticFilterRefresh = true;
82860
82854
  this.handleMenuOptions();
82861
82855
  setTimeout(() => {
@@ -89688,7 +89682,7 @@ let AuroHelpText$1 = class AuroHelpText extends i$3 {
89688
89682
  }
89689
89683
  };
89690
89684
 
89691
- var formkitVersion$1 = '202606192121';
89685
+ var formkitVersion$1 = '202606231948';
89692
89686
 
89693
89687
  class AuroElement extends i$3 {
89694
89688
  static get properties() {
@@ -90639,6 +90633,14 @@ class AuroDropdown extends AuroElement {
90639
90633
  };
90640
90634
  this.addEventListener('keydown', this._bibTabHandler);
90641
90635
 
90636
+ // Suppress AuroFloatingUI's auto-hide-on-focus-loss while the
90637
+ // desktopModal trap owns focus management. Without this, the very
90638
+ // first focus move into the bib (from the RAF below) triggers
90639
+ // handleFocusLoss → hideBib, tearing down the trap before the
90640
+ // user can press Tab.
90641
+ this._priorNoHideOnFocusLoss = this.noHideOnThisFocusLoss;
90642
+ this.noHideOnThisFocusLoss = true;
90643
+
90642
90644
  // Move initial focus into the bib content, matching FocusTrap behavior
90643
90645
  requestAnimationFrame(() => {
90644
90646
  const focusables = getFocusableElements(this.bibContent);
@@ -91701,7 +91703,7 @@ class AuroHelpText extends i$3 {
91701
91703
  }
91702
91704
  }
91703
91705
 
91704
- var formkitVersion = '202606192121';
91706
+ var formkitVersion = '202606231948';
91705
91707
 
91706
91708
  var styleCss = i$6`.util_displayInline{display:inline}.util_displayInlineBlock{display:inline-block}.util_displayBlock{display:block}.util_displayFlex{display:flex}.util_displayHidden{display:none}.util_displayHiddenVisually{position:absolute;overflow:hidden;clip:rect(1px, 1px, 1px, 1px);width:1px;height:1px;padding:0;border:0}.body-default{font-size:var(--wcss-body-default-font-size, 1rem);font-weight:var(--wcss-body-default-weight, );line-height:var(--wcss-body-default-line-height, 1.5rem)}.body-default,.body-default-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;letter-spacing:var(--wcss-body-letter-spacing, 0)}.body-default-emphasized{font-size:var(--wcss-body-default-emphasized-font-size, 1rem);font-weight:var(--wcss-body-default-emphasized-weight, );line-height:var(--wcss-body-default-emphasized-line-height, 1.5rem)}.body-lg{font-size:var(--wcss-body-lg-font-size, 1.125rem);font-weight:var(--wcss-body-lg-weight, );line-height:var(--wcss-body-lg-line-height, 1.625rem)}.body-lg,.body-lg-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;letter-spacing:var(--wcss-body-letter-spacing, 0)}.body-lg-emphasized{font-size:var(--wcss-body-lg-emphasized-font-size, 1.125rem);font-weight:var(--wcss-body-lg-emphasized-weight, );line-height:var(--wcss-body-lg-emphasized-line-height, 1.625rem)}.body-sm{font-size:var(--wcss-body-sm-font-size, 0.875rem);font-weight:var(--wcss-body-sm-weight, );line-height:var(--wcss-body-sm-line-height, 1.25rem)}.body-sm,.body-sm-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;letter-spacing:var(--wcss-body-letter-spacing, 0)}.body-sm-emphasized{font-size:var(--wcss-body-sm-emphasized-font-size, 0.875rem);font-weight:var(--wcss-body-sm-emphasized-weight, );line-height:var(--wcss-body-sm-emphasized-line-height, 1.25rem)}.body-xs{font-size:var(--wcss-body-xs-font-size, 0.75rem);font-weight:var(--wcss-body-xs-weight, );line-height:var(--wcss-body-xs-line-height, 1rem)}.body-xs,.body-xs-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;letter-spacing:var(--wcss-body-letter-spacing, 0)}.body-xs-emphasized{font-size:var(--wcss-body-xs-emphasized-font-size, 0.75rem);font-weight:var(--wcss-body-xs-emphasized-weight, );line-height:var(--wcss-body-xs-emphasized-line-height, 1rem)}.body-2xs{font-size:var(--wcss-body-2xs-font-size, 0.625rem);font-weight:var(--wcss-body-2xs-weight, );line-height:var(--wcss-body-2xs-line-height, 0.875rem)}.body-2xs,.body-2xs-emphasized{font-family:var(--wcss-body-family, "AS Circular"),system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;letter-spacing:var(--wcss-body-letter-spacing, 0)}.body-2xs-emphasized{font-size:var(--wcss-body-2xs-emphasized-font-size, 0.625rem);font-weight:var(--wcss-body-2xs-emphasized-weight, );line-height:var(--wcss-body-2xs-emphasized-line-height, 0.875rem)}.display-2xl{font-family:var(--wcss-display-2xl-family, "AS Circular"),var(--wcss-display-2xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-2xl-font-size, clamp(3.5rem, 6vw, 5.375rem));font-weight:var(--wcss-display-2xl-weight, 300);letter-spacing:var(--wcss-display-2xl-letter-spacing, 0);line-height:var(--wcss-display-2xl-line-height, 1.3)}.display-xl{font-family:var(--wcss-display-xl-family, "AS Circular"),var(--wcss-display-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-xl-font-size, clamp(3rem, 5.3333333333vw, 4.5rem));font-weight:var(--wcss-display-xl-weight, 300);letter-spacing:var(--wcss-display-xl-letter-spacing, 0);line-height:var(--wcss-display-xl-line-height, 1.3)}.display-lg{font-family:var(--wcss-display-lg-family, "AS Circular"),var(--wcss-display-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-lg-font-size, clamp(2.75rem, 4.6666666667vw, 4rem));font-weight:var(--wcss-display-lg-weight, 300);letter-spacing:var(--wcss-display-lg-letter-spacing, 0);line-height:var(--wcss-display-lg-line-height, 1.3)}.display-md{font-family:var(--wcss-display-md-family, "AS Circular"),var(--wcss-display-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-md-font-size, clamp(2.5rem, 4vw, 3.5rem));font-weight:var(--wcss-display-md-weight, 300);letter-spacing:var(--wcss-display-md-letter-spacing, 0);line-height:var(--wcss-display-md-line-height, 1.3)}.display-sm{font-family:var(--wcss-display-sm-family, "AS Circular"),var(--wcss-display-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-sm-font-size, clamp(2rem, 3.6666666667vw, 3rem));font-weight:var(--wcss-display-sm-weight, 300);letter-spacing:var(--wcss-display-sm-letter-spacing, 0);line-height:var(--wcss-display-sm-line-height, 1.3)}.display-xs{font-family:var(--wcss-display-xs-family, "AS Circular"),var(--wcss-display-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-display-xs-font-size, clamp(1.75rem, 3vw, 2.375rem));font-weight:var(--wcss-display-xs-weight, 300);letter-spacing:var(--wcss-display-xs-letter-spacing, 0);line-height:var(--wcss-display-xs-line-height, 1.3)}.heading-xl{font-family:var(--wcss-heading-xl-family, "AS Circular"),var(--wcss-heading-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-xl-font-size, clamp(2rem, 3vw, 2.5rem));font-weight:var(--wcss-heading-xl-weight, 300);letter-spacing:var(--wcss-heading-xl-letter-spacing, 0);line-height:var(--wcss-heading-xl-line-height, 1.3)}.heading-lg{font-family:var(--wcss-heading-lg-family, "AS Circular"),var(--wcss-heading-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-lg-font-size, clamp(1.75rem, 2.6666666667vw, 2.25rem));font-weight:var(--wcss-heading-lg-weight, 300);letter-spacing:var(--wcss-heading-lg-letter-spacing, 0);line-height:var(--wcss-heading-lg-line-height, 1.3)}.heading-md{font-family:var(--wcss-heading-md-family, "AS Circular"),var(--wcss-heading-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-md-font-size, clamp(1.625rem, 2.3333333333vw, 1.75rem));font-weight:var(--wcss-heading-md-weight, 300);letter-spacing:var(--wcss-heading-md-letter-spacing, 0);line-height:var(--wcss-heading-md-line-height, 1.3)}.heading-sm{font-family:var(--wcss-heading-sm-family, "AS Circular"),var(--wcss-heading-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-sm-font-size, clamp(1.375rem, 2vw, 1.5rem));font-weight:var(--wcss-heading-sm-weight, 300);letter-spacing:var(--wcss-heading-sm-letter-spacing, 0);line-height:var(--wcss-heading-sm-line-height, 1.3)}.heading-xs{font-family:var(--wcss-heading-xs-family, "AS Circular"),var(--wcss-heading-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-xs-font-size, clamp(1.25rem, 1.6666666667vw, 1.25rem));font-weight:var(--wcss-heading-xs-weight, 300);letter-spacing:var(--wcss-heading-xs-letter-spacing, 0);line-height:var(--wcss-heading-xs-line-height, 1.3)}.heading-2xs{font-family:var(--wcss-heading-2xs-family, "AS Circular"),var(--wcss-heading-2xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-heading-2xs-font-size, clamp(1.125rem, 1.5vw, 1.125rem));font-weight:var(--wcss-heading-2xs-weight, 300);letter-spacing:var(--wcss-heading-2xs-letter-spacing, 0);line-height:var(--wcss-heading-2xs-line-height, 1.3)}.accent-2xl{font-family:var(--wcss-accent-2xl-family, "Good OT"),var(--wcss-accent-2xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-2xl-font-size, clamp(2rem, 3.1666666667vw, 2.375rem));font-weight:var(--wcss-accent-2xl-weight, 450);letter-spacing:var(--wcss-accent-2xl-letter-spacing, 0.05em);line-height:var(--wcss-accent-2xl-line-height, 1)}.accent-2xl,.accent-xl{text-transform:uppercase}.accent-xl{font-family:var(--wcss-accent-xl-family, "Good OT"),var(--wcss-accent-xl-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-xl-font-size, clamp(1.625rem, 2.3333333333vw, 2rem));font-weight:var(--wcss-accent-xl-weight, 450);letter-spacing:var(--wcss-accent-xl-letter-spacing, 0.05em);line-height:var(--wcss-accent-xl-line-height, 1.3)}.accent-lg{font-family:var(--wcss-accent-lg-family, "Good OT"),var(--wcss-accent-lg-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-lg-font-size, clamp(1.5rem, 2.1666666667vw, 1.75rem));font-weight:var(--wcss-accent-lg-weight, 450);letter-spacing:var(--wcss-accent-lg-letter-spacing, 0.05em);line-height:var(--wcss-accent-lg-line-height, 1.3)}.accent-lg,.accent-md{text-transform:uppercase}.accent-md{font-family:var(--wcss-accent-md-family, "Good OT"),var(--wcss-accent-md-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-md-font-size, clamp(1.375rem, 1.8333333333vw, 1.5rem));font-weight:var(--wcss-accent-md-weight, 500);letter-spacing:var(--wcss-accent-md-letter-spacing, 0.05em);line-height:var(--wcss-accent-md-line-height, 1.3)}.accent-sm{font-family:var(--wcss-accent-sm-family, "Good OT"),var(--wcss-accent-sm-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-sm-font-size, clamp(1.125rem, 1.5vw, 1.25rem));font-weight:var(--wcss-accent-sm-weight, 500);letter-spacing:var(--wcss-accent-sm-letter-spacing, 0.05em);line-height:var(--wcss-accent-sm-line-height, 1.3)}.accent-sm,.accent-xs{text-transform:uppercase}.accent-xs{font-family:var(--wcss-accent-xs-family, "Good OT"),var(--wcss-accent-xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-xs-font-size, clamp(1rem, 1.3333333333vw, 1rem));font-weight:var(--wcss-accent-xs-weight, 500);letter-spacing:var(--wcss-accent-xs-letter-spacing, 0.1em);line-height:var(--wcss-accent-xs-line-height, 1.3)}.accent-2xs{font-family:var(--wcss-accent-2xs-family, "Good OT"),var(--wcss-accent-2xs-family-fallback, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif);font-size:var(--wcss-accent-2xs-font-size, clamp(0.875rem, 1.1666666667vw, 0.875rem));font-weight:var(--wcss-accent-2xs-weight, 450);letter-spacing:var(--wcss-accent-2xs-letter-spacing, 0.1em);line-height:var(--wcss-accent-2xs-line-height, 1.3);text-transform:uppercase}[auro-dropdown]{--ds-auro-dropdown-trigger-border-color: var(--ds-auro-select-border-color);--ds-auro-dropdown-trigger-background-color: var(--ds-auro-select-background-color);--ds-auro-dropdown-trigger-container-color: var(--ds-auro-select-background-color);--ds-auro-dropdown-trigger-outline-color: var(--ds-auro-select-outline-color)}:host{display:inline-block;text-align:left;vertical-align:top}:host([layout*=emphasized]) [auro-dropdown],:host([layout*=snowflake]) [auro-dropdown]{--ds-auro-select-border-color: transparent}:host([layout*=emphasized]) .mainContent,:host([layout*=snowflake]) .mainContent{text-align:center}.mainContent{position:relative;display:flex;overflow:hidden;flex:1;flex-direction:column;align-items:center;justify-content:center}.valueContainer [slot=displayValue]{display:none}.accents{display:flex;flex-direction:row;align-items:center;justify-content:center}::slotted([slot=typeIcon]){margin-right:var(--ds-size-100, 0.5rem)}.displayValue{display:block}.displayValue:not(.force){display:none}.displayValue:not(.force).hasContent:is(.withValue):not(.hasFocus){display:block}.triggerContent{display:flex;width:100%;align-items:center;justify-content:center}:host([layout*=emphasized]) .triggerContent{padding:0 var(--ds-size-100, 0.5rem) 0 var(--ds-size-300, 1.5rem)}:host([layout*=snowflake]) .triggerContent{padding:0 var(--ds-size-100, 0.5rem) 0 var(--ds-size-200, 1rem)}:host([layout*=snowflake]) label{padding-block:var(--ds-size-25, 0.125rem)}:host([layout*=classic]) .triggerContent{padding:0 var(--ds-size-100, 0.5rem)}:host([layout*=classic]) .mainContent{align-items:start}:host([layout*=classic]) label{overflow:hidden;cursor:text;text-overflow:ellipsis;white-space:nowrap}:host([layout*=classic]) .value{height:auto}label{color:var(--ds-auro-select-label-text-color)}:host(:is([validity]:not([validity=valid]))) [auro-dropdown]{--ds-auro-select-border-color: var(--ds-basic-color-status-error, #e31f26);--ds-auro-select-outline-color: var(--ds-basic-color-status-error, #e31f26);--ds-auro-dropdown-helptext-text-color: var(--ds-basic-color-texticon-default, #2a2a2a)}:host([ondark]:is([validity]:not([validity=valid]))) [auro-dropdown],:host([appearance=inverse]:is([validity]:not([validity=valid]))) [auro-dropdown]{--ds-auro-select-border-color: var(--ds-advanced-color-state-error-inverse, #f9a4a8);--ds-auro-select-outline-color: var(--ds-advanced-color-state-error-inverse, #f9a4a8);--ds-auro-dropdown-helptext-text-color: var(--ds-basic-color-texticon-inverse, #ffffff)}#slotHolder{display:none}:host([fluid]){width:100%}:host([disabled]){pointer-events:none;user-select:none}:host([disabled]:not([ondark])) [auro-dropdown],:host([disabled]:not([appearance=inverse])) [auro-dropdown]{--ds-auro-select-border-color: var(--ds-basic-color-border-subtle, #dddddd)}:host(:not([layout*=classic])[disabled][ondark]) [auro-dropdown],:host(:not([layout*=classic])[disabled][appearance=inverse]) [auro-dropdown]{--ds-auro-select-border-color: transparent}`;
91707
91709