@ionic/core 8.7.7-nightly.20251015 → 8.7.8-dev.11760615726.158c2a8a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/components/button.js +3 -7
  2. package/components/header.js +41 -3
  3. package/components/ion-accordion-group.js +28 -26
  4. package/components/ion-accordion.js +7 -3
  5. package/components/ion-input.js +6 -14
  6. package/components/ion-select.js +58 -10
  7. package/components/ion-textarea.js +5 -13
  8. package/components/{notch-controller.js → validity.js} +14 -1
  9. package/dist/cjs/ion-accordion_2.cjs.entry.js +35 -29
  10. package/dist/cjs/ion-app_8.cjs.entry.js +41 -3
  11. package/dist/cjs/ion-button_2.cjs.entry.js +3 -7
  12. package/dist/cjs/ion-input.cjs.entry.js +7 -15
  13. package/dist/cjs/ion-select_3.cjs.entry.js +56 -10
  14. package/dist/cjs/ion-textarea.cjs.entry.js +6 -14
  15. package/dist/cjs/ionic.cjs.js +1 -1
  16. package/dist/cjs/loader.cjs.js +1 -1
  17. package/dist/cjs/{notch-controller-Bzqhjm4f.js → validity-C8QoAYT2.js} +14 -0
  18. package/dist/collection/components/accordion/accordion.js +7 -3
  19. package/dist/collection/components/accordion-group/accordion-group.js +28 -26
  20. package/dist/collection/components/button/button.js +3 -7
  21. package/dist/collection/components/header/header.js +5 -4
  22. package/dist/collection/components/header/header.utils.js +37 -0
  23. package/dist/collection/components/input/input.js +6 -14
  24. package/dist/collection/components/select/select.js +59 -11
  25. package/dist/collection/components/textarea/textarea.js +5 -13
  26. package/dist/collection/utils/forms/index.js +1 -0
  27. package/dist/collection/utils/forms/validity.js +15 -0
  28. package/dist/docs.json +2 -2
  29. package/dist/esm/ion-accordion_2.entry.js +35 -29
  30. package/dist/esm/ion-app_8.entry.js +41 -3
  31. package/dist/esm/ion-button_2.entry.js +3 -7
  32. package/dist/esm/ion-input.entry.js +6 -14
  33. package/dist/esm/ion-select_3.entry.js +55 -9
  34. package/dist/esm/ion-textarea.entry.js +5 -13
  35. package/dist/esm/ionic.js +1 -1
  36. package/dist/esm/loader.js +1 -1
  37. package/dist/esm/{notch-controller-BwelN_JM.js → validity-B8oWougr.js} +14 -1
  38. package/dist/ionic/ionic.esm.js +1 -1
  39. package/dist/ionic/p-16280296.entry.js +4 -0
  40. package/dist/ionic/p-43ed1ef5.entry.js +4 -0
  41. package/dist/ionic/p-4c85d268.entry.js +4 -0
  42. package/dist/ionic/p-4cc26913.entry.js +4 -0
  43. package/dist/ionic/p-8bdfc8f6.entry.js +4 -0
  44. package/dist/ionic/{p-DCv9sLH2.js → p-DieJyvMP.js} +1 -1
  45. package/dist/ionic/p-f65f9308.entry.js +4 -0
  46. package/dist/types/components/accordion-group/accordion-group-interface.d.ts +1 -0
  47. package/dist/types/components/accordion-group/accordion-group.d.ts +1 -0
  48. package/dist/types/components/header/header.utils.d.ts +10 -0
  49. package/dist/types/components/input/input.d.ts +0 -4
  50. package/dist/types/components/select/select.d.ts +6 -0
  51. package/dist/types/components/textarea/textarea.d.ts +0 -4
  52. package/dist/types/utils/forms/index.d.ts +1 -0
  53. package/dist/types/utils/forms/validity.d.ts +10 -0
  54. package/hydrate/index.js +121 -70
  55. package/hydrate/index.mjs +121 -70
  56. package/package.json +2 -2
  57. package/dist/ionic/p-1c8a476d.entry.js +0 -4
  58. package/dist/ionic/p-62e50f80.entry.js +0 -4
  59. package/dist/ionic/p-7647da93.entry.js +0 -4
  60. package/dist/ionic/p-785026d7.entry.js +0 -4
  61. package/dist/ionic/p-78c74a3e.entry.js +0 -4
  62. package/dist/ionic/p-913a7c1e.entry.js +0 -4
package/hydrate/index.js CHANGED
@@ -4042,7 +4042,11 @@ const accordionMdCss = ":host{display:block;position:relative;width:100%;backgro
4042
4042
  class Accordion {
4043
4043
  constructor(hostRef) {
4044
4044
  registerInstance(this, hostRef);
4045
- this.updateListener = () => this.updateState(false);
4045
+ this.updateListener = (ev) => {
4046
+ var _a, _b;
4047
+ const initialUpdate = (_b = (_a = ev.detail) === null || _a === void 0 ? void 0 : _a.initial) !== null && _b !== void 0 ? _b : false;
4048
+ this.updateState(initialUpdate);
4049
+ };
4046
4050
  this.state = 1 /* AccordionState.Collapsed */;
4047
4051
  this.isNext = false;
4048
4052
  this.isPrevious = false;
@@ -4344,7 +4348,7 @@ class Accordion {
4344
4348
  const headerPart = expanded ? 'header expanded' : 'header';
4345
4349
  const contentPart = expanded ? 'content expanded' : 'content';
4346
4350
  this.setAria(expanded);
4347
- return (hAsync(Host, { key: '073e1d02c18dcbc20c68648426e87c14750c031d', class: {
4351
+ return (hAsync(Host, { key: '4c8a2978e1c428f1b856d80adcee31d7abb3925d', class: {
4348
4352
  [mode]: true,
4349
4353
  'accordion-expanding': this.state === 8 /* AccordionState.Expanding */,
4350
4354
  'accordion-expanded': this.state === 4 /* AccordionState.Expanded */,
@@ -4355,7 +4359,7 @@ class Accordion {
4355
4359
  'accordion-disabled': disabled,
4356
4360
  'accordion-readonly': readonly,
4357
4361
  'accordion-animated': this.shouldAnimate(),
4358
- } }, hAsync("div", { key: '9b4cf326de8bb6b4033992903c0c1bfd7eea9bcc', onClick: () => this.toggleExpanded(), id: "header", part: headerPart, "aria-controls": "content", ref: (headerEl) => (this.headerEl = headerEl) }, hAsync("slot", { key: '464c32a37f64655eacf4218284214f5f30b14a1e', name: "header" })), hAsync("div", { key: '8bb52e6a62d7de0106b253201a89a32e79d9a594', id: "content", part: contentPart, role: "region", "aria-labelledby": "header", ref: (contentEl) => (this.contentEl = contentEl) }, hAsync("div", { key: '1d9dfd952ad493754aaeea7a8f625b33c2dd90a0', id: "content-wrapper", ref: (contentElWrapper) => (this.contentElWrapper = contentElWrapper) }, hAsync("slot", { key: '970dfbc55a612d739d0ca3b7b1a08e5c96d0c479', name: "content" })))));
4362
+ } }, hAsync("div", { key: '789c5cec6e54d2aa7528d63e880e1f2cf1924ff2', onClick: () => this.toggleExpanded(), id: "header", part: headerPart, "aria-controls": "content", ref: (headerEl) => (this.headerEl = headerEl) }, hAsync("slot", { key: 'c98907a0e54d2edc0e6e50b8dce1af6e81588eba', name: "header" })), hAsync("div", { key: 'adfe9e7083d5addc1b7cbba0cfc9c65d5809f8f6', id: "content", part: contentPart, role: "region", "aria-labelledby": "header", ref: (contentEl) => (this.contentEl = contentEl) }, hAsync("div", { key: 'c77a044fae8d5173ea180bc57c84d24b39abb494', id: "content-wrapper", ref: (contentElWrapper) => (this.contentElWrapper = contentElWrapper) }, hAsync("slot", { key: '8214cfd99fcb7a77fc08b1145b2c3d58ddf7f185', name: "content" })))));
4359
4363
  }
4360
4364
  static get delegatesFocus() { return true; }
4361
4365
  get el() { return getElement(this); }
@@ -4421,26 +4425,7 @@ class AccordionGroup {
4421
4425
  this.expand = 'compact';
4422
4426
  }
4423
4427
  valueChanged() {
4424
- const { value, multiple } = this;
4425
- if (!multiple && Array.isArray(value)) {
4426
- /**
4427
- * We do some processing on the `value` array so
4428
- * that it looks more like an array when logged to
4429
- * the console.
4430
- * Example given ['a', 'b']
4431
- * Default toString() behavior: a,b
4432
- * Custom behavior: ['a', 'b']
4433
- */
4434
- printIonWarning(`[ion-accordion-group] - An array of values was passed, but multiple is "false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false".
4435
-
4436
- Value Passed: [${value.map((v) => `'${v}'`).join(', ')}]
4437
- `, this.el);
4438
- }
4439
- /**
4440
- * Do not use `value` here as that will be
4441
- * not account for the adjustment we make above.
4442
- */
4443
- this.ionValueChange.emit({ value: this.value });
4428
+ this.emitValueChange(false);
4444
4429
  }
4445
4430
  async disabledChanged() {
4446
4431
  const { disabled } = this;
@@ -4514,11 +4499,10 @@ class AccordionGroup {
4514
4499
  * it is possible for the value to be set after the Web Component
4515
4500
  * initializes but before the value watcher is set up in Stencil.
4516
4501
  * As a result, the watcher callback may not be fired.
4517
- * We work around this by manually calling the watcher
4518
- * callback when the component has loaded and the watcher
4519
- * is configured.
4502
+ * We work around this by manually emitting a value change when the component
4503
+ * has loaded and the watcher is configured.
4520
4504
  */
4521
- this.valueChanged();
4505
+ this.emitValueChange(true);
4522
4506
  }
4523
4507
  /**
4524
4508
  * Sets the value property and emits ionChange.
@@ -4598,15 +4582,37 @@ class AccordionGroup {
4598
4582
  async getAccordions() {
4599
4583
  return Array.from(this.el.querySelectorAll(':scope > ion-accordion'));
4600
4584
  }
4585
+ emitValueChange(initial) {
4586
+ const { value, multiple } = this;
4587
+ if (!multiple && Array.isArray(value)) {
4588
+ /**
4589
+ * We do some processing on the `value` array so
4590
+ * that it looks more like an array when logged to
4591
+ * the console.
4592
+ * Example given ['a', 'b']
4593
+ * Default toString() behavior: a,b
4594
+ * Custom behavior: ['a', 'b']
4595
+ */
4596
+ printIonWarning(`[ion-accordion-group] - An array of values was passed, but multiple is "false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false".
4597
+
4598
+ Value Passed: [${value.map((v) => `'${v}'`).join(', ')}]
4599
+ `, this.el);
4600
+ }
4601
+ /**
4602
+ * Do not use `value` here as that will not account
4603
+ * for the adjustment we make above.
4604
+ */
4605
+ this.ionValueChange.emit({ value: this.value, initial });
4606
+ }
4601
4607
  render() {
4602
4608
  const { disabled, readonly, expand } = this;
4603
4609
  const mode = getIonMode$1(this);
4604
- return (hAsync(Host, { key: 'd1a79a93179474fbba66fcf11a92f4871dacc975', class: {
4610
+ return (hAsync(Host, { key: 'c69c3fa4c844cb2e88f778af9a8c757d752bb261', class: {
4605
4611
  [mode]: true,
4606
4612
  'accordion-group-disabled': disabled,
4607
4613
  'accordion-group-readonly': readonly,
4608
4614
  [`accordion-group-expand-${expand}`]: true,
4609
- }, role: "presentation" }, hAsync("slot", { key: 'e6b8954b686d1fbb4fc92adb07fddc97a24b0a31' })));
4615
+ }, role: "presentation" }, hAsync("slot", { key: 'fe661334ae44c291cc4e61f22f17a7c51f62acb7' })));
4610
4616
  }
4611
4617
  get el() { return getElement(this); }
4612
4618
  static get watchers() { return {
@@ -9263,11 +9269,7 @@ class Button {
9263
9269
  target,
9264
9270
  };
9265
9271
  let fill = this.fill;
9266
- /**
9267
- * We check both undefined and null to
9268
- * work around https://github.com/ionic-team/stencil/issues/3586.
9269
- */
9270
- if (fill == null) {
9272
+ if (fill === undefined) {
9271
9273
  fill = this.inToolbar || this.inListHeader ? 'clear' : 'solid';
9272
9274
  }
9273
9275
  /**
@@ -9280,7 +9282,7 @@ class Button {
9280
9282
  {
9281
9283
  type !== 'button' && this.renderHiddenButton();
9282
9284
  }
9283
- return (hAsync(Host, { key: 'b105ad09215adb3ca2298acdadf0dc9154bbb9b0', onClick: this.handleClick, "aria-disabled": disabled ? 'true' : null, class: createColorClasses$1(color, {
9285
+ return (hAsync(Host, { key: 'ed82ea53705523f9afc5f1a9addff44cc6424f27', onClick: this.handleClick, "aria-disabled": disabled ? 'true' : null, class: createColorClasses$1(color, {
9284
9286
  [mode]: true,
9285
9287
  [buttonType]: true,
9286
9288
  [`${buttonType}-${expand}`]: expand !== undefined,
@@ -9295,7 +9297,7 @@ class Button {
9295
9297
  'button-disabled': disabled,
9296
9298
  'ion-activatable': true,
9297
9299
  'ion-focusable': true,
9298
- }) }, hAsync(TagType, Object.assign({ key: '66b4e7112bcb9e41d5a723fbbadb0a3104f9ee1d' }, attrs, { class: "button-native", part: "native", disabled: disabled, onFocus: this.onFocus, onBlur: this.onBlur }, inheritedAttributes), hAsync("span", { key: '1439fc3da280221028dcf7ce8ec9dab273c4d4bb', class: "button-inner" }, hAsync("slot", { key: 'd5269ae1afc87ec7b99746032f59cbae93720a9f', name: "icon-only", onSlotchange: this.slotChanged }), hAsync("slot", { key: '461c83e97aa246aa86d83e14f1e15a288d35041e', name: "start" }), hAsync("slot", { key: '807170d47101f9f6a333dd4ff489c89284f306fe' }), hAsync("slot", { key: 'e67f116dd0349a0d27893e4f3ff0ccef1d402f80', name: "end" })), mode === 'md' && hAsync("ion-ripple-effect", { key: '273f0bd9645a36c1bfd18a5c2ab4f81e22b7b989', type: this.rippleType }))));
9300
+ }) }, hAsync(TagType, Object.assign({ key: 'fadec13053469dd0405bbbc61b70ced568aa4826' }, attrs, { class: "button-native", part: "native", disabled: disabled, onFocus: this.onFocus, onBlur: this.onBlur }, inheritedAttributes), hAsync("span", { key: '6bf0e5144fb1148002e88038522402b789689d2c', class: "button-inner" }, hAsync("slot", { key: '25da0ca155cfa9e2754842c34f4fd09f576ac2d2', name: "icon-only", onSlotchange: this.slotChanged }), hAsync("slot", { key: '51414065bb11953ec9d818f8d9353589bc9072c5', name: "start" }), hAsync("slot", { key: 'c9b5f8842aeabd20628df2f4600f1257ea913d8d' }), hAsync("slot", { key: '478dd3671c7be1909fc84e672f0fa8dfe6082263', name: "end" })), mode === 'md' && hAsync("ion-ripple-effect", { key: 'e1d55f85a55144d743f58a5914cd116cb065fa8c', type: this.rippleType }))));
9299
9301
  }
9300
9302
  get el() { return getElement(this); }
9301
9303
  static get watchers() { return {
@@ -15229,6 +15231,8 @@ class Grid {
15229
15231
  }
15230
15232
 
15231
15233
  const TRANSITION = 'all 0.2s ease-in-out';
15234
+ const ROLE_NONE = 'none';
15235
+ const ROLE_BANNER = 'banner';
15232
15236
  const cloneElement = (tagName) => {
15233
15237
  const getCachedEl = document.querySelector(`${tagName}.ion-cloned-element`);
15234
15238
  if (getCachedEl !== null) {
@@ -15355,6 +15359,7 @@ const setHeaderActive = (headerIndex, active = true) => {
15355
15359
  const toolbars = headerIndex.toolbars;
15356
15360
  const ionTitles = toolbars.map((toolbar) => toolbar.ionTitleEl);
15357
15361
  if (active) {
15362
+ headerEl.setAttribute('role', ROLE_BANNER);
15358
15363
  headerEl.classList.remove('header-collapse-condense-inactive');
15359
15364
  ionTitles.forEach((ionTitle) => {
15360
15365
  if (ionTitle) {
@@ -15363,6 +15368,16 @@ const setHeaderActive = (headerIndex, active = true) => {
15363
15368
  });
15364
15369
  }
15365
15370
  else {
15371
+ /**
15372
+ * There can only be one banner landmark per page.
15373
+ * By default, all ion-headers have the banner role.
15374
+ * This causes an accessibility issue when using a
15375
+ * condensed header since there are two ion-headers
15376
+ * on the page at once (active and inactive).
15377
+ * To solve this, the role needs to be toggled
15378
+ * based on which header is active.
15379
+ */
15380
+ headerEl.setAttribute('role', ROLE_NONE);
15366
15381
  headerEl.classList.add('header-collapse-condense-inactive');
15367
15382
  /**
15368
15383
  * The small title should only be accessed by screen readers
@@ -15422,6 +15437,30 @@ const handleHeaderFade = (scrollEl, baseEl, condenseHeader) => {
15422
15437
  });
15423
15438
  });
15424
15439
  };
15440
+ /**
15441
+ * Get the role type for the ion-header.
15442
+ *
15443
+ * @param isInsideMenu If ion-header is inside ion-menu.
15444
+ * @param isCondensed If ion-header has collapse="condense".
15445
+ * @param mode The current mode.
15446
+ * @returns 'none' if inside ion-menu or if condensed in md
15447
+ * mode, otherwise 'banner'.
15448
+ */
15449
+ const getRoleType = (isInsideMenu, isCondensed, mode) => {
15450
+ // If the header is inside a menu, it should not have the banner role.
15451
+ if (isInsideMenu) {
15452
+ return ROLE_NONE;
15453
+ }
15454
+ /**
15455
+ * Only apply role="none" to `md` mode condensed headers
15456
+ * since the large header is never shown.
15457
+ */
15458
+ if (isCondensed && mode === 'md') {
15459
+ return ROLE_NONE;
15460
+ }
15461
+ // Default to banner role.
15462
+ return ROLE_BANNER;
15463
+ };
15425
15464
 
15426
15465
  const headerIosCss = "ion-header{display:block;position:relative;-ms-flex-order:-1;order:-1;width:100%;z-index:10}ion-header ion-toolbar:first-of-type{padding-top:var(--ion-safe-area-top, 0)}.header-ios ion-toolbar:last-of-type{--border-width:0 0 0.55px}@supports ((-webkit-backdrop-filter: blur(0)) or (backdrop-filter: blur(0))){.header-background{left:0;right:0;top:0;bottom:0;position:absolute;-webkit-backdrop-filter:saturate(180%) blur(20px);backdrop-filter:saturate(180%) blur(20px)}.header-translucent-ios ion-toolbar{--opacity:.8}.header-collapse-condense-inactive .header-background{-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px)}}.header-ios.ion-no-border ion-toolbar:last-of-type{--border-width:0}.header-collapse-fade ion-toolbar{--opacity-scale:inherit}.header-collapse-fade.header-transitioning ion-toolbar{--background:transparent;--border-style:none}.header-collapse-condense{z-index:9}.header-collapse-condense ion-toolbar{position:-webkit-sticky;position:sticky;top:0}.header-collapse-condense ion-toolbar:first-of-type{padding-top:0px;z-index:1}.header-collapse-condense ion-toolbar{z-index:0}.header-collapse-condense ion-toolbar:last-of-type{--border-width:0px}.header-collapse-condense ion-toolbar ion-searchbar{padding-top:0px;padding-bottom:13px}.header-collapse-main{--opacity-scale:1}.header-collapse-main ion-toolbar{--opacity-scale:inherit}.header-collapse-main ion-toolbar.in-toolbar ion-title,.header-collapse-main ion-toolbar.in-toolbar ion-buttons{-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.header-collapse-condense ion-toolbar,.header-collapse-condense-inactive.header-transitioning:not(.header-collapse-condense) ion-toolbar{--background:var(--ion-background-color, #fff)}.header-collapse-condense-inactive.header-transitioning:not(.header-collapse-condense) ion-toolbar{--border-style:none;--opacity-scale:1}.header-collapse-condense-inactive:not(.header-collapse-condense) ion-toolbar.in-toolbar ion-title,.header-collapse-condense-inactive:not(.header-collapse-condense) ion-toolbar.in-toolbar ion-buttons.buttons-collapse{opacity:0;pointer-events:none}.header-collapse-condense-inactive.header-collapse-condense ion-toolbar.in-toolbar ion-title,.header-collapse-condense-inactive.header-collapse-condense ion-toolbar.in-toolbar ion-buttons.buttons-collapse{visibility:hidden}ion-header.header-ios:not(.header-collapse-main):has(~ion-content ion-header.header-ios[collapse=condense],~ion-content ion-header.header-ios.header-collapse-condense){opacity:0}";
15427
15466
 
@@ -15566,16 +15605,17 @@ class Header {
15566
15605
  const { translucent, inheritedAttributes } = this;
15567
15606
  const mode = getIonMode$1(this);
15568
15607
  const collapse = this.collapse || 'none';
15608
+ const isCondensed = collapse === 'condense';
15569
15609
  // banner role must be at top level, so remove role if inside a menu
15570
- const roleType = hostContext('ion-menu', this.el) ? 'none' : 'banner';
15571
- return (hAsync(Host, Object.assign({ key: 'b6cc27f0b08afc9fcc889683525da765d80ba672', role: roleType, class: {
15610
+ const roleType = getRoleType(hostContext('ion-menu', this.el), isCondensed, mode);
15611
+ return (hAsync(Host, Object.assign({ key: '863c4568cd7b8c0ec55109f193bbbaed68a1346e', role: roleType, class: {
15572
15612
  [mode]: true,
15573
15613
  // Used internally for styling
15574
15614
  [`header-${mode}`]: true,
15575
15615
  [`header-translucent`]: this.translucent,
15576
15616
  [`header-collapse-${collapse}`]: true,
15577
15617
  [`header-translucent-${mode}`]: this.translucent,
15578
- } }, inheritedAttributes), mode === 'ios' && translucent && hAsync("div", { key: '395766d4dcee3398bc91960db21f922095292f14', class: "header-background" }), hAsync("slot", { key: '09a67ece27b258ff1248805d43d92a49b2c6859a' })));
15618
+ } }, inheritedAttributes), mode === 'ios' && translucent && hAsync("div", { key: '25c3bdce328b0b35607d154c8b8374679313d881', class: "header-background" }), hAsync("slot", { key: 'b44fab0a9be7920b9650da26117c783e751e1702' })));
15579
15619
  }
15580
15620
  get el() { return getElement(this); }
15581
15621
  static get style() { return {
@@ -16387,6 +16427,19 @@ const isOptionSelected = (currentValue, compareValue, compareWith) => {
16387
16427
  }
16388
16428
  };
16389
16429
 
16430
+ /**
16431
+ * Checks if the form element is in an invalid state based on
16432
+ * Ionic validation classes.
16433
+ *
16434
+ * @param el The form element to check.
16435
+ * @returns `true` if the element is invalid, `false` otherwise.
16436
+ */
16437
+ const checkInvalidState = (el) => {
16438
+ const hasIonTouched = el.classList.contains('ion-touched');
16439
+ const hasIonInvalid = el.classList.contains('ion-invalid');
16440
+ return hasIonTouched && hasIonInvalid;
16441
+ };
16442
+
16390
16443
  /**
16391
16444
  * Used to update a scoped component that uses emulated slots. This fires when
16392
16445
  * content is passed into the slot or when the content inside of a slot changes.
@@ -16752,20 +16805,12 @@ class Input {
16752
16805
  componentWillLoad() {
16753
16806
  this.inheritedAttributes = Object.assign(Object.assign({}, inheritAriaAttributes(this.el)), inheritAttributes$1(this.el, ['tabindex', 'title', 'data-form-type', 'dir']));
16754
16807
  }
16755
- /**
16756
- * Checks if the input is in an invalid state based on Ionic validation classes
16757
- */
16758
- checkInvalidState() {
16759
- const hasIonTouched = this.el.classList.contains('ion-touched');
16760
- const hasIonInvalid = this.el.classList.contains('ion-invalid');
16761
- return hasIonTouched && hasIonInvalid;
16762
- }
16763
16808
  connectedCallback() {
16764
16809
  const { el } = this;
16765
16810
  this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate());
16766
16811
  this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
16767
16812
  // Always set initial state
16768
- this.isInvalid = this.checkInvalidState();
16813
+ this.isInvalid = checkInvalidState(el);
16769
16814
  this.debounceChanged();
16770
16815
  }
16771
16816
  componentDidLoad() {
@@ -17019,7 +17064,7 @@ class Input {
17019
17064
  * TODO(FW-5592): Remove hasStartEndSlots condition
17020
17065
  */
17021
17066
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
17022
- return (hAsync(Host, { key: '8a51f0300d5bc66392f9ab9a6fa0b5d388072a33', class: createColorClasses$1(this.color, {
17067
+ return (hAsync(Host, { key: '97b5308021064d9e7434ef2d3d96f27045c1b0c4', class: createColorClasses$1(this.color, {
17023
17068
  [mode]: true,
17024
17069
  'has-value': hasValue,
17025
17070
  'has-focus': hasFocus,
@@ -17030,14 +17075,14 @@ class Input {
17030
17075
  'in-item': inItem,
17031
17076
  'in-item-color': hostContext('ion-item.ion-color', this.el),
17032
17077
  'input-disabled': disabled,
17033
- }) }, hAsync("label", { key: '9f8cf88d7d0e27931b51bd9c67f048c7fc6f5703', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), hAsync("div", { key: '7ad30bf9777774062a6ccf9a3ba804f251eef1bb', class: "native-wrapper", onClick: this.onLabelClick }, hAsync("slot", { key: '8af0b0325d101df8eed7d24f2767d6ca4d307319', name: "start" }), hAsync("input", Object.assign({ key: '1c53f7f9fa2567f3df19681cf4e7c21be382eae6', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (hAsync("button", { key: 'b081d0e1ec1444b4c9cca145fc9cd2ad4a68b3da', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
17078
+ }) }, hAsync("label", { key: '353f68726ce180299bd9adc81e5ff7d26a48f54f', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), hAsync("div", { key: '2034b4bad04fc157f3298a1805819216b6f439d0', class: "native-wrapper", onClick: this.onLabelClick }, hAsync("slot", { key: '96bb5e30176b2bd76dfb75bfbf6c1c3d4403f4bb', name: "start" }), hAsync("input", Object.assign({ key: '1a1d75b0e414a95c89d5a760757c33548d234aca', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (hAsync("button", { key: '95f3df17b7691d9a2e7dcd4a51f16a94aa3ca36f', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
17034
17079
  /**
17035
17080
  * This prevents mobile browsers from
17036
17081
  * blurring the input when the clear
17037
17082
  * button is activated.
17038
17083
  */
17039
17084
  ev.preventDefault();
17040
- }, onClick: this.clearTextInput }, hAsync("ion-icon", { key: '01535299241c3635460c05646420acf62a1ff567', "aria-hidden": "true", icon: clearIconData }))), hAsync("slot", { key: '480f3eb58b08ae792866a5b9b4c068748c5567cc', name: "end" })), shouldRenderHighlight && hAsync("div", { key: 'a8609cacee88e4a09f1cca65b6a47cb79a56f35e', class: "input-highlight" })), this.renderBottomContent()));
17085
+ }, onClick: this.clearTextInput }, hAsync("ion-icon", { key: '16b0af75eed50c8115fb5597f73b5fbf71c2530e', "aria-hidden": "true", icon: clearIconData }))), hAsync("slot", { key: 'c48da0f8ddb3764ac43efa705bb4a6bb2d9cc2fd', name: "end" })), shouldRenderHighlight && hAsync("div", { key: 'f15238481fc20de56ca7ecb6e350b3c024cc755e', class: "input-highlight" })), this.renderBottomContent()));
17041
17086
  }
17042
17087
  get el() { return getElement(this); }
17043
17088
  static get watchers() { return {
@@ -33129,6 +33174,10 @@ class Select {
33129
33174
  * is applied in both cases.
33130
33175
  */
33131
33176
  this.hasFocus = false;
33177
+ /**
33178
+ * Track validation state for proper aria-live announcements.
33179
+ */
33180
+ this.isInvalid = false;
33132
33181
  /**
33133
33182
  * The text to display on the cancel button.
33134
33183
  */
@@ -33251,9 +33300,12 @@ class Select {
33251
33300
  this.mutationO = watchForOptions(this.el, 'ion-select-option', async () => {
33252
33301
  this.updateOverlayOptions();
33253
33302
  });
33303
+ // Always set initial state
33304
+ this.isInvalid = checkInvalidState(this.el);
33254
33305
  }
33255
33306
  componentWillLoad() {
33256
33307
  this.inheritedAttributes = inheritAttributes$1(this.el, ['aria-label']);
33308
+ this.hintTextID = this.getHintTextID();
33257
33309
  }
33258
33310
  componentDidLoad() {
33259
33311
  /**
@@ -33277,6 +33329,11 @@ class Select {
33277
33329
  this.notchController.destroy();
33278
33330
  this.notchController = undefined;
33279
33331
  }
33332
+ // Clean up validation observer to prevent memory leaks.
33333
+ if (this.validationObserver) {
33334
+ this.validationObserver.disconnect();
33335
+ this.validationObserver = undefined;
33336
+ }
33280
33337
  }
33281
33338
  /**
33282
33339
  * Open the select overlay. The overlay is either an alert, action sheet, or popover,
@@ -33747,11 +33804,11 @@ class Select {
33747
33804
  }
33748
33805
  renderListbox() {
33749
33806
  const { disabled, inputId, isExpanded, required } = this;
33750
- return (hAsync("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "dialog", "aria-expanded": `${isExpanded}`, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId, "aria-required": `${required}`, onFocus: this.onFocus, onBlur: this.onBlur, ref: (focusEl) => (this.focusEl = focusEl) }));
33807
+ return (hAsync("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "dialog", "aria-expanded": `${isExpanded}`, "aria-describedby": this.hintTextID, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-required": `${required}`, onFocus: this.onFocus, onBlur: this.onBlur, ref: (focusEl) => (this.focusEl = focusEl) }));
33751
33808
  }
33752
33809
  getHintTextID() {
33753
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
33754
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
33810
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
33811
+ if (isInvalid && errorText) {
33755
33812
  return errorTextId;
33756
33813
  }
33757
33814
  if (helperText) {
@@ -33763,10 +33820,10 @@ class Select {
33763
33820
  * Renders the helper text or error text values
33764
33821
  */
33765
33822
  renderHintText() {
33766
- const { helperText, errorText, helperTextId, errorTextId } = this;
33823
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
33767
33824
  return [
33768
- hAsync("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText),
33769
- hAsync("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText),
33825
+ hAsync("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null),
33826
+ hAsync("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null),
33770
33827
  ];
33771
33828
  }
33772
33829
  /**
@@ -33814,7 +33871,7 @@ class Select {
33814
33871
  * TODO(FW-5592): Remove hasStartEndSlots condition
33815
33872
  */
33816
33873
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
33817
- return (hAsync(Host, { key: 'c03fb65e8fc9f9aab295e07b282377d57d910519', onClick: this.onClick, class: createColorClasses$1(this.color, {
33874
+ return (hAsync(Host, { key: '35b5e18e6f79a802ff2d46d1242e80ff755cc0b9', onClick: this.onClick, class: createColorClasses$1(this.color, {
33818
33875
  [mode]: true,
33819
33876
  'in-item': inItem,
33820
33877
  'in-item-color': hostContext('ion-item.ion-color', el),
@@ -33832,7 +33889,7 @@ class Select {
33832
33889
  [`select-justify-${justify}`]: justifyEnabled,
33833
33890
  [`select-shape-${shape}`]: shape !== undefined,
33834
33891
  [`select-label-placement-${labelPlacement}`]: true,
33835
- }) }, hAsync("label", { key: '0d0c8ec55269adcac625f2899a547f4e7f3e3741', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick }, this.renderLabelContainer(), hAsync("div", { key: 'f6dfc93c0e23cbe75a2947abde67d842db2dad78', class: "select-wrapper-inner" }, hAsync("slot", { key: '957bfadf9f101f519091419a362d3abdc2be66f6', name: "start" }), hAsync("div", { key: 'ca349202a484e7f2e884533fd330f0b136754f7d', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), hAsync("slot", { key: 'f0e62a6533ff1c8f62bd2d27f60b23385c4fa9ed', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && hAsync("div", { key: 'fb840d46bafafb09898ebeebbe8c181906a3d8a2', class: "select-highlight" })), this.renderBottomContent()));
33892
+ }) }, hAsync("label", { key: '6005b34a0c50bc4d7653a4276bc232ecd02e083c', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick }, this.renderLabelContainer(), hAsync("div", { key: 'c7e07aa81ae856c057f16275dd058f37c5670a47', class: "select-wrapper-inner" }, hAsync("slot", { key: '7fc2deefe0424404caacdbbd9e08ed43ba55d28a', name: "start" }), hAsync("div", { key: '157d74ee717b1bc30b5f1c233a09b0c8456aa68e', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), hAsync("slot", { key: 'ea66db304528b82bf9317730b6dce3db2612f235', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && hAsync("div", { key: '786eb1530b7476f0615d4e7c0bf4e7e4dc66509c', class: "select-highlight" })), this.renderBottomContent()));
33836
33893
  }
33837
33894
  get el() { return getElement(this); }
33838
33895
  static get watchers() { return {
@@ -33873,6 +33930,8 @@ class Select {
33873
33930
  "required": [4],
33874
33931
  "isExpanded": [32],
33875
33932
  "hasFocus": [32],
33933
+ "isInvalid": [32],
33934
+ "hintTextID": [32],
33876
33935
  "open": [64]
33877
33936
  },
33878
33937
  "$listeners$": undefined,
@@ -35060,20 +35119,12 @@ class Textarea {
35060
35119
  this.el.click();
35061
35120
  }
35062
35121
  }
35063
- /**
35064
- * Checks if the textarea is in an invalid state based on Ionic validation classes
35065
- */
35066
- checkValidationState() {
35067
- const hasIonTouched = this.el.classList.contains('ion-touched');
35068
- const hasIonInvalid = this.el.classList.contains('ion-invalid');
35069
- return hasIonTouched && hasIonInvalid;
35070
- }
35071
35122
  connectedCallback() {
35072
35123
  const { el } = this;
35073
35124
  this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate());
35074
35125
  this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
35075
35126
  // Always set initial state
35076
- this.isInvalid = this.checkValidationState();
35127
+ this.isInvalid = checkInvalidState(this.el);
35077
35128
  this.debounceChanged();
35078
35129
  }
35079
35130
  disconnectedCallback() {
@@ -35327,7 +35378,7 @@ class Textarea {
35327
35378
  * TODO(FW-5592): Remove hasStartEndSlots condition
35328
35379
  */
35329
35380
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
35330
- return (hAsync(Host, { key: '26b46666a92b3f652775bb1c46661f9a30392104', class: createColorClasses$1(this.color, {
35381
+ return (hAsync(Host, { key: 'a70a62d7aae3831a50acd74f60b930925ada1326', class: createColorClasses$1(this.color, {
35331
35382
  [mode]: true,
35332
35383
  'has-value': hasValue,
35333
35384
  'has-focus': hasFocus,
@@ -35336,7 +35387,7 @@ class Textarea {
35336
35387
  [`textarea-shape-${shape}`]: shape !== undefined,
35337
35388
  [`textarea-label-placement-${labelPlacement}`]: true,
35338
35389
  'textarea-disabled': disabled,
35339
- }) }, hAsync("label", { key: '2649da816216959ebe1f34cafd9dedbac20ec3c2', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), hAsync("div", { key: 'dca98593efece1b044dbcda045fa70882d715cb2', class: "textarea-wrapper-inner" }, hAsync("div", { key: '2019daf87fddca5ec0b2e336f0376fd9642bae1b', class: "start-slot-wrapper" }, hAsync("slot", { key: '36c423c394a71d08261705b9d6729e756bf65924', name: "start" })), hAsync("div", { key: '0c3ea34105c7eddfa4094371c5d288c50ed10db3', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, hAsync("textarea", Object.assign({ key: 'ce173b83b16aff43d293fa1edef9b66c6676227b', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes), value)), hAsync("div", { key: '756e343cfd208bb5ad9ecf08d77cbb0a9606dc7b', class: "end-slot-wrapper" }, hAsync("slot", { key: '0eb596814a037fa4634ff8c5bac0045540edfe21', name: "end" }))), shouldRenderHighlight && hAsync("div", { key: 'df62f896eb6e0e2d1217aa487c198eb82a52bcb8', class: "textarea-highlight" })), this.renderBottomContent()));
35390
+ }) }, hAsync("label", { key: '8a2dd59a60f7469df84018eb0ede3a9ec3862703', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), hAsync("div", { key: '1bfc368236e3da7a225a45118c27fbfc1fe5fa46', class: "textarea-wrapper-inner" }, hAsync("div", { key: '215cbb2635ff52e31a8973376989b85e7245d40f', class: "start-slot-wrapper" }, hAsync("slot", { key: '9f6b461cdee9d629deb695d2bea054ece2f32305', name: "start" })), hAsync("div", { key: 'c1af35a2d5bc452bebe0b22a26d15ff52b4e9fc8', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, hAsync("textarea", Object.assign({ key: '69a69b3cf0932baafbe37e6e846f1a571608d3f2', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes), value)), hAsync("div", { key: 'c053ea8b865d0e29763aed2e4939cc9c9e374c15', class: "end-slot-wrapper" }, hAsync("slot", { key: '930aa641833b0df54b9ea10368fc2f46d5f491f6', name: "end" }))), shouldRenderHighlight && hAsync("div", { key: '8d12597d15f5f429d80e8272ea99e64ed924e482', class: "textarea-highlight" })), this.renderBottomContent()));
35340
35391
  }
35341
35392
  get el() { return getElement(this); }
35342
35393
  static get watchers() { return {