voyager-ionic-core 8.3.1 → 8.3.4

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 (121) hide show
  1. package/components/action-sheet.js +2 -2
  2. package/components/alert.js +1 -1
  3. package/components/backdrop.js +1 -1
  4. package/components/ion-app.js +1 -2
  5. package/components/ion-input.js +20 -5
  6. package/components/ion-loading.js +1 -1
  7. package/components/ion-picker-legacy.js +1 -1
  8. package/components/ion-segment.js +19 -6
  9. package/components/ion-textarea.js +19 -4
  10. package/components/overlays.js +62 -7
  11. package/dist/cjs/index.cjs.js +1 -1
  12. package/dist/cjs/ion-action-sheet.cjs.entry.js +3 -3
  13. package/dist/cjs/ion-alert.cjs.entry.js +2 -2
  14. package/dist/cjs/ion-app_8.cjs.entry.js +1 -2
  15. package/dist/cjs/ion-backdrop.cjs.entry.js +1 -1
  16. package/dist/cjs/ion-datetime_3.cjs.entry.js +2 -2
  17. package/dist/cjs/ion-input.cjs.entry.js +20 -5
  18. package/dist/cjs/ion-loading.cjs.entry.js +2 -2
  19. package/dist/cjs/ion-menu_3.cjs.entry.js +1 -1
  20. package/dist/cjs/ion-modal.cjs.entry.js +1 -1
  21. package/dist/cjs/ion-popover.cjs.entry.js +1 -1
  22. package/dist/cjs/ion-segment_2.cjs.entry.js +19 -6
  23. package/dist/cjs/ion-select_3.cjs.entry.js +1 -1
  24. package/dist/cjs/ion-textarea.cjs.entry.js +19 -4
  25. package/dist/cjs/ion-toast.cjs.entry.js +1 -1
  26. package/dist/cjs/{overlays-0123d7d4.js → overlays-ba0f6986.js} +62 -7
  27. package/dist/collection/components/action-sheet/action-sheet.js +2 -2
  28. package/dist/collection/components/alert/alert.js +1 -1
  29. package/dist/collection/components/app/app.js +3 -7
  30. package/dist/collection/components/backdrop/backdrop.js +1 -1
  31. package/dist/collection/components/input/input.js +20 -5
  32. package/dist/collection/components/loading/loading.js +1 -1
  33. package/dist/collection/components/picker-legacy/picker.js +1 -1
  34. package/dist/collection/components/segment/segment.js +19 -6
  35. package/dist/collection/components/textarea/textarea.js +19 -4
  36. package/dist/collection/utils/overlays.js +62 -7
  37. package/dist/docs.json +41 -2
  38. package/dist/esm/index.js +1 -1
  39. package/dist/esm/ion-action-sheet.entry.js +3 -3
  40. package/dist/esm/ion-alert.entry.js +2 -2
  41. package/dist/esm/ion-app_8.entry.js +1 -2
  42. package/dist/esm/ion-backdrop.entry.js +1 -1
  43. package/dist/esm/ion-datetime_3.entry.js +2 -2
  44. package/dist/esm/ion-input.entry.js +20 -5
  45. package/dist/esm/ion-loading.entry.js +2 -2
  46. package/dist/esm/ion-menu_3.entry.js +1 -1
  47. package/dist/esm/ion-modal.entry.js +1 -1
  48. package/dist/esm/ion-popover.entry.js +1 -1
  49. package/dist/esm/ion-segment_2.entry.js +19 -6
  50. package/dist/esm/ion-select_3.entry.js +1 -1
  51. package/dist/esm/ion-textarea.entry.js +19 -4
  52. package/dist/esm/ion-toast.entry.js +1 -1
  53. package/dist/esm/{overlays-9c75ec54.js → overlays-ae10d43d.js} +62 -7
  54. package/dist/esm-es5/index.js +1 -1
  55. package/dist/esm-es5/ion-action-sheet.entry.js +1 -1
  56. package/dist/esm-es5/ion-alert.entry.js +1 -1
  57. package/dist/esm-es5/ion-app_8.entry.js +1 -1
  58. package/dist/esm-es5/ion-backdrop.entry.js +1 -1
  59. package/dist/esm-es5/ion-datetime_3.entry.js +1 -1
  60. package/dist/esm-es5/ion-input.entry.js +1 -1
  61. package/dist/esm-es5/ion-loading.entry.js +1 -1
  62. package/dist/esm-es5/ion-menu_3.entry.js +1 -1
  63. package/dist/esm-es5/ion-modal.entry.js +1 -1
  64. package/dist/esm-es5/ion-popover.entry.js +1 -1
  65. package/dist/esm-es5/ion-segment_2.entry.js +1 -1
  66. package/dist/esm-es5/ion-select_3.entry.js +1 -1
  67. package/dist/esm-es5/ion-textarea.entry.js +1 -1
  68. package/dist/esm-es5/ion-toast.entry.js +1 -1
  69. package/dist/esm-es5/overlays-ae10d43d.js +4 -0
  70. package/dist/ionic/index.esm.js +1 -1
  71. package/dist/ionic/ionic.esm.js +1 -1
  72. package/dist/ionic/p-04fc24ee.system.js +4 -0
  73. package/dist/ionic/{p-ecb57d24.system.entry.js → p-0790b342.system.entry.js} +1 -1
  74. package/dist/ionic/{p-100b83fd.system.entry.js → p-110e03be.system.entry.js} +1 -1
  75. package/dist/ionic/p-148b8abb.entry.js +4 -0
  76. package/dist/ionic/{p-4b0fedb7.system.entry.js → p-18105026.system.entry.js} +1 -1
  77. package/dist/ionic/p-322c5fb4.system.js +1 -1
  78. package/dist/ionic/{p-d57661a1.entry.js → p-41c89b8d.entry.js} +1 -1
  79. package/dist/ionic/p-44d1539c.system.entry.js +4 -0
  80. package/dist/ionic/{p-772dacba.system.entry.js → p-53add985.system.entry.js} +1 -1
  81. package/dist/ionic/{p-08127172.entry.js → p-5800e441.entry.js} +1 -1
  82. package/dist/ionic/p-5c831f49.js +4 -0
  83. package/dist/ionic/p-60cc7986.entry.js +4 -0
  84. package/dist/ionic/{p-a72fb8a1.system.entry.js → p-63d65dbc.system.entry.js} +1 -1
  85. package/dist/ionic/p-6ceb04b5.entry.js +4 -0
  86. package/dist/ionic/{p-9fef1364.entry.js → p-6d50faff.entry.js} +1 -1
  87. package/dist/ionic/{p-a4d51b8d.system.js → p-79b12fda.system.js} +1 -1
  88. package/dist/ionic/{p-8ed31163.system.entry.js → p-79e7be3a.system.entry.js} +1 -1
  89. package/dist/ionic/p-82ab7ccb.entry.js +4 -0
  90. package/dist/ionic/{p-22c020db.system.entry.js → p-857ca696.system.entry.js} +1 -1
  91. package/dist/ionic/{p-9c23044d.entry.js → p-87a4407b.entry.js} +1 -1
  92. package/dist/ionic/{p-2b7c93b4.entry.js → p-9895e7f3.entry.js} +1 -1
  93. package/dist/ionic/p-9910f786.entry.js +4 -0
  94. package/dist/ionic/{p-5e66bcf2.entry.js → p-9e208825.entry.js} +1 -1
  95. package/dist/ionic/p-a89dac49.entry.js +4 -0
  96. package/dist/ionic/{p-85f73bdd.system.entry.js → p-ad9b5007.system.entry.js} +1 -1
  97. package/dist/ionic/{p-755b27f0.system.entry.js → p-be715dd3.system.entry.js} +1 -1
  98. package/dist/ionic/{p-ed6962d3.system.entry.js → p-be71fe0f.system.entry.js} +1 -1
  99. package/dist/ionic/{p-cff5585e.system.entry.js → p-c71f301f.system.entry.js} +1 -1
  100. package/dist/ionic/{p-a41699db.entry.js → p-d58f21d2.entry.js} +1 -1
  101. package/dist/ionic/{p-f50ae0d5.system.entry.js → p-d754c709.system.entry.js} +1 -1
  102. package/dist/ionic/p-dda5c73d.entry.js +4 -0
  103. package/dist/ionic/{p-9cc3bcc5.system.entry.js → p-f0ab13a8.system.entry.js} +1 -1
  104. package/dist/types/components/app/app.d.ts +0 -1
  105. package/dist/types/components/input/input.d.ts +3 -0
  106. package/dist/types/components/textarea/textarea.d.ts +3 -0
  107. package/dist/types/components.d.ts +3 -0
  108. package/hydrate/index.js +127 -30
  109. package/hydrate/index.mjs +127 -30
  110. package/package.json +1 -1
  111. package/dist/esm-es5/overlays-9c75ec54.js +0 -4
  112. package/dist/ionic/p-0fa0c92b.entry.js +0 -4
  113. package/dist/ionic/p-16207409.system.entry.js +0 -4
  114. package/dist/ionic/p-2c4bdd9d.entry.js +0 -4
  115. package/dist/ionic/p-854f9b64.entry.js +0 -4
  116. package/dist/ionic/p-98871496.system.js +0 -4
  117. package/dist/ionic/p-a440397c.js +0 -4
  118. package/dist/ionic/p-ab8a2ff1.entry.js +0 -4
  119. package/dist/ionic/p-b4c950f8.entry.js +0 -4
  120. package/dist/ionic/p-bfa2e81c.entry.js +0 -4
  121. package/dist/ionic/p-d77e12ca.entry.js +0 -4
@@ -12,7 +12,7 @@ const helpers = require('./helpers-afaa9001.js');
12
12
  const lockController = require('./lock-controller-6585a42a.js');
13
13
  const index$4 = require('./index-5915f9b3.js');
14
14
  const capacitor = require('./capacitor-c04564bf.js');
15
- const overlays = require('./overlays-0123d7d4.js');
15
+ const overlays = require('./overlays-ba0f6986.js');
16
16
  const theme = require('./theme-d1c573d2.js');
17
17
  const index$5 = require('./index-f05acd21.js');
18
18
  const ionicGlobal = require('./ionic-global-d9a8bb5b.js');
@@ -6,7 +6,7 @@
6
6
  Object.defineProperty(exports, '__esModule', { value: true });
7
7
 
8
8
  const index = require('./index-73f75efb.js');
9
- const overlays = require('./overlays-0123d7d4.js');
9
+ const overlays = require('./overlays-ba0f6986.js');
10
10
  const frameworkDelegate = require('./framework-delegate-55f5683a.js');
11
11
  const helpers = require('./helpers-afaa9001.js');
12
12
  const lockController = require('./lock-controller-6585a42a.js');
@@ -288,21 +288,34 @@ const Segment = class {
288
288
  */
289
289
  const centeredX = activeButtonLeft - scrollContainerBox.width / 2 + activeButtonBox.width / 2;
290
290
  /**
291
- * We intentionally use scrollBy here instead of scrollIntoView
291
+ * newScrollPosition is the absolute scroll position that the
292
+ * container needs to move to in order to center the active button.
293
+ * It is calculated by adding the current scroll position
294
+ * (scrollLeft) to the offset needed to center the button
295
+ * (centeredX).
296
+ */
297
+ const newScrollPosition = el.scrollLeft + centeredX;
298
+ /**
299
+ * We intentionally use scrollTo here instead of scrollIntoView
292
300
  * to avoid a WebKit bug where accelerated animations break
293
301
  * when using scrollIntoView. Using scrollIntoView will cause the
294
302
  * segment container to jump during the transition and then snap into place.
295
303
  * This is because scrollIntoView can potentially cause parent element
296
- * containers to also scroll. scrollBy does not have this same behavior, so
304
+ * containers to also scroll. scrollTo does not have this same behavior, so
297
305
  * we use this API instead.
298
306
  *
307
+ * scrollTo is used instead of scrollBy because there is a
308
+ * Webkit bug that causes scrollBy to not work smoothly when
309
+ * the active button is near the edge of the scroll container.
310
+ * This leads to the buttons to jump around during the transition.
311
+ *
299
312
  * Note that if there is not enough scrolling space to center the element
300
313
  * within the scroll container, the browser will attempt
301
314
  * to center by as much as it can.
302
315
  */
303
- el.scrollBy({
316
+ el.scrollTo({
304
317
  top: 0,
305
- left: centeredX,
318
+ left: newScrollPosition,
306
319
  behavior: smoothScroll ? 'smooth' : 'instant',
307
320
  });
308
321
  }
@@ -431,14 +444,14 @@ const Segment = class {
431
444
  }
432
445
  render() {
433
446
  const mode = ionicGlobal.getIonMode(this);
434
- return (index.h(index.Host, { key: 'ad0946134c8d465b760ad792655f1cb9922db520', role: "tablist", onClick: this.onClick, class: theme.createColorClasses(this.color, {
447
+ return (index.h(index.Host, { key: 'f1f7103b4c298e037df850ac809a1a7c6e9987a7', role: "tablist", onClick: this.onClick, class: theme.createColorClasses(this.color, {
435
448
  [mode]: true,
436
449
  'in-toolbar': theme.hostContext('ion-toolbar', this.el),
437
450
  'in-toolbar-color': theme.hostContext('ion-toolbar[color]', this.el),
438
451
  'segment-activated': this.activated,
439
452
  'segment-disabled': this.disabled,
440
453
  'segment-scrollable': this.scrollable,
441
- }) }, index.h("slot", { key: 'dcdb425bcda0d60acb7c317e5e671ed462715b4a', onSlotchange: this.onSlottedItemsChange })));
454
+ }) }, index.h("slot", { key: '6efdb318b13da8d21687084aa0761728fdf12579', onSlotchange: this.onSlottedItemsChange })));
442
455
  }
443
456
  get el() { return index.getElement(this); }
444
457
  static get watchers() { return {
@@ -9,7 +9,7 @@ const index = require('./index-73f75efb.js');
9
9
  const notchController = require('./notch-controller-d69150f5.js');
10
10
  const compareWithUtils = require('./compare-with-utils-df1001d7.js');
11
11
  const helpers = require('./helpers-afaa9001.js');
12
- const overlays = require('./overlays-0123d7d4.js');
12
+ const overlays = require('./overlays-ba0f6986.js');
13
13
  const dir = require('./dir-94c21456.js');
14
14
  const theme = require('./theme-d1c573d2.js');
15
15
  const watchOptions = require('./watch-options-f5f3e158.js');
@@ -28,6 +28,8 @@ const Textarea = class {
28
28
  this.ionBlur = index.createEvent(this, "ionBlur", 7);
29
29
  this.ionFocus = index.createEvent(this, "ionFocus", 7);
30
30
  this.inputId = `ion-textarea-${textareaIds++}`;
31
+ this.helperTextId = `${this.inputId}-helper-text`;
32
+ this.errorTextId = `${this.inputId}-error-text`;
31
33
  /**
32
34
  * `true` if the textarea was cleared as a result of the user typing
33
35
  * with `clearOnEdit` enabled.
@@ -315,8 +317,21 @@ const Textarea = class {
315
317
  * Renders the helper text or error text values
316
318
  */
317
319
  renderHintText() {
318
- const { helperText, errorText } = this;
319
- return [index.h("div", { class: "helper-text" }, helperText), index.h("div", { class: "error-text" }, errorText)];
320
+ const { helperText, errorText, helperTextId, errorTextId } = this;
321
+ return [
322
+ index.h("div", { id: helperTextId, class: "helper-text" }, helperText),
323
+ index.h("div", { id: errorTextId, class: "error-text" }, errorText),
324
+ ];
325
+ }
326
+ getHintTextID() {
327
+ const { el, helperText, errorText, helperTextId, errorTextId } = this;
328
+ if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
329
+ return errorTextId;
330
+ }
331
+ if (helperText) {
332
+ return helperTextId;
333
+ }
334
+ return undefined;
320
335
  }
321
336
  renderCounter() {
322
337
  const { counter, maxlength, counterFormatter, value } = this;
@@ -369,7 +384,7 @@ const Textarea = class {
369
384
  * TODO(FW-5592): Remove hasStartEndSlots condition
370
385
  */
371
386
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
372
- return (index.h(index.Host, { key: '37595a18d77dea1a337ac1c672e5f08a4128111d', class: theme.createColorClasses(this.color, {
387
+ return (index.h(index.Host, { key: 'e8a5b5727c6a018bbd6f5507b690bc5f0959e352', class: theme.createColorClasses(this.color, {
373
388
  [mode]: true,
374
389
  'has-value': hasValue,
375
390
  'has-focus': hasFocus,
@@ -378,7 +393,7 @@ const Textarea = class {
378
393
  [`textarea-shape-${shape}`]: shape !== undefined,
379
394
  [`textarea-label-placement-${labelPlacement}`]: true,
380
395
  'textarea-disabled': disabled,
381
- }) }, index.h("label", { key: '67342758743e5a40448a32ff695876d39778621f', class: "textarea-wrapper", htmlFor: inputId }, this.renderLabelContainer(), index.h("div", { key: 'a994be8264bf5652811cf816d79a04178954e83f', class: "textarea-wrapper-inner" }, index.h("div", { key: 'e09c93ebcd5b3d227d51e682ca23dfc7fd7027ad', class: "start-slot-wrapper" }, index.h("slot", { key: 'd39758f21f19ae70aea21e9a9a7b7c20991fe593', name: "start" })), index.h("div", { key: '6a4e10e53de4bb235ee30def4c9ae5bdee888816', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, index.h("textarea", Object.assign({ key: '9e254e551f124d972e9bc6b09ef0f2bb55890be5', 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 }, this.inheritedAttributes), value)), index.h("div", { key: 'a66aa2d2bc4778a0bec56a8b9ec9052a832eb3b2', class: "end-slot-wrapper" }, index.h("slot", { key: '8e6a90b4475de32e23afc593da4108610dcca663', name: "end" }))), shouldRenderHighlight && index.h("div", { key: '6da03205a8daff45581b20f0af3938634a9d5f8c', class: "textarea-highlight" })), this.renderBottomContent()));
396
+ }) }, index.h("label", { key: '48e889571e2d3e7e150b038e4b4b15862b418288', class: "textarea-wrapper", htmlFor: inputId }, this.renderLabelContainer(), index.h("div", { key: '6c6c7872ae4d351d8b8d772ad18ce3ba7a0e9a87', class: "textarea-wrapper-inner" }, index.h("div", { key: '03aef5dfa59af0fa78a3e5c92e0ed72396717d72', class: "start-slot-wrapper" }, index.h("slot", { key: '39aee9faebb0f1d10de5a5817fd9d9771b074b96', name: "start" })), index.h("div", { key: '738e8ff603f4c9b9083b3139db861f4b7ec20f79', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, index.h("textarea", Object.assign({ key: '7763fb4f8ffe94635167331c70d21d661544c4f8', 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: 'b3af9ec0982c6dbd381afaa02df153d7e90471be', class: "end-slot-wrapper" }, index.h("slot", { key: '60f99bcd49a5ec2e1fa6e3e77809aefeb9d2d0d6', name: "end" }))), shouldRenderHighlight && index.h("div", { key: '6a7f45639ba027f4da573adae53f1197a6dec383', class: "textarea-highlight" })), this.renderBottomContent()));
382
397
  }
383
398
  get el() { return index.getElement(this); }
384
399
  static get watchers() { return {
@@ -10,7 +10,7 @@ const config = require('./config-4f60b98a.js');
10
10
  const helpers = require('./helpers-afaa9001.js');
11
11
  const lockController = require('./lock-controller-6585a42a.js');
12
12
  const index$1 = require('./index-5915f9b3.js');
13
- const overlays = require('./overlays-0123d7d4.js');
13
+ const overlays = require('./overlays-ba0f6986.js');
14
14
  const theme = require('./theme-d1c573d2.js');
15
15
  const ionicGlobal = require('./ionic-global-d9a8bb5b.js');
16
16
  const animation = require('./animation-b4fdf128.js');
@@ -502,9 +502,19 @@ const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation, opts)
502
502
  if (overlay.presented) {
503
503
  return;
504
504
  }
505
- setRootAriaHidden(true);
505
+ /**
506
+ * Due to accessibility guidelines, toasts do not have
507
+ * focus traps.
508
+ *
509
+ * All other overlays should have focus traps to prevent
510
+ * the keyboard focus from leaving the overlay.
511
+ */
512
+ if (overlay.el.tagName !== 'ION-TOAST') {
513
+ setRootAriaHidden(true);
514
+ }
506
515
  document.body.classList.add(gestureController.BACKDROP_NO_SCROLL);
507
- hideOverlaysFromScreenReaders(overlay.el);
516
+ hideUnderlyingOverlaysFromScreenReaders(overlay.el);
517
+ hideAnimatingOverlayFromScreenReaders(overlay.el);
508
518
  overlay.presented = true;
509
519
  overlay.willPresent.emit();
510
520
  (_a = overlay.willPresentShorthand) === null || _a === void 0 ? void 0 : _a.emit();
@@ -544,6 +554,11 @@ const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation, opts)
544
554
  * it would still have aria-hidden on being presented again.
545
555
  * Removing it here ensures the overlay is visible to screen
546
556
  * readers.
557
+ *
558
+ * If this overlay was being presented, then it was hidden
559
+ * from screen readers during the animation. Now that the
560
+ * animation is complete, we can reveal the overlay to
561
+ * screen readers.
547
562
  */
548
563
  overlay.el.removeAttribute('aria-hidden');
549
564
  };
@@ -601,17 +616,35 @@ const dismiss = async (overlay, data, role, name, iosLeaveAnimation, mdLeaveAnim
601
616
  if (!overlay.presented) {
602
617
  return false;
603
618
  }
604
- const lastOverlay = index.doc !== undefined && getPresentedOverlays(index.doc).length === 1;
605
619
  /**
606
- * If this is the last visible overlay then
607
- * we want to re-add the root to the accessibility tree.
620
+ * For accessibility, toasts lack focus traps and don’t receive
621
+ * `aria-hidden` on the root element when presented.
622
+ *
623
+ * All other overlays use focus traps to keep keyboard focus
624
+ * within the overlay, setting `aria-hidden` on the root element
625
+ * to enhance accessibility.
626
+ *
627
+ * Therefore, we must remove `aria-hidden` from the root element
628
+ * when the last non-toast overlay is dismissed.
608
629
  */
609
- if (lastOverlay) {
630
+ const overlaysNotToast = index.doc !== undefined ? getPresentedOverlays(index.doc).filter((o) => o.tagName !== 'ION-TOAST') : [];
631
+ const lastOverlayNotToast = overlaysNotToast.length === 1 && overlaysNotToast[0].id === overlay.el.id;
632
+ /**
633
+ * If this is the last visible overlay that is not a toast
634
+ * then we want to re-add the root to the accessibility tree.
635
+ */
636
+ if (lastOverlayNotToast) {
610
637
  setRootAriaHidden(false);
611
638
  document.body.classList.remove(gestureController.BACKDROP_NO_SCROLL);
612
639
  }
613
640
  overlay.presented = false;
614
641
  try {
642
+ /**
643
+ * There is no need to show the overlay to screen readers during
644
+ * the dismiss animation. This is because the overlay will be removed
645
+ * from the DOM after the animation is complete.
646
+ */
647
+ hideAnimatingOverlayFromScreenReaders(overlay.el);
615
648
  // Overlay contents should not be clickable during dismiss
616
649
  overlay.el.style.setProperty('pointer-events', 'none');
617
650
  overlay.willDismiss.emit({ data, role });
@@ -846,6 +879,28 @@ const createTriggerController = () => {
846
879
  removeClickListener,
847
880
  };
848
881
  };
882
+ /**
883
+ * The overlay that is being animated also needs to hide from screen
884
+ * readers during its animation. This ensures that assistive technologies
885
+ * like TalkBack do not announce or interact with the content until the
886
+ * animation is complete, avoiding confusion for users.
887
+ *
888
+ * If the overlay is being presented, it prevents focus rings from appearing
889
+ * in incorrect positions due to the transition (specifically `transform`
890
+ * styles), ensuring that when aria-hidden is removed, the focus rings are
891
+ * correctly displayed in the final location of the elements.
892
+ *
893
+ * @param overlay - The overlay that is being animated.
894
+ */
895
+ const hideAnimatingOverlayFromScreenReaders = (overlay) => {
896
+ if (index.doc === undefined)
897
+ return;
898
+ /**
899
+ * Once the animation is complete, this attribute will be removed.
900
+ * This is done at the end of the `present` method.
901
+ */
902
+ overlay.setAttribute('aria-hidden', 'true');
903
+ };
849
904
  /**
850
905
  * Ensure that underlying overlays have aria-hidden if necessary so that screen readers
851
906
  * cannot move focus to these elements. Note that we cannot rely on focus/focusin/focusout
@@ -856,7 +911,7 @@ const createTriggerController = () => {
856
911
  * @param newTopMostOverlay - The overlay that is being presented. Since the overlay has not been
857
912
  * fully presented yet at the time this function is called it will not be included in the getPresentedOverlays result.
858
913
  */
859
- const hideOverlaysFromScreenReaders = (newTopMostOverlay) => {
914
+ const hideUnderlyingOverlaysFromScreenReaders = (newTopMostOverlay) => {
860
915
  var _a;
861
916
  if (index.doc === undefined)
862
917
  return;
@@ -196,10 +196,10 @@ export class ActionSheet {
196
196
  const headerID = `action-sheet-${overlayIndex}-header`;
197
197
  return (h(Host, Object.assign({ key: '7bbd202ca9e19727e7514abbe073687d982f80c3', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1" }, htmlAttributes, { style: {
198
198
  zIndex: `${20000 + this.overlayIndex}`,
199
- }, class: Object.assign(Object.assign({ [mode]: true }, getClassMap(this.cssClass)), { 'overlay-hidden': true, 'action-sheet-translucent': this.translucent }), onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }), h("ion-backdrop", { key: '23344a9221a2e6720d7b9de5249dc37256cafa7b', tappable: this.backdropDismiss }), h("div", { key: 'd46361bb5cdc32a7922dcf76b566f358a6174bfa', tabindex: "0" }), h("div", { key: '136c3f2e77c8a2eac8e9ae4bb13d735e1d62598d', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, h("div", { key: '6168ea8b2be42020b2edeb8ff3a0f3d1254be37a', class: "action-sheet-container" }, h("div", { key: '29b9e6619dc54574733a704d6bf885839151bd84', class: "action-sheet-group", ref: (el) => (this.groupEl = el) }, header !== undefined && (h("div", { key: '536ce764bfddb3816ea3512d90f4acef2ccb8589', id: headerID, class: {
199
+ }, class: Object.assign(Object.assign({ [mode]: true }, getClassMap(this.cssClass)), { 'overlay-hidden': true, 'action-sheet-translucent': this.translucent }), onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }), h("ion-backdrop", { key: '23344a9221a2e6720d7b9de5249dc37256cafa7b', tappable: this.backdropDismiss }), h("div", { key: 'fbc2ba15549c2ab04e759e82df6e177fd80cc0a6', tabindex: "0", "aria-hidden": "true" }), h("div", { key: '748ee5235d0b4cb26d6f1b7589f77af2e37ad28a', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, h("div", { key: '7ce5fa236cf75e9b1e49c4725c9a811078706554', class: "action-sheet-container" }, h("div", { key: 'dc2251f3bcee4a93e3449f09621cbd2b65d329e9', class: "action-sheet-group", ref: (el) => (this.groupEl = el) }, header !== undefined && (h("div", { key: '48d325c8a852f56ed57a9ada1a6709d05ba32ee2', id: headerID, class: {
200
200
  'action-sheet-title': true,
201
201
  'action-sheet-has-sub-title': this.subHeader !== undefined,
202
- } }, header, this.subHeader && h("div", { key: '6d888219145824fd36cdfe0d3c8388bbf2769777', class: "action-sheet-sub-title" }, this.subHeader))), buttons.map((b) => (h("button", Object.assign({}, b.htmlAttributes, { type: "button", id: b.id, class: buttonClass(b), onClick: () => this.buttonClick(b), disabled: b.disabled }), h("span", { class: "action-sheet-button-inner" }, b.icon && h("ion-icon", { icon: b.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" }), b.text), mode === 'md' && h("ion-ripple-effect", null))))), cancelButton && (h("div", { key: '46f98f0ed5a9bdb3e35feb9ae71c4489c17b7d77', class: "action-sheet-group action-sheet-group-cancel" }, h("button", Object.assign({ key: 'e3c457bced8ad5f692e48de26e65f731fd631b4f' }, cancelButton.htmlAttributes, { type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) }), h("span", { key: '4cba6dc559f734ecc852e024959210cd0dd25354', class: "action-sheet-button-inner" }, cancelButton.icon && (h("ion-icon", { key: '9df64989aad1b4d1e75edf4d37ab208965cfe37f', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && h("ion-ripple-effect", { key: '558089275a29177cefdb2e38eefc9b5c8b62872b' })))))), h("div", { key: 'fa4df6e043b00a6e4126dbc71cb344cfc2b2e7bc', tabindex: "0" })));
202
+ } }, header, this.subHeader && h("div", { key: '66093728052eb67f37a35f3232761ce4a08896f3', class: "action-sheet-sub-title" }, this.subHeader))), buttons.map((b) => (h("button", Object.assign({}, b.htmlAttributes, { type: "button", id: b.id, class: buttonClass(b), onClick: () => this.buttonClick(b), disabled: b.disabled }), h("span", { class: "action-sheet-button-inner" }, b.icon && h("ion-icon", { icon: b.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" }), b.text), mode === 'md' && h("ion-ripple-effect", null))))), cancelButton && (h("div", { key: 'f4eb8e3e2885b85af5080df18d0de0bdd1d719de', class: "action-sheet-group action-sheet-group-cancel" }, h("button", Object.assign({ key: '169f4eb09255aba85062baad49ceb151239fbfb7' }, cancelButton.htmlAttributes, { type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) }), h("span", { key: '25fb8a466dd67ea94c79cfb4f9965527e1ce6d42', class: "action-sheet-button-inner" }, cancelButton.icon && (h("ion-icon", { key: 'eb5b071e120a2c86afdf967af6a763a43044d1ca', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && h("ion-ripple-effect", { key: '452ad7e1052b2c681e2d98de8193949755ad4d54' })))))), h("div", { key: 'e1cecf280c987c050d9445e2c458b903f153089b', tabindex: "0", "aria-hidden": "true" })));
203
203
  }
204
204
  static get is() { return "ion-action-sheet"; }
205
205
  static get encapsulation() { return "scoped"; }
@@ -396,7 +396,7 @@ export class Alert {
396
396
  const ariaLabelledBy = header ? hdrId : subHeader ? subHdrId : null;
397
397
  return (h(Host, Object.assign({ key: 'c7d53f48b359f2bc3480a2e1ba34948fc9378fb0', role: role, "aria-modal": "true", "aria-labelledby": ariaLabelledBy, "aria-describedby": message !== undefined ? msgId : null, tabindex: "-1" }, htmlAttributes, { style: {
398
398
  zIndex: `${20000 + overlayIndex}`,
399
- }, class: Object.assign(Object.assign({}, getClassMap(this.cssClass)), { [mode]: true, 'overlay-hidden': true, 'alert-translucent': this.translucent }), onIonAlertWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }), h("ion-backdrop", { key: '18c7e6b5d63435d9a6a82bda951158e7e1af6e92', tappable: this.backdropDismiss }), h("div", { key: 'e35e1a4b81286976c8a6bab570c986f3196b21f4', tabindex: "0" }), h("div", { key: '9089864c80d96ed834bf723f3de863cf1c4a5b97', class: "alert-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, h("div", { key: 'dd600f02c84352059c6cdf98821b9c9a831bcdcb', class: "alert-head" }, header && (h("h2", { key: '11afb605e1ccefc889fbdd2533d491bea8fbf183', id: hdrId, class: "alert-title" }, header)), subHeader && (h("h2", { key: 'e53b0613d09d26e5a2cd7c9c6e63ec2535625ce5', id: subHdrId, class: "alert-sub-title" }, subHeader))), this.renderAlertMessage(msgId), this.renderAlertInputs(), this.renderAlertButtons()), h("div", { key: 'cef60ec8b34c9aec8bc698f16f55324d1ce67c72', tabindex: "0" })));
399
+ }, class: Object.assign(Object.assign({}, getClassMap(this.cssClass)), { [mode]: true, 'overlay-hidden': true, 'alert-translucent': this.translucent }), onIonAlertWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }), h("ion-backdrop", { key: '18c7e6b5d63435d9a6a82bda951158e7e1af6e92', tappable: this.backdropDismiss }), h("div", { key: '8ea08a2a70239e817442c12596f51b108aca428c', tabindex: "0", "aria-hidden": "true" }), h("div", { key: '215ca5d72b8b180788e9748f5f22de01fa5a152a', class: "alert-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, h("div", { key: '9880acd318428d4b5d670c67dd156477877f99f2', class: "alert-head" }, header && (h("h2", { key: '612dec8dc9bfdcb36e1e4655dc214b7c229b20c8', id: hdrId, class: "alert-title" }, header)), subHeader && (h("h2", { key: 'e3c54c80a1cd9399595064b5a0a887c16fdc8ff8', id: subHdrId, class: "alert-sub-title" }, subHeader))), this.renderAlertMessage(msgId), this.renderAlertInputs(), this.renderAlertButtons()), h("div", { key: '9062655e906b5caf36df6f742fd412552ae510be', tabindex: "0", "aria-hidden": "true" })));
400
400
  }
401
401
  static get is() { return "ion-alert"; }
402
402
  static get encapsulation() { return "scoped"; }
@@ -49,7 +49,6 @@ export class App {
49
49
  }
50
50
  }
51
51
  /**
52
- * @internal
53
52
  * Used to set focus on an element that uses `ion-focusable`.
54
53
  * Do not use this if focusing the element as a result of a keyboard
55
54
  * event as the focus utility should handle this for us. This method
@@ -65,7 +64,7 @@ export class App {
65
64
  }
66
65
  render() {
67
66
  const mode = getIonMode(this);
68
- return (h(Host, { key: 'a562850f242d9d45573e35efdd4bd178254677ef', class: {
67
+ return (h(Host, { key: 'e95cdeb2709edbc74f4e6ebf77cb110154605b72', class: {
69
68
  [mode]: true,
70
69
  'ion-page': true,
71
70
  'force-statusbar-padding': config.getBoolean('_forceStatusbarPadding'),
@@ -105,11 +104,8 @@ export class App {
105
104
  "return": "Promise<void>"
106
105
  },
107
106
  "docs": {
108
- "text": "",
109
- "tags": [{
110
- "name": "internal",
111
- "text": "Used to set focus on an element that uses `ion-focusable`.\nDo not use this if focusing the element as a result of a keyboard\nevent as the focus utility should handle this for us. This method\nshould be used when we want to programmatically focus an element as\na result of another user action. (Ex: We focus the first element\ninside of a popover when the user presents it, but the popover is not always\npresented as a result of keyboard action.)"
112
- }]
107
+ "text": "Used to set focus on an element that uses `ion-focusable`.\nDo not use this if focusing the element as a result of a keyboard\nevent as the focus utility should handle this for us. This method\nshould be used when we want to programmatically focus an element as\na result of another user action. (Ex: We focus the first element\ninside of a popover when the user presents it, but the popover is not always\npresented as a result of keyboard action.)",
108
+ "tags": []
113
109
  }
114
110
  }
115
111
  };
@@ -23,7 +23,7 @@ export class Backdrop {
23
23
  }
24
24
  render() {
25
25
  const mode = getIonMode(this);
26
- return (h(Host, { key: 'c803b4302c8e722064feb03dafe3cb6e190b4f2b', tabindex: "-1", "aria-hidden": "true", class: {
26
+ return (h(Host, { key: 'fe191068e2aaadfdbe7a463c08d227a1b36d7a98', "aria-hidden": "true", class: {
27
27
  [mode]: true,
28
28
  'backdrop-hide': !this.visible,
29
29
  'backdrop-no-tappable': !this.tappable,
@@ -19,6 +19,8 @@ import { getCounterText } from "./input.utils";
19
19
  export class Input {
20
20
  constructor() {
21
21
  this.inputId = `ion-input-${inputIds++}`;
22
+ this.helperTextId = `${this.inputId}-helper-text`;
23
+ this.errorTextId = `${this.inputId}-error-text`;
22
24
  this.inheritedAttributes = {};
23
25
  this.isComposing = false;
24
26
  /**
@@ -291,8 +293,21 @@ export class Input {
291
293
  * Renders the helper text or error text values
292
294
  */
293
295
  renderHintText() {
294
- const { helperText, errorText } = this;
295
- return [h("div", { class: "helper-text" }, helperText), h("div", { class: "error-text" }, errorText)];
296
+ const { helperText, errorText, helperTextId, errorTextId } = this;
297
+ return [
298
+ h("div", { id: helperTextId, class: "helper-text" }, helperText),
299
+ h("div", { id: errorTextId, class: "error-text" }, errorText),
300
+ ];
301
+ }
302
+ getHintTextID() {
303
+ const { el, helperText, errorText, helperTextId, errorTextId } = this;
304
+ if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
305
+ return errorTextId;
306
+ }
307
+ if (helperText) {
308
+ return helperTextId;
309
+ }
310
+ return undefined;
296
311
  }
297
312
  renderCounter() {
298
313
  const { counter, maxlength, counterFormatter, value } = this;
@@ -399,7 +414,7 @@ export class Input {
399
414
  * TODO(FW-5592): Remove hasStartEndSlots condition
400
415
  */
401
416
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
402
- return (h(Host, { key: '907ce98a82b5cfae5a08504cd79e00a2330b7444', class: createColorClasses(this.color, {
417
+ return (h(Host, { key: 'cdde9963d71685e6a4f3f3b3bc52e75d022435cb', class: createColorClasses(this.color, {
403
418
  [mode]: true,
404
419
  'has-value': hasValue,
405
420
  'has-focus': hasFocus,
@@ -410,7 +425,7 @@ export class Input {
410
425
  'in-item': inItem,
411
426
  'in-item-color': hostContext('ion-item.ion-color', this.el),
412
427
  'input-disabled': disabled,
413
- }) }, h("label", { key: '59d5bb45d2a5b828bba0ed8687a632a551e2f4d8', class: "input-wrapper", htmlFor: inputId }, this.renderLabelContainer(), h("div", { key: 'f93f129d08246d0e9a601c100d718534d6403853', class: "native-wrapper" }, h("slot", { key: '54eeb1a6bace662b7eb0d7e27180ea3d7e3a3729', name: "start" }), h("input", Object.assign({ key: 'b3e0be55bc1a4a539ae3b0fdcf7fc078723cca16', 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 }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: '5f6373504a6d0d074bfbf875c794d45ea2748175', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
428
+ }) }, h("label", { key: '4412791c93405f20e50e50363879265180b6078f', class: "input-wrapper", htmlFor: inputId }, this.renderLabelContainer(), h("div", { key: '5c71c43ef3eeb44dde76ed64ce10763834c8fbc7', class: "native-wrapper" }, h("slot", { key: '1d86b58dc299a4208dd02ccdc9ceaea7576698c3', name: "start" }), h("input", Object.assign({ key: '7fc932174485f7d09e6a797dfd49ea11fbad71b6', 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: '46de455e4e308b8d7640b04778f7c7559b4124c6', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
414
429
  /**
415
430
  * This prevents mobile browsers from
416
431
  * blurring the input when the clear
@@ -425,7 +440,7 @@ export class Input {
425
440
  * for screen readers as it means users would be unable to swipe past the clear button.
426
441
  */
427
442
  ev.stopPropagation();
428
- }, onClick: this.clearTextInput }, h("ion-icon", { key: '230d77973aa83458ceb32bf52e3abe9bc322cfe6', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '9d69ac6e8a3c4b2b303dba2478f82695d5755ed2', name: "end" })), shouldRenderHighlight && h("div", { key: 'ac61f16237ce731e0745ab72d0fc3f066252464a', class: "input-highlight" })), this.renderBottomContent()));
443
+ }, onClick: this.clearTextInput }, h("ion-icon", { key: '375e860a20e1807ce0db0e1934a0650fe9929966', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '859d47a1b88aa8377479e5d11e52a6a1c1c30e7e', name: "end" })), shouldRenderHighlight && h("div", { key: 'd2d4286b348107e33247b5c0afda0f8bd697ecee', class: "input-highlight" })), this.renderBottomContent()));
429
444
  }
430
445
  static get is() { return "ion-input"; }
431
446
  static get encapsulation() { return "scoped"; }
@@ -163,7 +163,7 @@ export class Loading {
163
163
  const ariaLabelledBy = message !== undefined ? msgId : null;
164
164
  return (h(Host, Object.assign({ key: 'fb3d2cd7cd0539ed6540d8be50e243c7916b3ca2', role: "dialog", "aria-modal": "true", "aria-labelledby": ariaLabelledBy, tabindex: "-1" }, htmlAttributes, { style: {
165
165
  zIndex: `${40000 + this.overlayIndex}`,
166
- }, onIonBackdropTap: this.onBackdropTap, class: Object.assign(Object.assign({}, getClassMap(this.cssClass)), { [mode]: true, 'overlay-hidden': true, 'loading-translucent': this.translucent }) }), h("ion-backdrop", { key: 'c8bd30782f3040b1b80e88aa924861e439d40754', visible: this.showBackdrop, tappable: this.backdropDismiss }), h("div", { key: '4ebfbb67c92e8eb56c27dd7c199b35bf6be1cf63', tabindex: "0" }), h("div", { key: '9492723cd87f1ef75534e449d4bc2b2deb0cb3cc', class: "loading-wrapper ion-overlay-wrapper" }, spinner && (h("div", { key: 'd9eb42454e48c82704d974a68b80ceb4de990417', class: "loading-spinner" }, h("ion-spinner", { key: 'cdb046bad89872f4208ae466979315652766bf3a', name: spinner, "aria-hidden": "true" }))), message !== undefined && this.renderLoadingMessage(msgId)), h("div", { key: '412d1bb537dafa6e6863d07ddc15f5969fe0617d', tabindex: "0" })));
166
+ }, onIonBackdropTap: this.onBackdropTap, class: Object.assign(Object.assign({}, getClassMap(this.cssClass)), { [mode]: true, 'overlay-hidden': true, 'loading-translucent': this.translucent }) }), h("ion-backdrop", { key: 'c8bd30782f3040b1b80e88aa924861e439d40754', visible: this.showBackdrop, tappable: this.backdropDismiss }), h("div", { key: '7ad4447fafa49aa861d311221b8c2e8171b4c610', tabindex: "0", "aria-hidden": "true" }), h("div", { key: '1879df0d895eb8ee20ccdc94dd35d708c1140b91', class: "loading-wrapper ion-overlay-wrapper" }, spinner && (h("div", { key: '220f292c136681f5fa91f94669aac75b15e490ce', class: "loading-spinner" }, h("ion-spinner", { key: 'd599084a1426d941bdd080a988d8ad5b9589b823', name: spinner, "aria-hidden": "true" }))), message !== undefined && this.renderLoadingMessage(msgId)), h("div", { key: 'e443897b353a48e0c72718dba04d83d7c5070d73', tabindex: "0", "aria-hidden": "true" })));
167
167
  }
168
168
  static get is() { return "ion-loading"; }
169
169
  static get encapsulation() { return "scoped"; }
@@ -189,7 +189,7 @@ export class Picker {
189
189
  zIndex: `${20000 + this.overlayIndex}`,
190
190
  }, class: Object.assign({ [mode]: true,
191
191
  // Used internally for styling
192
- [`picker-${mode}`]: true, 'overlay-hidden': true }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonPickerWillDismiss: this.dispatchCancelHandler }), h("ion-backdrop", { key: 'da4224447bdbcfbadd1bd63ebe7a7bfdb8aa3129', visible: this.showBackdrop, tappable: this.backdropDismiss }), h("div", { key: '211475c859b7acf4d64556905c45993201d92d43', tabindex: "0" }), h("div", { key: 'c3b3674038155809caea84ae6348ff0d66897a64', class: "picker-wrapper ion-overlay-wrapper", role: "dialog" }, h("div", { key: 'a21757da70dc7864945a5751d9da3ca4bf87897b', class: "picker-toolbar" }, this.buttons.map((b) => (h("div", { class: buttonWrapperClass(b) }, h("button", { type: "button", onClick: () => this.buttonClick(b), class: buttonClass(b) }, b.text))))), h("div", { key: '0220da1854d09ef22d2d9a32eaa3c3b222be5a0a', class: "picker-columns" }, h("div", { key: '55757c165ba9e38e56b4dee76a9b13d3ff8b21e6', class: "picker-above-highlight" }), this.presented && this.columns.map((c) => h("ion-picker-legacy-column", { col: c })), h("div", { key: '160092a46a48287bf75e5dfe3502f1755a2d0c70', class: "picker-below-highlight" }))), h("div", { key: 'e212db6cca7d75fc0eda0baf4312dd51b9b7a4ef', tabindex: "0" })));
192
+ [`picker-${mode}`]: true, 'overlay-hidden': true }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonPickerWillDismiss: this.dispatchCancelHandler }), h("ion-backdrop", { key: 'da4224447bdbcfbadd1bd63ebe7a7bfdb8aa3129', visible: this.showBackdrop, tappable: this.backdropDismiss }), h("div", { key: '65eba425990a21be00fdac90de20a630d284cf97', tabindex: "0", "aria-hidden": "true" }), h("div", { key: 'd78ada360801b2c051baa030751f3d83ac81e697', class: "picker-wrapper ion-overlay-wrapper", role: "dialog" }, h("div", { key: 'd5bd9ddfbdbdaf42048a1496e7939eb3aee6da65', class: "picker-toolbar" }, this.buttons.map((b) => (h("div", { class: buttonWrapperClass(b) }, h("button", { type: "button", onClick: () => this.buttonClick(b), class: buttonClass(b) }, b.text))))), h("div", { key: '96ee1e3ec77c57174b1a93135b66bea6b7002555', class: "picker-columns" }, h("div", { key: 'e11b1c238f350e65bde2ee67eee9d71c830e74e7', class: "picker-above-highlight" }), this.presented && this.columns.map((c) => h("ion-picker-legacy-column", { col: c })), h("div", { key: '4d5fc7c723185111ec01fd34ef95a897c301238d', class: "picker-below-highlight" }))), h("div", { key: '899bcad97609c1d971c1b886551db5ba0248a9f4', tabindex: "0", "aria-hidden": "true" })));
193
193
  }
194
194
  static get is() { return "ion-picker-legacy"; }
195
195
  static get encapsulation() { return "scoped"; }
@@ -276,21 +276,34 @@ export class Segment {
276
276
  */
277
277
  const centeredX = activeButtonLeft - scrollContainerBox.width / 2 + activeButtonBox.width / 2;
278
278
  /**
279
- * We intentionally use scrollBy here instead of scrollIntoView
279
+ * newScrollPosition is the absolute scroll position that the
280
+ * container needs to move to in order to center the active button.
281
+ * It is calculated by adding the current scroll position
282
+ * (scrollLeft) to the offset needed to center the button
283
+ * (centeredX).
284
+ */
285
+ const newScrollPosition = el.scrollLeft + centeredX;
286
+ /**
287
+ * We intentionally use scrollTo here instead of scrollIntoView
280
288
  * to avoid a WebKit bug where accelerated animations break
281
289
  * when using scrollIntoView. Using scrollIntoView will cause the
282
290
  * segment container to jump during the transition and then snap into place.
283
291
  * This is because scrollIntoView can potentially cause parent element
284
- * containers to also scroll. scrollBy does not have this same behavior, so
292
+ * containers to also scroll. scrollTo does not have this same behavior, so
285
293
  * we use this API instead.
286
294
  *
295
+ * scrollTo is used instead of scrollBy because there is a
296
+ * Webkit bug that causes scrollBy to not work smoothly when
297
+ * the active button is near the edge of the scroll container.
298
+ * This leads to the buttons to jump around during the transition.
299
+ *
287
300
  * Note that if there is not enough scrolling space to center the element
288
301
  * within the scroll container, the browser will attempt
289
302
  * to center by as much as it can.
290
303
  */
291
- el.scrollBy({
304
+ el.scrollTo({
292
305
  top: 0,
293
- left: centeredX,
306
+ left: newScrollPosition,
294
307
  behavior: smoothScroll ? 'smooth' : 'instant',
295
308
  });
296
309
  }
@@ -421,14 +434,14 @@ export class Segment {
421
434
  }
422
435
  render() {
423
436
  const mode = getIonMode(this);
424
- return (h(Host, { key: 'ad0946134c8d465b760ad792655f1cb9922db520', role: "tablist", onClick: this.onClick, class: createColorClasses(this.color, {
437
+ return (h(Host, { key: 'f1f7103b4c298e037df850ac809a1a7c6e9987a7', role: "tablist", onClick: this.onClick, class: createColorClasses(this.color, {
425
438
  [mode]: true,
426
439
  'in-toolbar': hostContext('ion-toolbar', this.el),
427
440
  'in-toolbar-color': hostContext('ion-toolbar[color]', this.el),
428
441
  'segment-activated': this.activated,
429
442
  'segment-disabled': this.disabled,
430
443
  'segment-scrollable': this.scrollable,
431
- }) }, h("slot", { key: 'dcdb425bcda0d60acb7c317e5e671ed462715b4a', onSlotchange: this.onSlottedItemsChange })));
444
+ }) }, h("slot", { key: '6efdb318b13da8d21687084aa0761728fdf12579', onSlotchange: this.onSlottedItemsChange })));
432
445
  }
433
446
  static get is() { return "ion-segment"; }
434
447
  static get encapsulation() { return "shadow"; }
@@ -18,6 +18,8 @@ import { getCounterText } from "../input/input.utils";
18
18
  export class Textarea {
19
19
  constructor() {
20
20
  this.inputId = `ion-textarea-${textareaIds++}`;
21
+ this.helperTextId = `${this.inputId}-helper-text`;
22
+ this.errorTextId = `${this.inputId}-error-text`;
21
23
  /**
22
24
  * `true` if the textarea was cleared as a result of the user typing
23
25
  * with `clearOnEdit` enabled.
@@ -305,8 +307,21 @@ export class Textarea {
305
307
  * Renders the helper text or error text values
306
308
  */
307
309
  renderHintText() {
308
- const { helperText, errorText } = this;
309
- return [h("div", { class: "helper-text" }, helperText), h("div", { class: "error-text" }, errorText)];
310
+ const { helperText, errorText, helperTextId, errorTextId } = this;
311
+ return [
312
+ h("div", { id: helperTextId, class: "helper-text" }, helperText),
313
+ h("div", { id: errorTextId, class: "error-text" }, errorText),
314
+ ];
315
+ }
316
+ getHintTextID() {
317
+ const { el, helperText, errorText, helperTextId, errorTextId } = this;
318
+ if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
319
+ return errorTextId;
320
+ }
321
+ if (helperText) {
322
+ return helperTextId;
323
+ }
324
+ return undefined;
310
325
  }
311
326
  renderCounter() {
312
327
  const { counter, maxlength, counterFormatter, value } = this;
@@ -359,7 +374,7 @@ export class Textarea {
359
374
  * TODO(FW-5592): Remove hasStartEndSlots condition
360
375
  */
361
376
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
362
- return (h(Host, { key: '37595a18d77dea1a337ac1c672e5f08a4128111d', class: createColorClasses(this.color, {
377
+ return (h(Host, { key: 'e8a5b5727c6a018bbd6f5507b690bc5f0959e352', class: createColorClasses(this.color, {
363
378
  [mode]: true,
364
379
  'has-value': hasValue,
365
380
  'has-focus': hasFocus,
@@ -368,7 +383,7 @@ export class Textarea {
368
383
  [`textarea-shape-${shape}`]: shape !== undefined,
369
384
  [`textarea-label-placement-${labelPlacement}`]: true,
370
385
  'textarea-disabled': disabled,
371
- }) }, h("label", { key: '67342758743e5a40448a32ff695876d39778621f', class: "textarea-wrapper", htmlFor: inputId }, this.renderLabelContainer(), h("div", { key: 'a994be8264bf5652811cf816d79a04178954e83f', class: "textarea-wrapper-inner" }, h("div", { key: 'e09c93ebcd5b3d227d51e682ca23dfc7fd7027ad', class: "start-slot-wrapper" }, h("slot", { key: 'd39758f21f19ae70aea21e9a9a7b7c20991fe593', name: "start" })), h("div", { key: '6a4e10e53de4bb235ee30def4c9ae5bdee888816', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: '9e254e551f124d972e9bc6b09ef0f2bb55890be5', 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 }, this.inheritedAttributes), value)), h("div", { key: 'a66aa2d2bc4778a0bec56a8b9ec9052a832eb3b2', class: "end-slot-wrapper" }, h("slot", { key: '8e6a90b4475de32e23afc593da4108610dcca663', name: "end" }))), shouldRenderHighlight && h("div", { key: '6da03205a8daff45581b20f0af3938634a9d5f8c', class: "textarea-highlight" })), this.renderBottomContent()));
386
+ }) }, h("label", { key: '48e889571e2d3e7e150b038e4b4b15862b418288', class: "textarea-wrapper", htmlFor: inputId }, this.renderLabelContainer(), h("div", { key: '6c6c7872ae4d351d8b8d772ad18ce3ba7a0e9a87', class: "textarea-wrapper-inner" }, h("div", { key: '03aef5dfa59af0fa78a3e5c92e0ed72396717d72', class: "start-slot-wrapper" }, h("slot", { key: '39aee9faebb0f1d10de5a5817fd9d9771b074b96', name: "start" })), h("div", { key: '738e8ff603f4c9b9083b3139db861f4b7ec20f79', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: '7763fb4f8ffe94635167331c70d21d661544c4f8', 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: 'b3af9ec0982c6dbd381afaa02df153d7e90471be', class: "end-slot-wrapper" }, h("slot", { key: '60f99bcd49a5ec2e1fa6e3e77809aefeb9d2d0d6', name: "end" }))), shouldRenderHighlight && h("div", { key: '6a7f45639ba027f4da573adae53f1197a6dec383', class: "textarea-highlight" })), this.renderBottomContent()));
372
387
  }
373
388
  static get is() { return "ion-textarea"; }
374
389
  static get encapsulation() { return "scoped"; }