@ionic/core 8.0.0-dev.11713809015.1aabd368 → 8.0.0-dev.11713893672.15a2b23e

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.
@@ -1450,7 +1450,7 @@ const Datetime = /*@__PURE__*/ proxyCustomElement(class Datetime extends HTMLEle
1450
1450
  }
1451
1451
  const activePart = this.getActivePartsWithFallback();
1452
1452
  const isDayPeriodRTL = isLocaleDayPeriodRTL(this.locale);
1453
- return (h("ion-picker-column", { "aria-label": "Select a day", style: isDayPeriodRTL ? { order: '-1' } : {}, color: this.color, disabled: disabled, value: activePart.ampm, onIonChange: (ev) => {
1453
+ return (h("ion-picker-column", { "aria-label": "Select a day period", style: isDayPeriodRTL ? { order: '-1' } : {}, color: this.color, disabled: disabled, value: activePart.ampm, onIonChange: (ev) => {
1454
1454
  const hour = calculateHourFromAMPM(workingParts, ev.detail.value);
1455
1455
  this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { ampm: ev.detail.value, hour }));
1456
1456
  this.setActiveParts(Object.assign(Object.assign({}, activePart), { ampm: ev.detail.value, hour }));
@@ -1760,7 +1760,7 @@ const Datetime = /*@__PURE__*/ proxyCustomElement(class Datetime extends HTMLEle
1760
1760
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1761
1761
  const hasWheelVariant = hasDatePresentation && preferWheel;
1762
1762
  renderHiddenInput(true, el, name, formatValue(value), disabled);
1763
- return (h(Host, { key: 'ea2ff775f17d5990cb9bb56a1372986e40b94fab', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1763
+ return (h(Host, { key: '131cd9d8f73805ea76158c0ad05d86809fe8dfce', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1764
1764
  [mode]: true,
1765
1765
  ['datetime-readonly']: readonly,
1766
1766
  ['datetime-disabled']: disabled,
@@ -1770,7 +1770,7 @@ const Datetime = /*@__PURE__*/ proxyCustomElement(class Datetime extends HTMLEle
1770
1770
  [`datetime-size-${size}`]: true,
1771
1771
  [`datetime-prefer-wheel`]: hasWheelVariant,
1772
1772
  [`datetime-grid`]: isGridStyle,
1773
- })) }, h("div", { key: 'acc8a91a57d090c0092066f543d921792b7cbed2', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1773
+ })) }, h("div", { key: '3be6976d484d9ccebac77630b2fd66388b2b85e4', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1774
1774
  }
1775
1775
  get el() { return this; }
1776
1776
  static get watchers() { return {
@@ -20,6 +20,7 @@ const PickerColumn = /*@__PURE__*/ proxyCustomElement(class PickerColumn extends
20
20
  this.isScrolling = false;
21
21
  this.isColumnVisible = false;
22
22
  this.canExitInputMode = true;
23
+ this.updateValueTextOnScroll = false;
23
24
  this.centerPickerItemInView = (target, smooth = true, canExitInputMode = true) => {
24
25
  const { isColumnVisible, scrollEl } = this;
25
26
  if (isColumnVisible && scrollEl) {
@@ -34,6 +35,7 @@ const PickerColumn = /*@__PURE__*/ proxyCustomElement(class PickerColumn extends
34
35
  * of these can cause a scroll to occur.
35
36
  */
36
37
  this.canExitInputMode = canExitInputMode;
38
+ this.updateValueTextOnScroll = false;
37
39
  scrollEl.scroll({
38
40
  top,
39
41
  left: 0,
@@ -103,9 +105,10 @@ const PickerColumn = /*@__PURE__*/ proxyCustomElement(class PickerColumn extends
103
105
  const enableHaptics = isPlatform('ios');
104
106
  const { el, scrollEl } = this;
105
107
  let timeout;
106
- let { activeItem: activeEl } = this.activeItem;
108
+ let activeEl = this.activeItem;
107
109
  const scrollCallback = () => {
108
110
  raf(() => {
111
+ var _a;
109
112
  if (!scrollEl)
110
113
  return;
111
114
  if (timeout) {
@@ -186,8 +189,23 @@ const PickerColumn = /*@__PURE__*/ proxyCustomElement(class PickerColumn extends
186
189
  }
187
190
  activeEl = newActiveElement;
188
191
  this.setPickerItemActiveState(newActiveElement, true);
192
+ /**
193
+ * Set the aria-valuetext even though the value prop has not been updated yet.
194
+ * This enables some screen readers to announce the value as the users drag
195
+ * as opposed to when their release their pointer from the screen.
196
+ *
197
+ * When the value is programmatically updated, we will smoothly scroll
198
+ * to the new option. However, we do not want to update aria-valuetext mid-scroll
199
+ * as that can cause the old value to be briefly set before being set to the
200
+ * correct option. This will cause some screen readers to announce the old value
201
+ * again before announcing the new value. The correct valuetext will be set on render.
202
+ */
203
+ if (this.updateValueTextOnScroll) {
204
+ (_a = this.assistiveFocusable) === null || _a === void 0 ? void 0 : _a.setAttribute('aria-valuetext', this.getOptionValueText(newActiveElement));
205
+ }
189
206
  timeout = setTimeout(() => {
190
207
  this.isScrolling = false;
208
+ this.updateValueTextOnScroll = true;
191
209
  enableHaptics && hapticSelectionEnd();
192
210
  /**
193
211
  * Certain tasks (such as those that
@@ -255,7 +273,7 @@ const PickerColumn = /*@__PURE__*/ proxyCustomElement(class PickerColumn extends
255
273
  * may be past the stride if the option at the stride is disabled.
256
274
  */
257
275
  this.findNextOption = (stride = 1) => {
258
- const { activeItem } = this.activeItem;
276
+ const { activeItem } = this;
259
277
  if (!activeItem)
260
278
  return null;
261
279
  let prevNode = activeItem;
@@ -282,7 +300,7 @@ const PickerColumn = /*@__PURE__*/ proxyCustomElement(class PickerColumn extends
282
300
  * may be past the stride if the option at the stride is disabled.
283
301
  */
284
302
  this.findPreviousOption = (stride = 1) => {
285
- const { activeItem } = this.activeItem;
303
+ const { activeItem } = this;
286
304
  if (!activeItem)
287
305
  return null;
288
306
  let nextNode = activeItem;
@@ -336,6 +354,13 @@ const PickerColumn = /*@__PURE__*/ proxyCustomElement(class PickerColumn extends
336
354
  ev.preventDefault();
337
355
  }
338
356
  };
357
+ /**
358
+ * Utility to generate the correct text for aria-valuetext.
359
+ */
360
+ this.getOptionValueText = (el) => {
361
+ var _a;
362
+ return el ? (_a = el.getAttribute('aria-label')) !== null && _a !== void 0 ? _a : el.innerText : '';
363
+ };
339
364
  /**
340
365
  * Render an element that overlays the column. This element is for assistive
341
366
  * tech to allow users to navigate the column up/down. This element should receive
@@ -343,11 +368,15 @@ const PickerColumn = /*@__PURE__*/ proxyCustomElement(class PickerColumn extends
343
368
  * slider role: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/slider_role
344
369
  */
345
370
  this.renderAssistiveFocusable = () => {
346
- var _a;
347
- const { activeItem, activeItemIndex } = this.activeItem;
348
- const { el } = this;
349
- const valueText = activeItem ? (_a = activeItem.getAttribute('aria-label')) !== null && _a !== void 0 ? _a : activeItem.innerText : '';
350
- return (h("div", { class: "assistive-focusable", role: "slider", tabindex: this.disabled ? undefined : 0, "aria-label": this.ariaLabel, "aria-valuemin": 0, "aria-valuemax": el.childElementCount - 1, "aria-valuenow": activeItemIndex >= 0 ? activeItemIndex : 0, "aria-valuetext": valueText, "aria-orientation": "vertical", onKeyDown: (ev) => this.onKeyDown(ev) }));
371
+ const { activeItem } = this;
372
+ const valueText = this.getOptionValueText(activeItem);
373
+ /**
374
+ * When using the picker, the valuetext provides important context that valuenow
375
+ * does not. Additionally, using non-zero valuemin/valuemax values can cause
376
+ * WebKit to incorrectly announce numeric valuetext values (such as a year
377
+ * like "2024") as percentages: https://bugs.webkit.org/show_bug.cgi?id=273126
378
+ */
379
+ return (h("div", { ref: (el) => (this.assistiveFocusable = el), class: "assistive-focusable", role: "slider", tabindex: this.disabled ? undefined : 0, "aria-label": this.ariaLabel, "aria-valuemin": 0, "aria-valuemax": 0, "aria-valuenow": 0, "aria-valuetext": valueText, "aria-orientation": "vertical", onKeyDown: (ev) => this.onKeyDown(ev) }));
351
380
  };
352
381
  this.ariaLabel = null;
353
382
  this.isActive = false;
@@ -389,8 +418,7 @@ const PickerColumn = /*@__PURE__*/ proxyCustomElement(class PickerColumn extends
389
418
  */
390
419
  const ev = entries[entries.length - 1];
391
420
  if (ev.isIntersecting) {
392
- const { activeItem } = this.activeItem;
393
- const { el } = this;
421
+ const { activeItem, el } = this;
394
422
  this.isColumnVisible = true;
395
423
  /**
396
424
  * Because this initial call to scrollActiveItemIntoView has to fire before
@@ -430,8 +458,7 @@ const PickerColumn = /*@__PURE__*/ proxyCustomElement(class PickerColumn extends
430
458
  }
431
459
  }
432
460
  componentDidRender() {
433
- const { activeItem } = this.activeItem;
434
- const { el, isColumnVisible, value } = this;
461
+ const { el, activeItem, isColumnVisible, value } = this;
435
462
  if (isColumnVisible && !activeItem) {
436
463
  const firstOption = el.querySelector('ion-picker-column-option');
437
464
  /**
@@ -448,7 +475,7 @@ const PickerColumn = /*@__PURE__*/ proxyCustomElement(class PickerColumn extends
448
475
  }
449
476
  /** @internal */
450
477
  async scrollActiveItemIntoView(smooth = false) {
451
- const { activeItem: activeEl } = this.activeItem;
478
+ const activeEl = this.activeItem;
452
479
  if (activeEl) {
453
480
  this.centerPickerItemInView(activeEl, smooth, false);
454
481
  }
@@ -482,33 +509,29 @@ const PickerColumn = /*@__PURE__*/ proxyCustomElement(class PickerColumn extends
482
509
  }
483
510
  get activeItem() {
484
511
  const { value } = this;
485
- const options = this.el.querySelectorAll('ion-picker-column-option');
486
- for (let i = 0; i <= options.length - 1; i++) {
487
- const option = options[i];
512
+ const options = Array.from(this.el.querySelectorAll('ion-picker-column-option'));
513
+ return options.find((option) => {
488
514
  /**
489
515
  * If the whole picker column is disabled, the current value should appear active
490
516
  * If the current value item is specifically disabled, it should not appear active
491
517
  */
492
- if (option.value === value && !option.disabled) {
493
- return {
494
- activeItem: option,
495
- activeItemIndex: i,
496
- };
518
+ if (!this.disabled && option.disabled) {
519
+ return false;
497
520
  }
498
- }
499
- return { activeItem: undefined, activeItemIndex: -1 };
521
+ return option.value === value;
522
+ });
500
523
  }
501
524
  render() {
502
525
  const { color, disabled, isActive, numericInput } = this;
503
526
  const mode = getIonMode(this);
504
- return (h(Host, { key: '87534568ba47096dca1be00fee184052767087f5', class: createColorClasses(color, {
527
+ return (h(Host, { key: '50fe50a49f88aa9ec435436083f3cd1ef49c8c9c', class: createColorClasses(color, {
505
528
  [mode]: true,
506
529
  ['picker-column-active']: isActive,
507
530
  ['picker-column-numeric-input']: numericInput,
508
531
  ['picker-column-disabled']: disabled,
509
- }) }, this.renderAssistiveFocusable(), h("slot", { key: '8dedc4f294f1a6658a12151a765f6fa790f4d854', name: "prefix" }), h("div", { key: 'c82975dc3996d3ddb8dee12ad381ee733466a069', "aria-hidden": "true", class: "picker-opts", ref: (el) => {
532
+ }) }, this.renderAssistiveFocusable(), h("slot", { key: '6350829748690895ab3ef101b9f2ae993852de7d', name: "prefix" }), h("div", { key: 'eaff9925aa8553f63ba2a1ae2bdfaa02963e0a5d', "aria-hidden": "true", class: "picker-opts", ref: (el) => {
510
533
  this.scrollEl = el;
511
- } }, h("div", { key: '2de49979f9339f4a9a384e115ea5a073b6467e50', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '304e0bcc506f4c4d35d053e95c85de9c9bdc303c', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '3da0eb1d7e1d8b196112f7d695447b40716eef1d', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("slot", { key: '43668a05c3eb39749bde1137e33872e1b406cba9' }), h("div", { key: '95d4ae922ad45aa541a70b08958b08a2d3c79fbe', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '412aca51c2d3b4cc1bcc1fffca68922db64f7745', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '2319fa61c7649dec3efa2ec2e7b93d5e9a775708', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0")), h("slot", { key: '010cee6ffdbe1a6d5331cb4e185b1aa0b6effd2d', name: "suffix" })));
534
+ } }, h("div", { key: '25a24c4174782f2ec8a1fe06cbaf1f564b1f632f', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '7f4e9a5f1f74f7e8d5ad74178894edc80c0fb523', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '329b88b4af1d0e37da347e5f3fe9b462efcc2886', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("slot", { key: '82b9d5d541d41801bce06db0867d6cfbcee4ac3e' }), h("div", { key: '288ae193601d1eb49ead06f79eb1affa478f31d3', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '29661d0b74d11d650c2b23be66f857dd008dc5a0', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: 'a7bd71b4abcb027654a828ffa65d87b6874d7484', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0")), h("slot", { key: '83ddd19d09ec48fe31b51f9f272a562320f90098', name: "suffix" })));
512
535
  }
513
536
  get el() { return this; }
514
537
  static get watchers() { return {
@@ -1452,7 +1452,7 @@ const Datetime = class {
1452
1452
  }
1453
1453
  const activePart = this.getActivePartsWithFallback();
1454
1454
  const isDayPeriodRTL = data.isLocaleDayPeriodRTL(this.locale);
1455
- return (index$1.h("ion-picker-column", { "aria-label": "Select a day", style: isDayPeriodRTL ? { order: '-1' } : {}, color: this.color, disabled: disabled, value: activePart.ampm, onIonChange: (ev) => {
1455
+ return (index$1.h("ion-picker-column", { "aria-label": "Select a day period", style: isDayPeriodRTL ? { order: '-1' } : {}, color: this.color, disabled: disabled, value: activePart.ampm, onIonChange: (ev) => {
1456
1456
  const hour = data.calculateHourFromAMPM(workingParts, ev.detail.value);
1457
1457
  this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { ampm: ev.detail.value, hour }));
1458
1458
  this.setActiveParts(Object.assign(Object.assign({}, activePart), { ampm: ev.detail.value, hour }));
@@ -1762,7 +1762,7 @@ const Datetime = class {
1762
1762
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1763
1763
  const hasWheelVariant = hasDatePresentation && preferWheel;
1764
1764
  helpers.renderHiddenInput(true, el, name, data.formatValue(value), disabled);
1765
- return (index$1.h(index$1.Host, { key: 'ea2ff775f17d5990cb9bb56a1372986e40b94fab', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, theme.createColorClasses(color, {
1765
+ return (index$1.h(index$1.Host, { key: '131cd9d8f73805ea76158c0ad05d86809fe8dfce', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, theme.createColorClasses(color, {
1766
1766
  [mode]: true,
1767
1767
  ['datetime-readonly']: readonly,
1768
1768
  ['datetime-disabled']: disabled,
@@ -1772,7 +1772,7 @@ const Datetime = class {
1772
1772
  [`datetime-size-${size}`]: true,
1773
1773
  [`datetime-prefer-wheel`]: hasWheelVariant,
1774
1774
  [`datetime-grid`]: isGridStyle,
1775
- })) }, index$1.h("div", { key: 'acc8a91a57d090c0092066f543d921792b7cbed2', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1775
+ })) }, index$1.h("div", { key: '3be6976d484d9ccebac77630b2fd66388b2b85e4', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1776
1776
  }
1777
1777
  get el() { return index$1.getElement(this); }
1778
1778
  static get watchers() { return {
@@ -23,6 +23,7 @@ const PickerColumn = class {
23
23
  this.isScrolling = false;
24
24
  this.isColumnVisible = false;
25
25
  this.canExitInputMode = true;
26
+ this.updateValueTextOnScroll = false;
26
27
  this.centerPickerItemInView = (target, smooth = true, canExitInputMode = true) => {
27
28
  const { isColumnVisible, scrollEl } = this;
28
29
  if (isColumnVisible && scrollEl) {
@@ -37,6 +38,7 @@ const PickerColumn = class {
37
38
  * of these can cause a scroll to occur.
38
39
  */
39
40
  this.canExitInputMode = canExitInputMode;
41
+ this.updateValueTextOnScroll = false;
40
42
  scrollEl.scroll({
41
43
  top,
42
44
  left: 0,
@@ -106,9 +108,10 @@ const PickerColumn = class {
106
108
  const enableHaptics = ionicGlobal.isPlatform('ios');
107
109
  const { el, scrollEl } = this;
108
110
  let timeout;
109
- let { activeItem: activeEl } = this.activeItem;
111
+ let activeEl = this.activeItem;
110
112
  const scrollCallback = () => {
111
113
  helpers.raf(() => {
114
+ var _a;
112
115
  if (!scrollEl)
113
116
  return;
114
117
  if (timeout) {
@@ -189,8 +192,23 @@ const PickerColumn = class {
189
192
  }
190
193
  activeEl = newActiveElement;
191
194
  this.setPickerItemActiveState(newActiveElement, true);
195
+ /**
196
+ * Set the aria-valuetext even though the value prop has not been updated yet.
197
+ * This enables some screen readers to announce the value as the users drag
198
+ * as opposed to when their release their pointer from the screen.
199
+ *
200
+ * When the value is programmatically updated, we will smoothly scroll
201
+ * to the new option. However, we do not want to update aria-valuetext mid-scroll
202
+ * as that can cause the old value to be briefly set before being set to the
203
+ * correct option. This will cause some screen readers to announce the old value
204
+ * again before announcing the new value. The correct valuetext will be set on render.
205
+ */
206
+ if (this.updateValueTextOnScroll) {
207
+ (_a = this.assistiveFocusable) === null || _a === void 0 ? void 0 : _a.setAttribute('aria-valuetext', this.getOptionValueText(newActiveElement));
208
+ }
192
209
  timeout = setTimeout(() => {
193
210
  this.isScrolling = false;
211
+ this.updateValueTextOnScroll = true;
194
212
  enableHaptics && haptic.hapticSelectionEnd();
195
213
  /**
196
214
  * Certain tasks (such as those that
@@ -258,7 +276,7 @@ const PickerColumn = class {
258
276
  * may be past the stride if the option at the stride is disabled.
259
277
  */
260
278
  this.findNextOption = (stride = 1) => {
261
- const { activeItem } = this.activeItem;
279
+ const { activeItem } = this;
262
280
  if (!activeItem)
263
281
  return null;
264
282
  let prevNode = activeItem;
@@ -285,7 +303,7 @@ const PickerColumn = class {
285
303
  * may be past the stride if the option at the stride is disabled.
286
304
  */
287
305
  this.findPreviousOption = (stride = 1) => {
288
- const { activeItem } = this.activeItem;
306
+ const { activeItem } = this;
289
307
  if (!activeItem)
290
308
  return null;
291
309
  let nextNode = activeItem;
@@ -339,6 +357,13 @@ const PickerColumn = class {
339
357
  ev.preventDefault();
340
358
  }
341
359
  };
360
+ /**
361
+ * Utility to generate the correct text for aria-valuetext.
362
+ */
363
+ this.getOptionValueText = (el) => {
364
+ var _a;
365
+ return el ? (_a = el.getAttribute('aria-label')) !== null && _a !== void 0 ? _a : el.innerText : '';
366
+ };
342
367
  /**
343
368
  * Render an element that overlays the column. This element is for assistive
344
369
  * tech to allow users to navigate the column up/down. This element should receive
@@ -346,11 +371,15 @@ const PickerColumn = class {
346
371
  * slider role: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/slider_role
347
372
  */
348
373
  this.renderAssistiveFocusable = () => {
349
- var _a;
350
- const { activeItem, activeItemIndex } = this.activeItem;
351
- const { el } = this;
352
- const valueText = activeItem ? (_a = activeItem.getAttribute('aria-label')) !== null && _a !== void 0 ? _a : activeItem.innerText : '';
353
- return (index.h("div", { class: "assistive-focusable", role: "slider", tabindex: this.disabled ? undefined : 0, "aria-label": this.ariaLabel, "aria-valuemin": 0, "aria-valuemax": el.childElementCount - 1, "aria-valuenow": activeItemIndex >= 0 ? activeItemIndex : 0, "aria-valuetext": valueText, "aria-orientation": "vertical", onKeyDown: (ev) => this.onKeyDown(ev) }));
374
+ const { activeItem } = this;
375
+ const valueText = this.getOptionValueText(activeItem);
376
+ /**
377
+ * When using the picker, the valuetext provides important context that valuenow
378
+ * does not. Additionally, using non-zero valuemin/valuemax values can cause
379
+ * WebKit to incorrectly announce numeric valuetext values (such as a year
380
+ * like "2024") as percentages: https://bugs.webkit.org/show_bug.cgi?id=273126
381
+ */
382
+ return (index.h("div", { ref: (el) => (this.assistiveFocusable = el), class: "assistive-focusable", role: "slider", tabindex: this.disabled ? undefined : 0, "aria-label": this.ariaLabel, "aria-valuemin": 0, "aria-valuemax": 0, "aria-valuenow": 0, "aria-valuetext": valueText, "aria-orientation": "vertical", onKeyDown: (ev) => this.onKeyDown(ev) }));
354
383
  };
355
384
  this.ariaLabel = null;
356
385
  this.isActive = false;
@@ -392,8 +421,7 @@ const PickerColumn = class {
392
421
  */
393
422
  const ev = entries[entries.length - 1];
394
423
  if (ev.isIntersecting) {
395
- const { activeItem } = this.activeItem;
396
- const { el } = this;
424
+ const { activeItem, el } = this;
397
425
  this.isColumnVisible = true;
398
426
  /**
399
427
  * Because this initial call to scrollActiveItemIntoView has to fire before
@@ -433,8 +461,7 @@ const PickerColumn = class {
433
461
  }
434
462
  }
435
463
  componentDidRender() {
436
- const { activeItem } = this.activeItem;
437
- const { el, isColumnVisible, value } = this;
464
+ const { el, activeItem, isColumnVisible, value } = this;
438
465
  if (isColumnVisible && !activeItem) {
439
466
  const firstOption = el.querySelector('ion-picker-column-option');
440
467
  /**
@@ -451,7 +478,7 @@ const PickerColumn = class {
451
478
  }
452
479
  /** @internal */
453
480
  async scrollActiveItemIntoView(smooth = false) {
454
- const { activeItem: activeEl } = this.activeItem;
481
+ const activeEl = this.activeItem;
455
482
  if (activeEl) {
456
483
  this.centerPickerItemInView(activeEl, smooth, false);
457
484
  }
@@ -485,33 +512,29 @@ const PickerColumn = class {
485
512
  }
486
513
  get activeItem() {
487
514
  const { value } = this;
488
- const options = this.el.querySelectorAll('ion-picker-column-option');
489
- for (let i = 0; i <= options.length - 1; i++) {
490
- const option = options[i];
515
+ const options = Array.from(this.el.querySelectorAll('ion-picker-column-option'));
516
+ return options.find((option) => {
491
517
  /**
492
518
  * If the whole picker column is disabled, the current value should appear active
493
519
  * If the current value item is specifically disabled, it should not appear active
494
520
  */
495
- if (option.value === value && !option.disabled) {
496
- return {
497
- activeItem: option,
498
- activeItemIndex: i,
499
- };
521
+ if (!this.disabled && option.disabled) {
522
+ return false;
500
523
  }
501
- }
502
- return { activeItem: undefined, activeItemIndex: -1 };
524
+ return option.value === value;
525
+ });
503
526
  }
504
527
  render() {
505
528
  const { color, disabled, isActive, numericInput } = this;
506
529
  const mode = ionicGlobal.getIonMode(this);
507
- return (index.h(index.Host, { key: '87534568ba47096dca1be00fee184052767087f5', class: theme.createColorClasses(color, {
530
+ return (index.h(index.Host, { key: '50fe50a49f88aa9ec435436083f3cd1ef49c8c9c', class: theme.createColorClasses(color, {
508
531
  [mode]: true,
509
532
  ['picker-column-active']: isActive,
510
533
  ['picker-column-numeric-input']: numericInput,
511
534
  ['picker-column-disabled']: disabled,
512
- }) }, this.renderAssistiveFocusable(), index.h("slot", { key: '8dedc4f294f1a6658a12151a765f6fa790f4d854', name: "prefix" }), index.h("div", { key: 'c82975dc3996d3ddb8dee12ad381ee733466a069', "aria-hidden": "true", class: "picker-opts", ref: (el) => {
535
+ }) }, this.renderAssistiveFocusable(), index.h("slot", { key: '6350829748690895ab3ef101b9f2ae993852de7d', name: "prefix" }), index.h("div", { key: 'eaff9925aa8553f63ba2a1ae2bdfaa02963e0a5d', "aria-hidden": "true", class: "picker-opts", ref: (el) => {
513
536
  this.scrollEl = el;
514
- } }, index.h("div", { key: '2de49979f9339f4a9a384e115ea5a073b6467e50', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '304e0bcc506f4c4d35d053e95c85de9c9bdc303c', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '3da0eb1d7e1d8b196112f7d695447b40716eef1d', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("slot", { key: '43668a05c3eb39749bde1137e33872e1b406cba9' }), index.h("div", { key: '95d4ae922ad45aa541a70b08958b08a2d3c79fbe', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '412aca51c2d3b4cc1bcc1fffca68922db64f7745', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '2319fa61c7649dec3efa2ec2e7b93d5e9a775708', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0")), index.h("slot", { key: '010cee6ffdbe1a6d5331cb4e185b1aa0b6effd2d', name: "suffix" })));
537
+ } }, index.h("div", { key: '25a24c4174782f2ec8a1fe06cbaf1f564b1f632f', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '7f4e9a5f1f74f7e8d5ad74178894edc80c0fb523', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '329b88b4af1d0e37da347e5f3fe9b462efcc2886', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("slot", { key: '82b9d5d541d41801bce06db0867d6cfbcee4ac3e' }), index.h("div", { key: '288ae193601d1eb49ead06f79eb1affa478f31d3', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: '29661d0b74d11d650c2b23be66f857dd008dc5a0', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), index.h("div", { key: 'a7bd71b4abcb027654a828ffa65d87b6874d7484', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0")), index.h("slot", { key: '83ddd19d09ec48fe31b51f9f272a562320f90098', name: "suffix" })));
515
538
  }
516
539
  get el() { return index.getElement(this); }
517
540
  static get watchers() { return {
@@ -1246,7 +1246,7 @@ export class Datetime {
1246
1246
  }
1247
1247
  const activePart = this.getActivePartsWithFallback();
1248
1248
  const isDayPeriodRTL = isLocaleDayPeriodRTL(this.locale);
1249
- return (h("ion-picker-column", { "aria-label": "Select a day", style: isDayPeriodRTL ? { order: '-1' } : {}, color: this.color, disabled: disabled, value: activePart.ampm, onIonChange: (ev) => {
1249
+ return (h("ion-picker-column", { "aria-label": "Select a day period", style: isDayPeriodRTL ? { order: '-1' } : {}, color: this.color, disabled: disabled, value: activePart.ampm, onIonChange: (ev) => {
1250
1250
  const hour = calculateHourFromAMPM(workingParts, ev.detail.value);
1251
1251
  this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { ampm: ev.detail.value, hour }));
1252
1252
  this.setActiveParts(Object.assign(Object.assign({}, activePart), { ampm: ev.detail.value, hour }));
@@ -1556,7 +1556,7 @@ export class Datetime {
1556
1556
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1557
1557
  const hasWheelVariant = hasDatePresentation && preferWheel;
1558
1558
  renderHiddenInput(true, el, name, formatValue(value), disabled);
1559
- return (h(Host, { key: 'ea2ff775f17d5990cb9bb56a1372986e40b94fab', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1559
+ return (h(Host, { key: '131cd9d8f73805ea76158c0ad05d86809fe8dfce', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1560
1560
  [mode]: true,
1561
1561
  ['datetime-readonly']: readonly,
1562
1562
  ['datetime-disabled']: disabled,
@@ -1566,7 +1566,7 @@ export class Datetime {
1566
1566
  [`datetime-size-${size}`]: true,
1567
1567
  [`datetime-prefer-wheel`]: hasWheelVariant,
1568
1568
  [`datetime-grid`]: isGridStyle,
1569
- })) }, h("div", { key: 'acc8a91a57d090c0092066f543d921792b7cbed2', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1569
+ })) }, h("div", { key: '3be6976d484d9ccebac77630b2fd66388b2b85e4', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1570
1570
  }
1571
1571
  static get is() { return "ion-datetime"; }
1572
1572
  static get encapsulation() { return "shadow"; }
@@ -19,6 +19,7 @@ export class PickerColumn {
19
19
  this.isScrolling = false;
20
20
  this.isColumnVisible = false;
21
21
  this.canExitInputMode = true;
22
+ this.updateValueTextOnScroll = false;
22
23
  this.centerPickerItemInView = (target, smooth = true, canExitInputMode = true) => {
23
24
  const { isColumnVisible, scrollEl } = this;
24
25
  if (isColumnVisible && scrollEl) {
@@ -33,6 +34,7 @@ export class PickerColumn {
33
34
  * of these can cause a scroll to occur.
34
35
  */
35
36
  this.canExitInputMode = canExitInputMode;
37
+ this.updateValueTextOnScroll = false;
36
38
  scrollEl.scroll({
37
39
  top,
38
40
  left: 0,
@@ -102,9 +104,10 @@ export class PickerColumn {
102
104
  const enableHaptics = isPlatform('ios');
103
105
  const { el, scrollEl } = this;
104
106
  let timeout;
105
- let { activeItem: activeEl } = this.activeItem;
107
+ let activeEl = this.activeItem;
106
108
  const scrollCallback = () => {
107
109
  raf(() => {
110
+ var _a;
108
111
  if (!scrollEl)
109
112
  return;
110
113
  if (timeout) {
@@ -185,8 +188,23 @@ export class PickerColumn {
185
188
  }
186
189
  activeEl = newActiveElement;
187
190
  this.setPickerItemActiveState(newActiveElement, true);
191
+ /**
192
+ * Set the aria-valuetext even though the value prop has not been updated yet.
193
+ * This enables some screen readers to announce the value as the users drag
194
+ * as opposed to when their release their pointer from the screen.
195
+ *
196
+ * When the value is programmatically updated, we will smoothly scroll
197
+ * to the new option. However, we do not want to update aria-valuetext mid-scroll
198
+ * as that can cause the old value to be briefly set before being set to the
199
+ * correct option. This will cause some screen readers to announce the old value
200
+ * again before announcing the new value. The correct valuetext will be set on render.
201
+ */
202
+ if (this.updateValueTextOnScroll) {
203
+ (_a = this.assistiveFocusable) === null || _a === void 0 ? void 0 : _a.setAttribute('aria-valuetext', this.getOptionValueText(newActiveElement));
204
+ }
188
205
  timeout = setTimeout(() => {
189
206
  this.isScrolling = false;
207
+ this.updateValueTextOnScroll = true;
190
208
  enableHaptics && hapticSelectionEnd();
191
209
  /**
192
210
  * Certain tasks (such as those that
@@ -254,7 +272,7 @@ export class PickerColumn {
254
272
  * may be past the stride if the option at the stride is disabled.
255
273
  */
256
274
  this.findNextOption = (stride = 1) => {
257
- const { activeItem } = this.activeItem;
275
+ const { activeItem } = this;
258
276
  if (!activeItem)
259
277
  return null;
260
278
  let prevNode = activeItem;
@@ -281,7 +299,7 @@ export class PickerColumn {
281
299
  * may be past the stride if the option at the stride is disabled.
282
300
  */
283
301
  this.findPreviousOption = (stride = 1) => {
284
- const { activeItem } = this.activeItem;
302
+ const { activeItem } = this;
285
303
  if (!activeItem)
286
304
  return null;
287
305
  let nextNode = activeItem;
@@ -337,6 +355,13 @@ export class PickerColumn {
337
355
  ev.preventDefault();
338
356
  }
339
357
  };
358
+ /**
359
+ * Utility to generate the correct text for aria-valuetext.
360
+ */
361
+ this.getOptionValueText = (el) => {
362
+ var _a;
363
+ return el ? (_a = el.getAttribute('aria-label')) !== null && _a !== void 0 ? _a : el.innerText : '';
364
+ };
340
365
  /**
341
366
  * Render an element that overlays the column. This element is for assistive
342
367
  * tech to allow users to navigate the column up/down. This element should receive
@@ -344,11 +369,15 @@ export class PickerColumn {
344
369
  * slider role: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/slider_role
345
370
  */
346
371
  this.renderAssistiveFocusable = () => {
347
- var _a;
348
- const { activeItem, activeItemIndex } = this.activeItem;
349
- const { el } = this;
350
- const valueText = activeItem ? (_a = activeItem.getAttribute('aria-label')) !== null && _a !== void 0 ? _a : activeItem.innerText : '';
351
- return (h("div", { class: "assistive-focusable", role: "slider", tabindex: this.disabled ? undefined : 0, "aria-label": this.ariaLabel, "aria-valuemin": 0, "aria-valuemax": el.childElementCount - 1, "aria-valuenow": activeItemIndex >= 0 ? activeItemIndex : 0, "aria-valuetext": valueText, "aria-orientation": "vertical", onKeyDown: (ev) => this.onKeyDown(ev) }));
372
+ const { activeItem } = this;
373
+ const valueText = this.getOptionValueText(activeItem);
374
+ /**
375
+ * When using the picker, the valuetext provides important context that valuenow
376
+ * does not. Additionally, using non-zero valuemin/valuemax values can cause
377
+ * WebKit to incorrectly announce numeric valuetext values (such as a year
378
+ * like "2024") as percentages: https://bugs.webkit.org/show_bug.cgi?id=273126
379
+ */
380
+ return (h("div", { ref: (el) => (this.assistiveFocusable = el), class: "assistive-focusable", role: "slider", tabindex: this.disabled ? undefined : 0, "aria-label": this.ariaLabel, "aria-valuemin": 0, "aria-valuemax": 0, "aria-valuenow": 0, "aria-valuetext": valueText, "aria-orientation": "vertical", onKeyDown: (ev) => this.onKeyDown(ev) }));
352
381
  };
353
382
  this.ariaLabel = null;
354
383
  this.isActive = false;
@@ -390,8 +419,7 @@ export class PickerColumn {
390
419
  */
391
420
  const ev = entries[entries.length - 1];
392
421
  if (ev.isIntersecting) {
393
- const { activeItem } = this.activeItem;
394
- const { el } = this;
422
+ const { activeItem, el } = this;
395
423
  this.isColumnVisible = true;
396
424
  /**
397
425
  * Because this initial call to scrollActiveItemIntoView has to fire before
@@ -431,8 +459,7 @@ export class PickerColumn {
431
459
  }
432
460
  }
433
461
  componentDidRender() {
434
- const { activeItem } = this.activeItem;
435
- const { el, isColumnVisible, value } = this;
462
+ const { el, activeItem, isColumnVisible, value } = this;
436
463
  if (isColumnVisible && !activeItem) {
437
464
  const firstOption = el.querySelector('ion-picker-column-option');
438
465
  /**
@@ -449,7 +476,7 @@ export class PickerColumn {
449
476
  }
450
477
  /** @internal */
451
478
  async scrollActiveItemIntoView(smooth = false) {
452
- const { activeItem: activeEl } = this.activeItem;
479
+ const activeEl = this.activeItem;
453
480
  if (activeEl) {
454
481
  this.centerPickerItemInView(activeEl, smooth, false);
455
482
  }
@@ -483,33 +510,29 @@ export class PickerColumn {
483
510
  }
484
511
  get activeItem() {
485
512
  const { value } = this;
486
- const options = this.el.querySelectorAll('ion-picker-column-option');
487
- for (let i = 0; i <= options.length - 1; i++) {
488
- const option = options[i];
513
+ const options = Array.from(this.el.querySelectorAll('ion-picker-column-option'));
514
+ return options.find((option) => {
489
515
  /**
490
516
  * If the whole picker column is disabled, the current value should appear active
491
517
  * If the current value item is specifically disabled, it should not appear active
492
518
  */
493
- if (option.value === value && !option.disabled) {
494
- return {
495
- activeItem: option,
496
- activeItemIndex: i,
497
- };
519
+ if (!this.disabled && option.disabled) {
520
+ return false;
498
521
  }
499
- }
500
- return { activeItem: undefined, activeItemIndex: -1 };
522
+ return option.value === value;
523
+ });
501
524
  }
502
525
  render() {
503
526
  const { color, disabled, isActive, numericInput } = this;
504
527
  const mode = getIonMode(this);
505
- return (h(Host, { key: '87534568ba47096dca1be00fee184052767087f5', class: createColorClasses(color, {
528
+ return (h(Host, { key: '50fe50a49f88aa9ec435436083f3cd1ef49c8c9c', class: createColorClasses(color, {
506
529
  [mode]: true,
507
530
  ['picker-column-active']: isActive,
508
531
  ['picker-column-numeric-input']: numericInput,
509
532
  ['picker-column-disabled']: disabled,
510
- }) }, this.renderAssistiveFocusable(), h("slot", { key: '8dedc4f294f1a6658a12151a765f6fa790f4d854', name: "prefix" }), h("div", { key: 'c82975dc3996d3ddb8dee12ad381ee733466a069', "aria-hidden": "true", class: "picker-opts", ref: (el) => {
533
+ }) }, this.renderAssistiveFocusable(), h("slot", { key: '6350829748690895ab3ef101b9f2ae993852de7d', name: "prefix" }), h("div", { key: 'eaff9925aa8553f63ba2a1ae2bdfaa02963e0a5d', "aria-hidden": "true", class: "picker-opts", ref: (el) => {
511
534
  this.scrollEl = el;
512
- } }, h("div", { key: '2de49979f9339f4a9a384e115ea5a073b6467e50', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '304e0bcc506f4c4d35d053e95c85de9c9bdc303c', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '3da0eb1d7e1d8b196112f7d695447b40716eef1d', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("slot", { key: '43668a05c3eb39749bde1137e33872e1b406cba9' }), h("div", { key: '95d4ae922ad45aa541a70b08958b08a2d3c79fbe', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '412aca51c2d3b4cc1bcc1fffca68922db64f7745', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '2319fa61c7649dec3efa2ec2e7b93d5e9a775708', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0")), h("slot", { key: '010cee6ffdbe1a6d5331cb4e185b1aa0b6effd2d', name: "suffix" })));
535
+ } }, h("div", { key: '25a24c4174782f2ec8a1fe06cbaf1f564b1f632f', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '7f4e9a5f1f74f7e8d5ad74178894edc80c0fb523', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '329b88b4af1d0e37da347e5f3fe9b462efcc2886', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("slot", { key: '82b9d5d541d41801bce06db0867d6cfbcee4ac3e' }), h("div", { key: '288ae193601d1eb49ead06f79eb1affa478f31d3', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '29661d0b74d11d650c2b23be66f857dd008dc5a0', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: 'a7bd71b4abcb027654a828ffa65d87b6874d7484', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0")), h("slot", { key: '83ddd19d09ec48fe31b51f9f272a562320f90098', name: "suffix" })));
513
536
  }
514
537
  static get is() { return "ion-picker-column"; }
515
538
  static get encapsulation() { return "shadow"; }