@ionic/core 8.7.4-dev.11755800369.16c1f61b → 8.7.4-dev.11755809082.11a7702b

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.
@@ -248,7 +248,16 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
248
248
  * Checks if the input is in an invalid state based on validation classes
249
249
  */
250
250
  checkValidationState() {
251
- return this.el.classList.contains('ion-touched') && this.el.classList.contains('ion-invalid');
251
+ // Check for both Ionic and Angular validation classes on the element itself
252
+ // Angular applies ng-touched/ng-invalid directly to the host element with ngModel
253
+ const hasIonTouched = this.el.classList.contains('ion-touched');
254
+ const hasIonInvalid = this.el.classList.contains('ion-invalid');
255
+ const hasNgTouched = this.el.classList.contains('ng-touched');
256
+ const hasNgInvalid = this.el.classList.contains('ng-invalid');
257
+ // Return true if we have both touched and invalid states from either framework
258
+ const isTouched = hasIonTouched || hasNgTouched;
259
+ const isInvalid = hasIonInvalid || hasNgInvalid;
260
+ return isTouched && isInvalid;
252
261
  }
253
262
  connectedCallback() {
254
263
  const { el } = this;
@@ -413,10 +422,10 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
413
422
  * Renders the helper text or error text values
414
423
  */
415
424
  renderHintText() {
416
- const { helperText, errorText, helperTextId, errorTextId } = this;
425
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
417
426
  return [
418
- h("div", { id: helperTextId, class: "helper-text" }, helperText),
419
- h("div", { id: errorTextId, class: "error-text" }, errorText),
427
+ helperText && (h("div", { id: helperTextId, class: "helper-text", "aria-live": "polite" }, helperText)),
428
+ errorText && (h("div", { id: errorTextId, class: "error-text", "aria-live": "assertive", "aria-atomic": "true", role: "alert", style: { display: isInvalid ? 'block' : 'none' } }, errorText)),
420
429
  ];
421
430
  }
422
431
  getHintTextID() {
@@ -534,7 +543,7 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
534
543
  * TODO(FW-5592): Remove hasStartEndSlots condition
535
544
  */
536
545
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
537
- return (h(Host, { key: 'af93f93b33492571bd61d6b67414f16821132138', class: createColorClasses(this.color, {
546
+ return (h(Host, { key: '1a141906096fc637d7de923edf6a9ea3a0168f5f', class: createColorClasses(this.color, {
538
547
  [mode]: true,
539
548
  'has-value': hasValue,
540
549
  'has-focus': hasFocus,
@@ -545,14 +554,14 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
545
554
  'in-item': inItem,
546
555
  'in-item-color': hostContext('ion-item.ion-color', this.el),
547
556
  '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) => {
557
+ }) }, h("label", { key: '8371432bc0fd37099d3efc50705530965f27b9e1', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '3d113da062bddf47b1bc67e67162c11fe4ee7f10', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: '3fe52ada4b91ebd21b21b1445b9d4009d47f8386', name: "start" }), h("input", Object.assign({ key: 'beda905e9d44797690149407ba814381ffd51d1d', 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: 'bc8579a0acb727b348bcf277f296b2dac9cb583d', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
549
558
  /**
550
559
  * This prevents mobile browsers from
551
560
  * blurring the input when the clear
552
561
  * button is activated.
553
562
  */
554
563
  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()));
564
+ }, onClick: this.clearTextInput }, h("ion-icon", { key: '41a64ec6fce010eb225637998d93abd859a62be7', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '9a7e6eba584748fc037aa1694d7e6e399de8b20b', name: "end" })), shouldRenderHighlight && h("div", { key: 'b828e6dc43b50f46727dda196de7ad2fecc3ef30', class: "input-highlight" })), this.renderBottomContent()));
556
565
  }
557
566
  get el() { return this; }
558
567
  static get watchers() { return {
@@ -207,7 +207,16 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
207
207
  * Checks if the textarea is in an invalid state based on validation classes
208
208
  */
209
209
  checkValidationState() {
210
- return this.el.classList.contains('ion-touched') && this.el.classList.contains('ion-invalid');
210
+ // Check for both Ionic and Angular validation classes on the element itself
211
+ // Angular applies ng-touched/ng-invalid directly to the host element with ngModel
212
+ const hasIonTouched = this.el.classList.contains('ion-touched');
213
+ const hasIonInvalid = this.el.classList.contains('ion-invalid');
214
+ const hasNgTouched = this.el.classList.contains('ng-touched');
215
+ const hasNgInvalid = this.el.classList.contains('ng-invalid');
216
+ // Return true if we have both touched and invalid states from either framework
217
+ const isTouched = hasIonTouched || hasNgTouched;
218
+ const isInvalid = hasIonInvalid || hasNgInvalid;
219
+ return isTouched && isInvalid;
211
220
  }
212
221
  connectedCallback() {
213
222
  const { el } = this;
@@ -426,10 +435,10 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
426
435
  * Renders the helper text or error text values
427
436
  */
428
437
  renderHintText() {
429
- const { helperText, errorText, helperTextId, errorTextId } = this;
438
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
430
439
  return [
431
- h("div", { id: helperTextId, class: "helper-text" }, helperText),
432
- h("div", { id: errorTextId, class: "error-text" }, errorText),
440
+ helperText && (h("div", { id: helperTextId, class: "helper-text", "aria-live": "polite" }, helperText)),
441
+ errorText && (h("div", { id: errorTextId, class: "error-text", "aria-live": "assertive", "aria-atomic": "true", role: "alert", style: { display: isInvalid ? 'block' : 'none' } }, errorText)),
433
442
  ];
434
443
  }
435
444
  getHintTextID() {
@@ -493,7 +502,7 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
493
502
  * TODO(FW-5592): Remove hasStartEndSlots condition
494
503
  */
495
504
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
496
- return (h(Host, { key: '075cd9dad6f4f026e421b63e4565e092b3ea0a2a', class: createColorClasses(this.color, {
505
+ return (h(Host, { key: '803c7648de15b8569c3df01692548018cc660510', class: createColorClasses(this.color, {
497
506
  [mode]: true,
498
507
  'has-value': hasValue,
499
508
  'has-focus': hasFocus,
@@ -502,7 +511,7 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
502
511
  [`textarea-shape-${shape}`]: shape !== undefined,
503
512
  [`textarea-label-placement-${labelPlacement}`]: true,
504
513
  '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()));
514
+ }) }, h("label", { key: '0e3a4e5fc809437abc6780eb7f99a0c770bb9f94', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'ff8d71e2d53daf4999338232d3b0a597627a3276', class: "textarea-wrapper-inner" }, h("div", { key: '313f5a8dc8b62e8ae6a0638a7e201b2594c180eb', class: "start-slot-wrapper" }, h("slot", { key: '32db088d41398be935f7060138e4d80a832fea85', name: "start" })), h("div", { key: '24b63ff40ffcae8816f24d9c8a6ec2e7848e0e0b', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: '2a4f918351f87d444d8922254eefa0941a6ca238', 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: '2c32636d2d13560de0a54507e1668eb9f4bcc2cf', class: "end-slot-wrapper" }, h("slot", { key: 'ccc3549246fd9d2e309958767a7a24e1be8545fd', name: "end" }))), shouldRenderHighlight && h("div", { key: 'f4719399ec530ce295b19817688b8d39d84b6b09', class: "textarea-highlight" })), this.renderBottomContent()));
506
515
  }
507
516
  get el() { return this; }
508
517
  static get watchers() { return {
@@ -249,7 +249,16 @@ const Input = class {
249
249
  * Checks if the input is in an invalid state based on validation classes
250
250
  */
251
251
  checkValidationState() {
252
- return this.el.classList.contains('ion-touched') && this.el.classList.contains('ion-invalid');
252
+ // Check for both Ionic and Angular validation classes on the element itself
253
+ // Angular applies ng-touched/ng-invalid directly to the host element with ngModel
254
+ const hasIonTouched = this.el.classList.contains('ion-touched');
255
+ const hasIonInvalid = this.el.classList.contains('ion-invalid');
256
+ const hasNgTouched = this.el.classList.contains('ng-touched');
257
+ const hasNgInvalid = this.el.classList.contains('ng-invalid');
258
+ // Return true if we have both touched and invalid states from either framework
259
+ const isTouched = hasIonTouched || hasNgTouched;
260
+ const isInvalid = hasIonInvalid || hasNgInvalid;
261
+ return isTouched && isInvalid;
253
262
  }
254
263
  connectedCallback() {
255
264
  const { el } = this;
@@ -414,10 +423,10 @@ const Input = class {
414
423
  * Renders the helper text or error text values
415
424
  */
416
425
  renderHintText() {
417
- const { helperText, errorText, helperTextId, errorTextId } = this;
426
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
418
427
  return [
419
- index.h("div", { id: helperTextId, class: "helper-text" }, helperText),
420
- index.h("div", { id: errorTextId, class: "error-text" }, errorText),
428
+ helperText && (index.h("div", { id: helperTextId, class: "helper-text", "aria-live": "polite" }, helperText)),
429
+ errorText && (index.h("div", { id: errorTextId, class: "error-text", "aria-live": "assertive", "aria-atomic": "true", role: "alert", style: { display: isInvalid ? 'block' : 'none' } }, errorText)),
421
430
  ];
422
431
  }
423
432
  getHintTextID() {
@@ -535,7 +544,7 @@ const Input = class {
535
544
  * TODO(FW-5592): Remove hasStartEndSlots condition
536
545
  */
537
546
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
538
- return (index.h(index.Host, { key: 'af93f93b33492571bd61d6b67414f16821132138', class: theme.createColorClasses(this.color, {
547
+ return (index.h(index.Host, { key: '1a141906096fc637d7de923edf6a9ea3a0168f5f', class: theme.createColorClasses(this.color, {
539
548
  [mode]: true,
540
549
  'has-value': hasValue,
541
550
  'has-focus': hasFocus,
@@ -546,14 +555,14 @@ const Input = class {
546
555
  'in-item': inItem,
547
556
  'in-item-color': theme.hostContext('ion-item.ion-color', this.el),
548
557
  '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) => {
558
+ }) }, index.h("label", { key: '8371432bc0fd37099d3efc50705530965f27b9e1', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), index.h("div", { key: '3d113da062bddf47b1bc67e67162c11fe4ee7f10', class: "native-wrapper", onClick: this.onLabelClick }, index.h("slot", { key: '3fe52ada4b91ebd21b21b1445b9d4009d47f8386', name: "start" }), index.h("input", Object.assign({ key: 'beda905e9d44797690149407ba814381ffd51d1d', 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: 'bc8579a0acb727b348bcf277f296b2dac9cb583d', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
550
559
  /**
551
560
  * This prevents mobile browsers from
552
561
  * blurring the input when the clear
553
562
  * button is activated.
554
563
  */
555
564
  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()));
565
+ }, onClick: this.clearTextInput }, index.h("ion-icon", { key: '41a64ec6fce010eb225637998d93abd859a62be7', "aria-hidden": "true", icon: clearIconData }))), index.h("slot", { key: '9a7e6eba584748fc037aa1694d7e6e399de8b20b', name: "end" })), shouldRenderHighlight && index.h("div", { key: 'b828e6dc43b50f46727dda196de7ad2fecc3ef30', class: "input-highlight" })), this.renderBottomContent()));
557
566
  }
558
567
  get el() { return index.getElement(this); }
559
568
  static get watchers() { return {
@@ -209,7 +209,16 @@ const Textarea = class {
209
209
  * Checks if the textarea is in an invalid state based on validation classes
210
210
  */
211
211
  checkValidationState() {
212
- return this.el.classList.contains('ion-touched') && this.el.classList.contains('ion-invalid');
212
+ // Check for both Ionic and Angular validation classes on the element itself
213
+ // Angular applies ng-touched/ng-invalid directly to the host element with ngModel
214
+ const hasIonTouched = this.el.classList.contains('ion-touched');
215
+ const hasIonInvalid = this.el.classList.contains('ion-invalid');
216
+ const hasNgTouched = this.el.classList.contains('ng-touched');
217
+ const hasNgInvalid = this.el.classList.contains('ng-invalid');
218
+ // Return true if we have both touched and invalid states from either framework
219
+ const isTouched = hasIonTouched || hasNgTouched;
220
+ const isInvalid = hasIonInvalid || hasNgInvalid;
221
+ return isTouched && isInvalid;
213
222
  }
214
223
  connectedCallback() {
215
224
  const { el } = this;
@@ -428,10 +437,10 @@ const Textarea = class {
428
437
  * Renders the helper text or error text values
429
438
  */
430
439
  renderHintText() {
431
- const { helperText, errorText, helperTextId, errorTextId } = this;
440
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
432
441
  return [
433
- index.h("div", { id: helperTextId, class: "helper-text" }, helperText),
434
- index.h("div", { id: errorTextId, class: "error-text" }, errorText),
442
+ helperText && (index.h("div", { id: helperTextId, class: "helper-text", "aria-live": "polite" }, helperText)),
443
+ errorText && (index.h("div", { id: errorTextId, class: "error-text", "aria-live": "assertive", "aria-atomic": "true", role: "alert", style: { display: isInvalid ? 'block' : 'none' } }, errorText)),
435
444
  ];
436
445
  }
437
446
  getHintTextID() {
@@ -495,7 +504,7 @@ const Textarea = class {
495
504
  * TODO(FW-5592): Remove hasStartEndSlots condition
496
505
  */
497
506
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
498
- return (index.h(index.Host, { key: '075cd9dad6f4f026e421b63e4565e092b3ea0a2a', class: theme.createColorClasses(this.color, {
507
+ return (index.h(index.Host, { key: '803c7648de15b8569c3df01692548018cc660510', class: theme.createColorClasses(this.color, {
499
508
  [mode]: true,
500
509
  'has-value': hasValue,
501
510
  'has-focus': hasFocus,
@@ -504,7 +513,7 @@ const Textarea = class {
504
513
  [`textarea-shape-${shape}`]: shape !== undefined,
505
514
  [`textarea-label-placement-${labelPlacement}`]: true,
506
515
  '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()));
516
+ }) }, index.h("label", { key: '0e3a4e5fc809437abc6780eb7f99a0c770bb9f94', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), index.h("div", { key: 'ff8d71e2d53daf4999338232d3b0a597627a3276', class: "textarea-wrapper-inner" }, index.h("div", { key: '313f5a8dc8b62e8ae6a0638a7e201b2594c180eb', class: "start-slot-wrapper" }, index.h("slot", { key: '32db088d41398be935f7060138e4d80a832fea85', name: "start" })), index.h("div", { key: '24b63ff40ffcae8816f24d9c8a6ec2e7848e0e0b', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, index.h("textarea", Object.assign({ key: '2a4f918351f87d444d8922254eefa0941a6ca238', 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: '2c32636d2d13560de0a54507e1668eb9f4bcc2cf', class: "end-slot-wrapper" }, index.h("slot", { key: 'ccc3549246fd9d2e309958767a7a24e1be8545fd', name: "end" }))), shouldRenderHighlight && index.h("div", { key: 'f4719399ec530ce295b19817688b8d39d84b6b09', class: "textarea-highlight" })), this.renderBottomContent()));
508
517
  }
509
518
  get el() { return index.getElement(this); }
510
519
  static get watchers() { return {
@@ -244,7 +244,16 @@ export class Input {
244
244
  * Checks if the input is in an invalid state based on validation classes
245
245
  */
246
246
  checkValidationState() {
247
- return this.el.classList.contains('ion-touched') && this.el.classList.contains('ion-invalid');
247
+ // Check for both Ionic and Angular validation classes on the element itself
248
+ // Angular applies ng-touched/ng-invalid directly to the host element with ngModel
249
+ const hasIonTouched = this.el.classList.contains('ion-touched');
250
+ const hasIonInvalid = this.el.classList.contains('ion-invalid');
251
+ const hasNgTouched = this.el.classList.contains('ng-touched');
252
+ const hasNgInvalid = this.el.classList.contains('ng-invalid');
253
+ // Return true if we have both touched and invalid states from either framework
254
+ const isTouched = hasIonTouched || hasNgTouched;
255
+ const isInvalid = hasIonInvalid || hasNgInvalid;
256
+ return isTouched && isInvalid;
248
257
  }
249
258
  connectedCallback() {
250
259
  const { el } = this;
@@ -409,10 +418,10 @@ export class Input {
409
418
  * Renders the helper text or error text values
410
419
  */
411
420
  renderHintText() {
412
- const { helperText, errorText, helperTextId, errorTextId } = this;
421
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
413
422
  return [
414
- h("div", { id: helperTextId, class: "helper-text" }, helperText),
415
- h("div", { id: errorTextId, class: "error-text" }, errorText),
423
+ helperText && (h("div", { id: helperTextId, class: "helper-text", "aria-live": "polite" }, helperText)),
424
+ errorText && (h("div", { id: errorTextId, class: "error-text", "aria-live": "assertive", "aria-atomic": "true", role: "alert", style: { display: isInvalid ? 'block' : 'none' } }, errorText)),
416
425
  ];
417
426
  }
418
427
  getHintTextID() {
@@ -530,7 +539,7 @@ export class Input {
530
539
  * TODO(FW-5592): Remove hasStartEndSlots condition
531
540
  */
532
541
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
533
- return (h(Host, { key: 'af93f93b33492571bd61d6b67414f16821132138', class: createColorClasses(this.color, {
542
+ return (h(Host, { key: '1a141906096fc637d7de923edf6a9ea3a0168f5f', class: createColorClasses(this.color, {
534
543
  [mode]: true,
535
544
  'has-value': hasValue,
536
545
  'has-focus': hasFocus,
@@ -541,14 +550,14 @@ export class Input {
541
550
  'in-item': inItem,
542
551
  'in-item-color': hostContext('ion-item.ion-color', this.el),
543
552
  'input-disabled': disabled,
544
- }) }, 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) => {
553
+ }) }, h("label", { key: '8371432bc0fd37099d3efc50705530965f27b9e1', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '3d113da062bddf47b1bc67e67162c11fe4ee7f10', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: '3fe52ada4b91ebd21b21b1445b9d4009d47f8386', name: "start" }), h("input", Object.assign({ key: 'beda905e9d44797690149407ba814381ffd51d1d', 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: 'bc8579a0acb727b348bcf277f296b2dac9cb583d', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
545
554
  /**
546
555
  * This prevents mobile browsers from
547
556
  * blurring the input when the clear
548
557
  * button is activated.
549
558
  */
550
559
  ev.preventDefault();
551
- }, 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()));
560
+ }, onClick: this.clearTextInput }, h("ion-icon", { key: '41a64ec6fce010eb225637998d93abd859a62be7', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '9a7e6eba584748fc037aa1694d7e6e399de8b20b', name: "end" })), shouldRenderHighlight && h("div", { key: 'b828e6dc43b50f46727dda196de7ad2fecc3ef30', class: "input-highlight" })), this.renderBottomContent()));
552
561
  }
553
562
  static get is() { return "ion-input"; }
554
563
  static get encapsulation() { return "scoped"; }
@@ -204,7 +204,16 @@ export class Textarea {
204
204
  * Checks if the textarea is in an invalid state based on validation classes
205
205
  */
206
206
  checkValidationState() {
207
- return this.el.classList.contains('ion-touched') && this.el.classList.contains('ion-invalid');
207
+ // Check for both Ionic and Angular validation classes on the element itself
208
+ // Angular applies ng-touched/ng-invalid directly to the host element with ngModel
209
+ const hasIonTouched = this.el.classList.contains('ion-touched');
210
+ const hasIonInvalid = this.el.classList.contains('ion-invalid');
211
+ const hasNgTouched = this.el.classList.contains('ng-touched');
212
+ const hasNgInvalid = this.el.classList.contains('ng-invalid');
213
+ // Return true if we have both touched and invalid states from either framework
214
+ const isTouched = hasIonTouched || hasNgTouched;
215
+ const isInvalid = hasIonInvalid || hasNgInvalid;
216
+ return isTouched && isInvalid;
208
217
  }
209
218
  connectedCallback() {
210
219
  const { el } = this;
@@ -423,10 +432,10 @@ export class Textarea {
423
432
  * Renders the helper text or error text values
424
433
  */
425
434
  renderHintText() {
426
- const { helperText, errorText, helperTextId, errorTextId } = this;
435
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
427
436
  return [
428
- h("div", { id: helperTextId, class: "helper-text" }, helperText),
429
- h("div", { id: errorTextId, class: "error-text" }, errorText),
437
+ helperText && (h("div", { id: helperTextId, class: "helper-text", "aria-live": "polite" }, helperText)),
438
+ errorText && (h("div", { id: errorTextId, class: "error-text", "aria-live": "assertive", "aria-atomic": "true", role: "alert", style: { display: isInvalid ? 'block' : 'none' } }, errorText)),
430
439
  ];
431
440
  }
432
441
  getHintTextID() {
@@ -490,7 +499,7 @@ export class Textarea {
490
499
  * TODO(FW-5592): Remove hasStartEndSlots condition
491
500
  */
492
501
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
493
- return (h(Host, { key: '075cd9dad6f4f026e421b63e4565e092b3ea0a2a', class: createColorClasses(this.color, {
502
+ return (h(Host, { key: '803c7648de15b8569c3df01692548018cc660510', class: createColorClasses(this.color, {
494
503
  [mode]: true,
495
504
  'has-value': hasValue,
496
505
  'has-focus': hasFocus,
@@ -499,7 +508,7 @@ export class Textarea {
499
508
  [`textarea-shape-${shape}`]: shape !== undefined,
500
509
  [`textarea-label-placement-${labelPlacement}`]: true,
501
510
  'textarea-disabled': disabled,
502
- }) }, 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()));
511
+ }) }, h("label", { key: '0e3a4e5fc809437abc6780eb7f99a0c770bb9f94', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'ff8d71e2d53daf4999338232d3b0a597627a3276', class: "textarea-wrapper-inner" }, h("div", { key: '313f5a8dc8b62e8ae6a0638a7e201b2594c180eb', class: "start-slot-wrapper" }, h("slot", { key: '32db088d41398be935f7060138e4d80a832fea85', name: "start" })), h("div", { key: '24b63ff40ffcae8816f24d9c8a6ec2e7848e0e0b', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: '2a4f918351f87d444d8922254eefa0941a6ca238', 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: '2c32636d2d13560de0a54507e1668eb9f4bcc2cf', class: "end-slot-wrapper" }, h("slot", { key: 'ccc3549246fd9d2e309958767a7a24e1be8545fd', name: "end" }))), shouldRenderHighlight && h("div", { key: 'f4719399ec530ce295b19817688b8d39d84b6b09', class: "textarea-highlight" })), this.renderBottomContent()));
503
512
  }
504
513
  static get is() { return "ion-textarea"; }
505
514
  static get encapsulation() { return "scoped"; }
package/dist/docs.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2025-08-21T18:21:21",
2
+ "timestamp": "2025-08-21T20:46:27",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
5
  "version": "4.36.2",
@@ -247,7 +247,16 @@ const Input = class {
247
247
  * Checks if the input is in an invalid state based on validation classes
248
248
  */
249
249
  checkValidationState() {
250
- return this.el.classList.contains('ion-touched') && this.el.classList.contains('ion-invalid');
250
+ // Check for both Ionic and Angular validation classes on the element itself
251
+ // Angular applies ng-touched/ng-invalid directly to the host element with ngModel
252
+ const hasIonTouched = this.el.classList.contains('ion-touched');
253
+ const hasIonInvalid = this.el.classList.contains('ion-invalid');
254
+ const hasNgTouched = this.el.classList.contains('ng-touched');
255
+ const hasNgInvalid = this.el.classList.contains('ng-invalid');
256
+ // Return true if we have both touched and invalid states from either framework
257
+ const isTouched = hasIonTouched || hasNgTouched;
258
+ const isInvalid = hasIonInvalid || hasNgInvalid;
259
+ return isTouched && isInvalid;
251
260
  }
252
261
  connectedCallback() {
253
262
  const { el } = this;
@@ -412,10 +421,10 @@ const Input = class {
412
421
  * Renders the helper text or error text values
413
422
  */
414
423
  renderHintText() {
415
- const { helperText, errorText, helperTextId, errorTextId } = this;
424
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
416
425
  return [
417
- h("div", { id: helperTextId, class: "helper-text" }, helperText),
418
- h("div", { id: errorTextId, class: "error-text" }, errorText),
426
+ helperText && (h("div", { id: helperTextId, class: "helper-text", "aria-live": "polite" }, helperText)),
427
+ errorText && (h("div", { id: errorTextId, class: "error-text", "aria-live": "assertive", "aria-atomic": "true", role: "alert", style: { display: isInvalid ? 'block' : 'none' } }, errorText)),
419
428
  ];
420
429
  }
421
430
  getHintTextID() {
@@ -533,7 +542,7 @@ const Input = class {
533
542
  * TODO(FW-5592): Remove hasStartEndSlots condition
534
543
  */
535
544
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
536
- return (h(Host, { key: 'af93f93b33492571bd61d6b67414f16821132138', class: createColorClasses(this.color, {
545
+ return (h(Host, { key: '1a141906096fc637d7de923edf6a9ea3a0168f5f', class: createColorClasses(this.color, {
537
546
  [mode]: true,
538
547
  'has-value': hasValue,
539
548
  'has-focus': hasFocus,
@@ -544,14 +553,14 @@ const Input = class {
544
553
  'in-item': inItem,
545
554
  'in-item-color': hostContext('ion-item.ion-color', this.el),
546
555
  'input-disabled': disabled,
547
- }) }, 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) => {
556
+ }) }, h("label", { key: '8371432bc0fd37099d3efc50705530965f27b9e1', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '3d113da062bddf47b1bc67e67162c11fe4ee7f10', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: '3fe52ada4b91ebd21b21b1445b9d4009d47f8386', name: "start" }), h("input", Object.assign({ key: 'beda905e9d44797690149407ba814381ffd51d1d', 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: 'bc8579a0acb727b348bcf277f296b2dac9cb583d', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
548
557
  /**
549
558
  * This prevents mobile browsers from
550
559
  * blurring the input when the clear
551
560
  * button is activated.
552
561
  */
553
562
  ev.preventDefault();
554
- }, 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()));
563
+ }, onClick: this.clearTextInput }, h("ion-icon", { key: '41a64ec6fce010eb225637998d93abd859a62be7', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '9a7e6eba584748fc037aa1694d7e6e399de8b20b', name: "end" })), shouldRenderHighlight && h("div", { key: 'b828e6dc43b50f46727dda196de7ad2fecc3ef30', class: "input-highlight" })), this.renderBottomContent()));
555
564
  }
556
565
  get el() { return getElement(this); }
557
566
  static get watchers() { return {
@@ -207,7 +207,16 @@ const Textarea = class {
207
207
  * Checks if the textarea is in an invalid state based on validation classes
208
208
  */
209
209
  checkValidationState() {
210
- return this.el.classList.contains('ion-touched') && this.el.classList.contains('ion-invalid');
210
+ // Check for both Ionic and Angular validation classes on the element itself
211
+ // Angular applies ng-touched/ng-invalid directly to the host element with ngModel
212
+ const hasIonTouched = this.el.classList.contains('ion-touched');
213
+ const hasIonInvalid = this.el.classList.contains('ion-invalid');
214
+ const hasNgTouched = this.el.classList.contains('ng-touched');
215
+ const hasNgInvalid = this.el.classList.contains('ng-invalid');
216
+ // Return true if we have both touched and invalid states from either framework
217
+ const isTouched = hasIonTouched || hasNgTouched;
218
+ const isInvalid = hasIonInvalid || hasNgInvalid;
219
+ return isTouched && isInvalid;
211
220
  }
212
221
  connectedCallback() {
213
222
  const { el } = this;
@@ -426,10 +435,10 @@ const Textarea = class {
426
435
  * Renders the helper text or error text values
427
436
  */
428
437
  renderHintText() {
429
- const { helperText, errorText, helperTextId, errorTextId } = this;
438
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
430
439
  return [
431
- h("div", { id: helperTextId, class: "helper-text" }, helperText),
432
- h("div", { id: errorTextId, class: "error-text" }, errorText),
440
+ helperText && (h("div", { id: helperTextId, class: "helper-text", "aria-live": "polite" }, helperText)),
441
+ errorText && (h("div", { id: errorTextId, class: "error-text", "aria-live": "assertive", "aria-atomic": "true", role: "alert", style: { display: isInvalid ? 'block' : 'none' } }, errorText)),
433
442
  ];
434
443
  }
435
444
  getHintTextID() {
@@ -493,7 +502,7 @@ const Textarea = class {
493
502
  * TODO(FW-5592): Remove hasStartEndSlots condition
494
503
  */
495
504
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
496
- return (h(Host, { key: '075cd9dad6f4f026e421b63e4565e092b3ea0a2a', class: createColorClasses(this.color, {
505
+ return (h(Host, { key: '803c7648de15b8569c3df01692548018cc660510', class: createColorClasses(this.color, {
497
506
  [mode]: true,
498
507
  'has-value': hasValue,
499
508
  'has-focus': hasFocus,
@@ -502,7 +511,7 @@ const Textarea = class {
502
511
  [`textarea-shape-${shape}`]: shape !== undefined,
503
512
  [`textarea-label-placement-${labelPlacement}`]: true,
504
513
  '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()));
514
+ }) }, h("label", { key: '0e3a4e5fc809437abc6780eb7f99a0c770bb9f94', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'ff8d71e2d53daf4999338232d3b0a597627a3276', class: "textarea-wrapper-inner" }, h("div", { key: '313f5a8dc8b62e8ae6a0638a7e201b2594c180eb', class: "start-slot-wrapper" }, h("slot", { key: '32db088d41398be935f7060138e4d80a832fea85', name: "start" })), h("div", { key: '24b63ff40ffcae8816f24d9c8a6ec2e7848e0e0b', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: '2a4f918351f87d444d8922254eefa0941a6ca238', 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: '2c32636d2d13560de0a54507e1668eb9f4bcc2cf', class: "end-slot-wrapper" }, h("slot", { key: 'ccc3549246fd9d2e309958767a7a24e1be8545fd', name: "end" }))), shouldRenderHighlight && h("div", { key: 'f4719399ec530ce295b19817688b8d39d84b6b09', class: "textarea-highlight" })), this.renderBottomContent()));
506
515
  }
507
516
  get el() { return getElement(this); }
508
517
  static get watchers() { return {