@ionic/core 8.7.4-dev.11755800369.16c1f61b → 8.7.4-nightly.20250821

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.
@@ -43,10 +43,6 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
43
43
  * is applied in both cases.
44
44
  */
45
45
  this.hasFocus = false;
46
- /**
47
- * Track validation state for proper aria-live announcements
48
- */
49
- this.isInvalid = false;
50
46
  /**
51
47
  * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
52
48
  * Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
@@ -132,19 +128,6 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
132
128
  }
133
129
  this.didInputClearOnEdit = false;
134
130
  this.ionBlur.emit(ev);
135
- /**
136
- * Check validation state after blur to handle framework-managed classes.
137
- * Frameworks like Angular update classes asynchronously, often using
138
- * requestAnimationFrame or promises. Using setTimeout ensures we check
139
- * after all microtasks and animation frames have completed.
140
- */
141
- setTimeout(() => {
142
- const newIsInvalid = this.checkValidationState();
143
- if (this.isInvalid !== newIsInvalid) {
144
- this.isInvalid = newIsInvalid;
145
- forceUpdate(this);
146
- }
147
- }, 100);
148
131
  };
149
132
  this.onFocus = (ev) => {
150
133
  this.hasFocus = true;
@@ -244,33 +227,10 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
244
227
  componentWillLoad() {
245
228
  this.inheritedAttributes = Object.assign(Object.assign({}, inheritAriaAttributes(this.el)), inheritAttributes(this.el, ['tabindex', 'title', 'data-form-type', 'dir']));
246
229
  }
247
- /**
248
- * Checks if the input is in an invalid state based on validation classes
249
- */
250
- checkValidationState() {
251
- return this.el.classList.contains('ion-touched') && this.el.classList.contains('ion-invalid');
252
- }
253
230
  connectedCallback() {
254
231
  const { el } = this;
255
232
  this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
256
233
  this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
257
- // Watch for class changes to update validation state
258
- if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
259
- this.validationObserver = new MutationObserver(() => {
260
- const newIsInvalid = this.checkValidationState();
261
- if (this.isInvalid !== newIsInvalid) {
262
- this.isInvalid = newIsInvalid;
263
- // Force a re-render to update aria-describedby immediately
264
- forceUpdate(this);
265
- }
266
- });
267
- this.validationObserver.observe(el, {
268
- attributes: true,
269
- attributeFilter: ['class'],
270
- });
271
- }
272
- // Always set initial state
273
- this.isInvalid = this.checkValidationState();
274
234
  this.debounceChanged();
275
235
  if (Build.isBrowser) {
276
236
  document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
@@ -307,11 +267,6 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
307
267
  this.notchController.destroy();
308
268
  this.notchController = undefined;
309
269
  }
310
- // Clean up validation observer to prevent memory leaks
311
- if (this.validationObserver) {
312
- this.validationObserver.disconnect();
313
- this.validationObserver = undefined;
314
- }
315
270
  }
316
271
  /**
317
272
  * Sets focus on the native `input` in `ion-input`. Use this method instead of the global
@@ -420,8 +375,8 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
420
375
  ];
421
376
  }
422
377
  getHintTextID() {
423
- const { isInvalid, helperText, errorText, helperTextId, errorTextId } = this;
424
- if (isInvalid && errorText) {
378
+ const { el, helperText, errorText, helperTextId, errorTextId } = this;
379
+ if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
425
380
  return errorTextId;
426
381
  }
427
382
  if (helperText) {
@@ -534,7 +489,7 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
534
489
  * TODO(FW-5592): Remove hasStartEndSlots condition
535
490
  */
536
491
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
537
- return (h(Host, { key: 'af93f93b33492571bd61d6b67414f16821132138', class: createColorClasses(this.color, {
492
+ return (h(Host, { key: '41b2526627e7d2773a80f011b123284203a71ca0', class: createColorClasses(this.color, {
538
493
  [mode]: true,
539
494
  'has-value': hasValue,
540
495
  'has-focus': hasFocus,
@@ -545,14 +500,14 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
545
500
  'in-item': inItem,
546
501
  'in-item-color': hostContext('ion-item.ion-color', this.el),
547
502
  'input-disabled': disabled,
548
- }) }, h("label", { key: '3d52da6c568fc5d60833e759ba78981f95ad78d5', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '11adb0df91d332a5e1d5c86af88ffbe18ad185a3', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: 'dee0e60628bc5f849c10bae0fed4a4089769266b', name: "start" }), h("input", Object.assign({ key: 'c72e0da90c882a0233b2e27249d5b326eb89f820', 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 && (h("button", { key: '7aec0dff5eff8dccbcb735301a0ac02f9d58b4f9', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
503
+ }) }, h("label", { key: '9ab078363e32528102b441ad1791d83f86fdcbdc', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'e34b594980ec62e4c618e827fadf7669a39ad0d8', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: '12dc04ead5502e9e5736240e918bf9331bf7b5d9', name: "start" }), h("input", Object.assign({ key: 'df356eb4ced23109b2c0242f36dc043aba8782d6', 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.getHintTextID() === this.errorTextId }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: 'f79f68cabcd4ea99419331174a377827db0c0741', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
549
504
  /**
550
505
  * This prevents mobile browsers from
551
506
  * blurring the input when the clear
552
507
  * button is activated.
553
508
  */
554
509
  ev.preventDefault();
555
- }, onClick: this.clearTextInput }, h("ion-icon", { key: '79e0789ae92590c4cea9923e4370710ba31a4c38', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '3d8d737c32ad1755954aadf14ce956f400873203', name: "end" })), shouldRenderHighlight && h("div", { key: '6d214bf9b8e4058116fd154224e93bbf33c6cb58', class: "input-highlight" })), this.renderBottomContent()));
510
+ }, onClick: this.clearTextInput }, h("ion-icon", { key: '237ec07ec2e10f08818a332bb596578c2c49f770', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '1f0a3624aa3e8dc3c307a6762230ab698768a5e5', name: "end" })), shouldRenderHighlight && h("div", { key: '8a8cbb82695a722a0010b53dd0b1f1f97534a20b', class: "input-highlight" })), this.renderBottomContent()));
556
511
  }
557
512
  get el() { return this; }
558
513
  static get watchers() { return {
@@ -601,7 +556,6 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
601
556
  "type": [1],
602
557
  "value": [1032],
603
558
  "hasFocus": [32],
604
- "isInvalid": [32],
605
559
  "setFocus": [64],
606
560
  "getInputElement": [64]
607
561
  }, [[2, "click", "onClickCapture"]], {
@@ -40,10 +40,6 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
40
40
  * is applied in both cases.
41
41
  */
42
42
  this.hasFocus = false;
43
- /**
44
- * Track validation state for proper aria-live announcements
45
- */
46
- this.isInvalid = false;
47
43
  /**
48
44
  * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
49
45
  * Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
@@ -133,19 +129,6 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
133
129
  }
134
130
  this.didTextareaClearOnEdit = false;
135
131
  this.ionBlur.emit(ev);
136
- /**
137
- * Check validation state after blur to handle framework-managed classes.
138
- * Frameworks like Angular update classes asynchronously, often using
139
- * requestAnimationFrame or promises. Using setTimeout ensures we check
140
- * after all microtasks and animation frames have completed.
141
- */
142
- setTimeout(() => {
143
- const newIsInvalid = this.checkValidationState();
144
- if (this.isInvalid !== newIsInvalid) {
145
- this.isInvalid = newIsInvalid;
146
- forceUpdate(this);
147
- }
148
- }, 100);
149
132
  };
150
133
  this.onKeyDown = (ev) => {
151
134
  this.checkClearOnEdit(ev);
@@ -203,33 +186,10 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
203
186
  this.el.click();
204
187
  }
205
188
  }
206
- /**
207
- * Checks if the textarea is in an invalid state based on validation classes
208
- */
209
- checkValidationState() {
210
- return this.el.classList.contains('ion-touched') && this.el.classList.contains('ion-invalid');
211
- }
212
189
  connectedCallback() {
213
190
  const { el } = this;
214
191
  this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
215
192
  this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
216
- // Watch for class changes to update validation state
217
- if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
218
- this.validationObserver = new MutationObserver(() => {
219
- const newIsInvalid = this.checkValidationState();
220
- if (this.isInvalid !== newIsInvalid) {
221
- this.isInvalid = newIsInvalid;
222
- // Force a re-render to update aria-describedby immediately
223
- forceUpdate(this);
224
- }
225
- });
226
- this.validationObserver.observe(el, {
227
- attributes: true,
228
- attributeFilter: ['class'],
229
- });
230
- }
231
- // Always set initial state
232
- this.isInvalid = this.checkValidationState();
233
193
  this.debounceChanged();
234
194
  if (Build.isBrowser) {
235
195
  document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
@@ -251,11 +211,6 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
251
211
  this.notchController.destroy();
252
212
  this.notchController = undefined;
253
213
  }
254
- // Clean up validation observer to prevent memory leaks
255
- if (this.validationObserver) {
256
- this.validationObserver.disconnect();
257
- this.validationObserver = undefined;
258
- }
259
214
  }
260
215
  componentWillLoad() {
261
216
  this.inheritedAttributes = Object.assign(Object.assign({}, inheritAriaAttributes(this.el)), inheritAttributes(this.el, ['data-form-type', 'title', 'tabindex', 'dir']));
@@ -433,8 +388,8 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
433
388
  ];
434
389
  }
435
390
  getHintTextID() {
436
- const { isInvalid, helperText, errorText, helperTextId, errorTextId } = this;
437
- if (isInvalid && errorText) {
391
+ const { el, helperText, errorText, helperTextId, errorTextId } = this;
392
+ if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
438
393
  return errorTextId;
439
394
  }
440
395
  if (helperText) {
@@ -493,7 +448,7 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
493
448
  * TODO(FW-5592): Remove hasStartEndSlots condition
494
449
  */
495
450
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
496
- return (h(Host, { key: '075cd9dad6f4f026e421b63e4565e092b3ea0a2a', class: createColorClasses(this.color, {
451
+ return (h(Host, { key: 'd9f2ede0107987fc42c99e310cd2336bad5a5755', class: createColorClasses(this.color, {
497
452
  [mode]: true,
498
453
  'has-value': hasValue,
499
454
  'has-focus': hasFocus,
@@ -502,7 +457,7 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
502
457
  [`textarea-shape-${shape}`]: shape !== undefined,
503
458
  [`textarea-label-placement-${labelPlacement}`]: true,
504
459
  'textarea-disabled': disabled,
505
- }) }, h("label", { key: 'acb810df87a8156e5f431d65ddba287831acfa97', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '6ee9e8b9dfd562a0a23f3cc4c07c7bad4d168d56', class: "textarea-wrapper-inner" }, h("div", { key: '68d1b9205ad427a2c6de6767a7eb74901fb4d508', class: "start-slot-wrapper" }, h("slot", { key: 'a3b407c79a73cba5cafb6f987d8018573a8c5993', name: "start" })), h("div", { key: '6de732e25024cee7b1da4eb923b8fa1c3b967223', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: 'e90c595b4d0d8b1f3d2ef8cc1f9ac76ccdbc741d', 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)), h("div", { key: 'dcb70f9b4c3b3123ca05225c3396bb65762fb12c', class: "end-slot-wrapper" }, h("slot", { key: '2b8dd7c492b60424512a7f36ba75306697875da4', name: "end" }))), shouldRenderHighlight && h("div", { key: 'f76c2c046a5a2cdcd3fa9df7a9922f429fc6dd79', class: "textarea-highlight" })), this.renderBottomContent()));
460
+ }) }, h("label", { key: '9de598b95237462bb3bccffaefe83afbb43554b8', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'e33c426c6541d723ccc246bb404c03687726ff83', class: "textarea-wrapper-inner" }, h("div", { key: '521e11af9d54d281b0a2b1c25bcfc6f742c18296', class: "start-slot-wrapper" }, h("slot", { key: '515523f6ca3ce0e5dd08f3275c21a190fb1ca177', name: "start" })), h("div", { key: '916e01e00de8400ae00ef06bc1fb62d8be2eee08', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: '810271e6532d90e27dab1fcb26546113c1ce9cb0', 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.getHintTextID() === this.errorTextId }, this.inheritedAttributes), value)), h("div", { key: '80aca9ea9546dca9d38efd291a6b0be384bb6978', class: "end-slot-wrapper" }, h("slot", { key: '407fab16c66a9f4a542369bfecc0d9afa0065977', name: "end" }))), shouldRenderHighlight && h("div", { key: 'f00523a6698fac8a1996e04303487bef01d10f25', class: "textarea-highlight" })), this.renderBottomContent()));
506
461
  }
507
462
  get el() { return this; }
508
463
  static get watchers() { return {
@@ -544,7 +499,6 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
544
499
  "labelPlacement": [1, "label-placement"],
545
500
  "shape": [1],
546
501
  "hasFocus": [32],
547
- "isInvalid": [32],
548
502
  "setFocus": [64],
549
503
  "getInputElement": [64]
550
504
  }, [[2, "click", "onClickCapture"]], {
@@ -44,10 +44,6 @@ const Input = class {
44
44
  * is applied in both cases.
45
45
  */
46
46
  this.hasFocus = false;
47
- /**
48
- * Track validation state for proper aria-live announcements
49
- */
50
- this.isInvalid = false;
51
47
  /**
52
48
  * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
53
49
  * Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
@@ -133,19 +129,6 @@ const Input = class {
133
129
  }
134
130
  this.didInputClearOnEdit = false;
135
131
  this.ionBlur.emit(ev);
136
- /**
137
- * Check validation state after blur to handle framework-managed classes.
138
- * Frameworks like Angular update classes asynchronously, often using
139
- * requestAnimationFrame or promises. Using setTimeout ensures we check
140
- * after all microtasks and animation frames have completed.
141
- */
142
- setTimeout(() => {
143
- const newIsInvalid = this.checkValidationState();
144
- if (this.isInvalid !== newIsInvalid) {
145
- this.isInvalid = newIsInvalid;
146
- index.forceUpdate(this);
147
- }
148
- }, 100);
149
132
  };
150
133
  this.onFocus = (ev) => {
151
134
  this.hasFocus = true;
@@ -245,33 +228,10 @@ const Input = class {
245
228
  componentWillLoad() {
246
229
  this.inheritedAttributes = Object.assign(Object.assign({}, helpers.inheritAriaAttributes(this.el)), helpers.inheritAttributes(this.el, ['tabindex', 'title', 'data-form-type', 'dir']));
247
230
  }
248
- /**
249
- * Checks if the input is in an invalid state based on validation classes
250
- */
251
- checkValidationState() {
252
- return this.el.classList.contains('ion-touched') && this.el.classList.contains('ion-invalid');
253
- }
254
231
  connectedCallback() {
255
232
  const { el } = this;
256
233
  this.slotMutationController = input_utils.createSlotMutationController(el, ['label', 'start', 'end'], () => index.forceUpdate(this));
257
234
  this.notchController = notchController.createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
258
- // Watch for class changes to update validation state
259
- if (typeof MutationObserver !== 'undefined') {
260
- this.validationObserver = new MutationObserver(() => {
261
- const newIsInvalid = this.checkValidationState();
262
- if (this.isInvalid !== newIsInvalid) {
263
- this.isInvalid = newIsInvalid;
264
- // Force a re-render to update aria-describedby immediately
265
- index.forceUpdate(this);
266
- }
267
- });
268
- this.validationObserver.observe(el, {
269
- attributes: true,
270
- attributeFilter: ['class'],
271
- });
272
- }
273
- // Always set initial state
274
- this.isInvalid = this.checkValidationState();
275
235
  this.debounceChanged();
276
236
  {
277
237
  document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
@@ -308,11 +268,6 @@ const Input = class {
308
268
  this.notchController.destroy();
309
269
  this.notchController = undefined;
310
270
  }
311
- // Clean up validation observer to prevent memory leaks
312
- if (this.validationObserver) {
313
- this.validationObserver.disconnect();
314
- this.validationObserver = undefined;
315
- }
316
271
  }
317
272
  /**
318
273
  * Sets focus on the native `input` in `ion-input`. Use this method instead of the global
@@ -421,8 +376,8 @@ const Input = class {
421
376
  ];
422
377
  }
423
378
  getHintTextID() {
424
- const { isInvalid, helperText, errorText, helperTextId, errorTextId } = this;
425
- if (isInvalid && errorText) {
379
+ const { el, helperText, errorText, helperTextId, errorTextId } = this;
380
+ if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
426
381
  return errorTextId;
427
382
  }
428
383
  if (helperText) {
@@ -535,7 +490,7 @@ const Input = class {
535
490
  * TODO(FW-5592): Remove hasStartEndSlots condition
536
491
  */
537
492
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
538
- return (index.h(index.Host, { key: 'af93f93b33492571bd61d6b67414f16821132138', class: theme.createColorClasses(this.color, {
493
+ return (index.h(index.Host, { key: '41b2526627e7d2773a80f011b123284203a71ca0', class: theme.createColorClasses(this.color, {
539
494
  [mode]: true,
540
495
  'has-value': hasValue,
541
496
  'has-focus': hasFocus,
@@ -546,14 +501,14 @@ const Input = class {
546
501
  'in-item': inItem,
547
502
  'in-item-color': theme.hostContext('ion-item.ion-color', this.el),
548
503
  'input-disabled': disabled,
549
- }) }, index.h("label", { key: '3d52da6c568fc5d60833e759ba78981f95ad78d5', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), index.h("div", { key: '11adb0df91d332a5e1d5c86af88ffbe18ad185a3', class: "native-wrapper", onClick: this.onLabelClick }, index.h("slot", { key: 'dee0e60628bc5f849c10bae0fed4a4089769266b', name: "start" }), index.h("input", Object.assign({ key: 'c72e0da90c882a0233b2e27249d5b326eb89f820', 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 && (index.h("button", { key: '7aec0dff5eff8dccbcb735301a0ac02f9d58b4f9', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
504
+ }) }, index.h("label", { key: '9ab078363e32528102b441ad1791d83f86fdcbdc', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), index.h("div", { key: 'e34b594980ec62e4c618e827fadf7669a39ad0d8', class: "native-wrapper", onClick: this.onLabelClick }, index.h("slot", { key: '12dc04ead5502e9e5736240e918bf9331bf7b5d9', name: "start" }), index.h("input", Object.assign({ key: 'df356eb4ced23109b2c0242f36dc043aba8782d6', 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.getHintTextID() === this.errorTextId }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (index.h("button", { key: 'f79f68cabcd4ea99419331174a377827db0c0741', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
550
505
  /**
551
506
  * This prevents mobile browsers from
552
507
  * blurring the input when the clear
553
508
  * button is activated.
554
509
  */
555
510
  ev.preventDefault();
556
- }, onClick: this.clearTextInput }, index.h("ion-icon", { key: '79e0789ae92590c4cea9923e4370710ba31a4c38', "aria-hidden": "true", icon: clearIconData }))), index.h("slot", { key: '3d8d737c32ad1755954aadf14ce956f400873203', name: "end" })), shouldRenderHighlight && index.h("div", { key: '6d214bf9b8e4058116fd154224e93bbf33c6cb58', class: "input-highlight" })), this.renderBottomContent()));
511
+ }, onClick: this.clearTextInput }, index.h("ion-icon", { key: '237ec07ec2e10f08818a332bb596578c2c49f770', "aria-hidden": "true", icon: clearIconData }))), index.h("slot", { key: '1f0a3624aa3e8dc3c307a6762230ab698768a5e5', name: "end" })), shouldRenderHighlight && index.h("div", { key: '8a8cbb82695a722a0010b53dd0b1f1f97534a20b', class: "input-highlight" })), this.renderBottomContent()));
557
512
  }
558
513
  get el() { return index.getElement(this); }
559
514
  static get watchers() { return {
@@ -42,10 +42,6 @@ const Textarea = class {
42
42
  * is applied in both cases.
43
43
  */
44
44
  this.hasFocus = false;
45
- /**
46
- * Track validation state for proper aria-live announcements
47
- */
48
- this.isInvalid = false;
49
45
  /**
50
46
  * Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
51
47
  * Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
@@ -135,19 +131,6 @@ const Textarea = class {
135
131
  }
136
132
  this.didTextareaClearOnEdit = false;
137
133
  this.ionBlur.emit(ev);
138
- /**
139
- * Check validation state after blur to handle framework-managed classes.
140
- * Frameworks like Angular update classes asynchronously, often using
141
- * requestAnimationFrame or promises. Using setTimeout ensures we check
142
- * after all microtasks and animation frames have completed.
143
- */
144
- setTimeout(() => {
145
- const newIsInvalid = this.checkValidationState();
146
- if (this.isInvalid !== newIsInvalid) {
147
- this.isInvalid = newIsInvalid;
148
- index.forceUpdate(this);
149
- }
150
- }, 100);
151
134
  };
152
135
  this.onKeyDown = (ev) => {
153
136
  this.checkClearOnEdit(ev);
@@ -205,33 +188,10 @@ const Textarea = class {
205
188
  this.el.click();
206
189
  }
207
190
  }
208
- /**
209
- * Checks if the textarea is in an invalid state based on validation classes
210
- */
211
- checkValidationState() {
212
- return this.el.classList.contains('ion-touched') && this.el.classList.contains('ion-invalid');
213
- }
214
191
  connectedCallback() {
215
192
  const { el } = this;
216
193
  this.slotMutationController = input_utils.createSlotMutationController(el, ['label', 'start', 'end'], () => index.forceUpdate(this));
217
194
  this.notchController = notchController.createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
218
- // Watch for class changes to update validation state
219
- if (typeof MutationObserver !== 'undefined') {
220
- this.validationObserver = new MutationObserver(() => {
221
- const newIsInvalid = this.checkValidationState();
222
- if (this.isInvalid !== newIsInvalid) {
223
- this.isInvalid = newIsInvalid;
224
- // Force a re-render to update aria-describedby immediately
225
- index.forceUpdate(this);
226
- }
227
- });
228
- this.validationObserver.observe(el, {
229
- attributes: true,
230
- attributeFilter: ['class'],
231
- });
232
- }
233
- // Always set initial state
234
- this.isInvalid = this.checkValidationState();
235
195
  this.debounceChanged();
236
196
  {
237
197
  document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
@@ -253,11 +213,6 @@ const Textarea = class {
253
213
  this.notchController.destroy();
254
214
  this.notchController = undefined;
255
215
  }
256
- // Clean up validation observer to prevent memory leaks
257
- if (this.validationObserver) {
258
- this.validationObserver.disconnect();
259
- this.validationObserver = undefined;
260
- }
261
216
  }
262
217
  componentWillLoad() {
263
218
  this.inheritedAttributes = Object.assign(Object.assign({}, helpers.inheritAriaAttributes(this.el)), helpers.inheritAttributes(this.el, ['data-form-type', 'title', 'tabindex', 'dir']));
@@ -435,8 +390,8 @@ const Textarea = class {
435
390
  ];
436
391
  }
437
392
  getHintTextID() {
438
- const { isInvalid, helperText, errorText, helperTextId, errorTextId } = this;
439
- if (isInvalid && errorText) {
393
+ const { el, helperText, errorText, helperTextId, errorTextId } = this;
394
+ if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
440
395
  return errorTextId;
441
396
  }
442
397
  if (helperText) {
@@ -495,7 +450,7 @@ const Textarea = class {
495
450
  * TODO(FW-5592): Remove hasStartEndSlots condition
496
451
  */
497
452
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
498
- return (index.h(index.Host, { key: '075cd9dad6f4f026e421b63e4565e092b3ea0a2a', class: theme.createColorClasses(this.color, {
453
+ return (index.h(index.Host, { key: 'd9f2ede0107987fc42c99e310cd2336bad5a5755', class: theme.createColorClasses(this.color, {
499
454
  [mode]: true,
500
455
  'has-value': hasValue,
501
456
  'has-focus': hasFocus,
@@ -504,7 +459,7 @@ const Textarea = class {
504
459
  [`textarea-shape-${shape}`]: shape !== undefined,
505
460
  [`textarea-label-placement-${labelPlacement}`]: true,
506
461
  'textarea-disabled': disabled,
507
- }) }, index.h("label", { key: 'acb810df87a8156e5f431d65ddba287831acfa97', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), index.h("div", { key: '6ee9e8b9dfd562a0a23f3cc4c07c7bad4d168d56', class: "textarea-wrapper-inner" }, index.h("div", { key: '68d1b9205ad427a2c6de6767a7eb74901fb4d508', class: "start-slot-wrapper" }, index.h("slot", { key: 'a3b407c79a73cba5cafb6f987d8018573a8c5993', name: "start" })), index.h("div", { key: '6de732e25024cee7b1da4eb923b8fa1c3b967223', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, index.h("textarea", Object.assign({ key: 'e90c595b4d0d8b1f3d2ef8cc1f9ac76ccdbc741d', 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)), index.h("div", { key: 'dcb70f9b4c3b3123ca05225c3396bb65762fb12c', class: "end-slot-wrapper" }, index.h("slot", { key: '2b8dd7c492b60424512a7f36ba75306697875da4', name: "end" }))), shouldRenderHighlight && index.h("div", { key: 'f76c2c046a5a2cdcd3fa9df7a9922f429fc6dd79', class: "textarea-highlight" })), this.renderBottomContent()));
462
+ }) }, index.h("label", { key: '9de598b95237462bb3bccffaefe83afbb43554b8', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), index.h("div", { key: 'e33c426c6541d723ccc246bb404c03687726ff83', class: "textarea-wrapper-inner" }, index.h("div", { key: '521e11af9d54d281b0a2b1c25bcfc6f742c18296', class: "start-slot-wrapper" }, index.h("slot", { key: '515523f6ca3ce0e5dd08f3275c21a190fb1ca177', name: "start" })), index.h("div", { key: '916e01e00de8400ae00ef06bc1fb62d8be2eee08', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, index.h("textarea", Object.assign({ key: '810271e6532d90e27dab1fcb26546113c1ce9cb0', 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.getHintTextID() === this.errorTextId }, this.inheritedAttributes), value)), index.h("div", { key: '80aca9ea9546dca9d38efd291a6b0be384bb6978', class: "end-slot-wrapper" }, index.h("slot", { key: '407fab16c66a9f4a542369bfecc0d9afa0065977', name: "end" }))), shouldRenderHighlight && index.h("div", { key: 'f00523a6698fac8a1996e04303487bef01d10f25', class: "textarea-highlight" })), this.renderBottomContent()));
508
463
  }
509
464
  get el() { return index.getElement(this); }
510
465
  static get watchers() { return {