voyager-ionic-core 8.8.2 → 8.8.5

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 (103) hide show
  1. package/components/ion-action-sheet.js +1 -1
  2. package/components/ion-checkbox.js +1 -1
  3. package/components/ion-content.js +1 -1
  4. package/components/ion-datetime.js +1 -1
  5. package/components/ion-input-otp.js +1 -1
  6. package/components/ion-modal.js +1 -1
  7. package/components/ion-picker-column.js +1 -1
  8. package/components/ion-radio-group.js +1 -1
  9. package/components/ion-select-modal.js +1 -1
  10. package/components/ion-select-popover.js +1 -1
  11. package/components/ion-select.js +1 -1
  12. package/components/p-0z8QSI5b.js +4 -0
  13. package/components/{p-ApmKVjaE.js → p-BGHGpkPX.js} +1 -1
  14. package/components/p-BlNv564p.js +4 -0
  15. package/components/p-D-cP12ZN.js +4 -0
  16. package/components/p-D3Ti70Hx.js +4 -0
  17. package/components/{p-Bk2zuNWT.js → p-DvOO1fxp.js} +1 -1
  18. package/components/p-FBcnjE5W.js +4 -0
  19. package/components/p-SBseW5KJ.js +4 -0
  20. package/css/palettes/dark.always.css +1 -1
  21. package/css/palettes/dark.always.css.map +1 -1
  22. package/css/palettes/dark.class.css +1 -1
  23. package/css/palettes/dark.class.css.map +1 -1
  24. package/css/palettes/dark.system.css +1 -1
  25. package/css/palettes/dark.system.css.map +1 -1
  26. package/css/palettes/high-contrast-dark.always.css +1 -1
  27. package/css/palettes/high-contrast-dark.always.css.map +1 -1
  28. package/css/palettes/high-contrast-dark.class.css +1 -1
  29. package/css/palettes/high-contrast-dark.class.css.map +1 -1
  30. package/css/palettes/high-contrast-dark.system.css +1 -1
  31. package/css/palettes/high-contrast-dark.system.css.map +1 -1
  32. package/dist/cjs/ion-action-sheet.cjs.entry.js +4 -4
  33. package/dist/cjs/ion-app_8.cjs.entry.js +1 -1
  34. package/dist/cjs/ion-checkbox.cjs.entry.js +39 -32
  35. package/dist/cjs/ion-datetime_3.cjs.entry.js +17 -6
  36. package/dist/cjs/ion-input-otp.cjs.entry.js +21 -6
  37. package/dist/cjs/ion-modal.cjs.entry.js +99 -45
  38. package/dist/cjs/ion-picker-column.cjs.entry.js +4 -4
  39. package/dist/cjs/ion-radio_2.cjs.entry.js +13 -1
  40. package/dist/cjs/ion-select-modal.cjs.entry.js +18 -7
  41. package/dist/cjs/ion-select_3.cjs.entry.js +18 -7
  42. package/dist/collection/components/action-sheet/action-sheet.js +4 -4
  43. package/dist/collection/components/checkbox/checkbox.js +39 -32
  44. package/dist/collection/components/content/content.css +1 -1
  45. package/dist/collection/components/datetime/datetime.js +17 -6
  46. package/dist/collection/components/input-otp/input-otp.js +21 -6
  47. package/dist/collection/components/modal/modal.js +73 -44
  48. package/dist/collection/components/modal/safe-area-utils.js +27 -2
  49. package/dist/collection/components/picker-column/picker-column.js +5 -5
  50. package/dist/collection/components/radio-group/radio-group.js +13 -1
  51. package/dist/collection/components/radio-group/test/fixtures.js +2 -2
  52. package/dist/collection/components/select-modal/select-modal.js +18 -7
  53. package/dist/collection/components/select-modal/test/fixtures.js +4 -0
  54. package/dist/collection/components/select-popover/select-popover.js +18 -7
  55. package/dist/collection/components/select-popover/test/fixtures.js +4 -0
  56. package/dist/docs.json +1 -1
  57. package/dist/esm/ion-action-sheet.entry.js +4 -4
  58. package/dist/esm/ion-app_8.entry.js +1 -1
  59. package/dist/esm/ion-checkbox.entry.js +39 -32
  60. package/dist/esm/ion-datetime_3.entry.js +17 -6
  61. package/dist/esm/ion-input-otp.entry.js +21 -6
  62. package/dist/esm/ion-modal.entry.js +99 -45
  63. package/dist/esm/ion-picker-column.entry.js +5 -5
  64. package/dist/esm/ion-radio_2.entry.js +13 -1
  65. package/dist/esm/ion-select-modal.entry.js +18 -7
  66. package/dist/esm/ion-select_3.entry.js +18 -7
  67. package/dist/ionic/ionic.esm.js +1 -1
  68. package/dist/ionic/p-078037da.entry.js +4 -0
  69. package/dist/ionic/p-23ec35e4.entry.js +4 -0
  70. package/dist/ionic/p-268a3397.entry.js +4 -0
  71. package/dist/ionic/p-28a9e720.entry.js +4 -0
  72. package/dist/ionic/p-4c67ce4c.entry.js +4 -0
  73. package/dist/ionic/p-87125490.entry.js +4 -0
  74. package/dist/ionic/{p-4dd5e8e0.entry.js → p-8fda6a62.entry.js} +1 -1
  75. package/dist/ionic/{p-9eac4eb1.entry.js → p-aa812c4b.entry.js} +1 -1
  76. package/dist/ionic/p-cb27fe68.entry.js +4 -0
  77. package/dist/ionic/p-ce2edb36.entry.js +4 -0
  78. package/dist/types/components/checkbox/checkbox.d.ts +0 -1
  79. package/dist/types/components/datetime/datetime.d.ts +1 -0
  80. package/dist/types/components/modal/modal.d.ts +41 -3
  81. package/dist/types/components/modal/safe-area-utils.d.ts +16 -0
  82. package/dist/types/components/radio-group/test/fixtures.d.ts +1 -1
  83. package/dist/types/components/select-modal/select-modal.d.ts +1 -0
  84. package/dist/types/components/select-modal/test/fixtures.d.ts +1 -0
  85. package/dist/types/components/select-popover/select-popover.d.ts +1 -0
  86. package/dist/types/components/select-popover/test/fixtures.d.ts +1 -0
  87. package/hydrate/index.js +199 -87
  88. package/hydrate/index.mjs +199 -87
  89. package/package.json +4 -4
  90. package/components/p-1KVKSLu5.js +0 -4
  91. package/components/p-BI7WNErr.js +0 -4
  92. package/components/p-C7AoMl7c.js +0 -4
  93. package/components/p-D6pdfDIA.js +0 -4
  94. package/components/p-cvHphHJA.js +0 -4
  95. package/components/p-e-EDj2CO.js +0 -4
  96. package/dist/ionic/p-2d4eb1b4.entry.js +0 -4
  97. package/dist/ionic/p-3e143d1d.entry.js +0 -4
  98. package/dist/ionic/p-51c11c47.entry.js +0 -4
  99. package/dist/ionic/p-5681dde4.entry.js +0 -4
  100. package/dist/ionic/p-9fae83d8.entry.js +0 -4
  101. package/dist/ionic/p-c744307d.entry.js +0 -4
  102. package/dist/ionic/p-cb78f5a0.entry.js +0 -4
  103. package/dist/ionic/p-e6c5f060.entry.js +0 -4
@@ -15,6 +15,12 @@ import { raf } from "../../utils/helpers";
15
15
  const MODAL_INSET_MIN_WIDTH = 768;
16
16
  const MODAL_INSET_MIN_HEIGHT = 600;
17
17
  const EDGE_THRESHOLD = 5;
18
+ /**
19
+ * CSS values for `--width` / `--height` that are treated as fullscreen
20
+ * (modal touches the corresponding screen edges). Empty string means the
21
+ * property was not overridden. See `hasCustomModalDimensions()`.
22
+ */
23
+ const FULLSCREEN_SIZE_VALUES = new Set(['', '100%', '100vw', '100vh', '100dvw', '100dvh', '100svw', '100svh']);
18
24
  /**
19
25
  * Cache for resolved root safe-area-top value, invalidated once per frame.
20
26
  */
@@ -63,6 +69,22 @@ export const getRootSafeAreaTop = () => {
63
69
  }
64
70
  return value;
65
71
  };
72
+ /**
73
+ * True when the modal host declares BOTH a non-fullscreen `--width` AND a
74
+ * non-fullscreen `--height` (i.e. a centered-dialog-like modal that doesn't
75
+ * touch any screen edge).
76
+ *
77
+ * The conservative "both axes" check avoids mis-zeroing safe-area for
78
+ * partial-custom modals where the modal still touches top/bottom edges
79
+ * (e.g. only `--width` overridden). Partial cases fall through to the
80
+ * existing position-based post-animation correction.
81
+ */
82
+ export const hasCustomModalDimensions = (hostEl) => {
83
+ const styles = getComputedStyle(hostEl);
84
+ const width = styles.getPropertyValue('--width').trim();
85
+ const height = styles.getPropertyValue('--height').trim();
86
+ return !FULLSCREEN_SIZE_VALUES.has(width) && !FULLSCREEN_SIZE_VALUES.has(height);
87
+ };
66
88
  /**
67
89
  * Returns the initial safe-area configuration based on modal type.
68
90
  * This is called before animation starts and uses configuration-based prediction.
@@ -97,8 +119,11 @@ export const getInitialSafeAreaConfig = (context) => {
97
119
  }
98
120
  // On viewports that meet the centered dialog media query breakpoints,
99
121
  // regular modals render as centered dialogs (not fullscreen), so they
100
- // don't touch any screen edges and don't need safe-area insets.
101
- if (isCenteredDialogViewport()) {
122
+ // don't touch any screen edges and don't need safe-area insets. Also
123
+ // applies to phone viewports when the modal declares custom --width and
124
+ // --height; these don't touch screen edges either, so the initial
125
+ // prediction must be zero to avoid a post-animation correction flash.
126
+ if (isCenteredDialogViewport() || context.hasCustomDimensions) {
102
127
  return {
103
128
  top: '0px',
104
129
  bottom: '0px',
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { Host, h } from "@stencil/core";
5
5
  import { doc } from "../../utils/browser/index";
6
- import { getElementRoot, raf } from "../../utils/helpers";
6
+ import { raf } from "../../utils/helpers";
7
7
  import { hapticSelectionChanged, hapticSelectionEnd, hapticSelectionStart } from "../../utils/native/haptic";
8
8
  import { isPlatform } from "../../utils/platform";
9
9
  import { createColorClasses } from "../../utils/theme";
@@ -451,7 +451,7 @@ export class PickerColumn {
451
451
  * Because this initial call to scrollActiveItemIntoView has to fire before
452
452
  * the scroll listener is set up, we need to manage the active class manually.
453
453
  */
454
- const oldActive = getElementRoot(el).querySelector(`.${PICKER_ITEM_ACTIVE_CLASS}`);
454
+ const oldActive = el.querySelector(`.${PICKER_ITEM_ACTIVE_CLASS}`);
455
455
  if (oldActive) {
456
456
  this.setPickerItemActiveState(oldActive, false);
457
457
  }
@@ -551,14 +551,14 @@ export class PickerColumn {
551
551
  render() {
552
552
  const { color, disabled, isActive, numericInput } = this;
553
553
  const mode = getIonMode(this);
554
- return (h(Host, { key: 'db903fd415f8a2d91994dececca481c1af8ba6a9', class: createColorClasses(color, {
554
+ return (h(Host, { key: '234c96a501d7ac413b9b0ea56b33017681e25b40', class: createColorClasses(color, {
555
555
  [mode]: true,
556
556
  ['picker-column-active']: isActive,
557
557
  ['picker-column-numeric-input']: numericInput,
558
558
  ['picker-column-disabled']: disabled,
559
- }) }, h("slot", { key: '02ce9e1dd7df91afcd50b06416552bcffb5dec98', name: "prefix" }), h("div", { key: '6dfd7d2429bec19244a6b1afb4448121963a031b', class: "picker-opts", ref: (el) => {
559
+ }) }, h("slot", { key: '9dc15ea0601ddd2cb2e0a745e91e036a8bd96f8b', name: "prefix" }), h("div", { key: 'de4fe28ee4bc46b7c0420d6ab0df0e7809443da9', class: "picker-opts", ref: (el) => {
560
560
  this.scrollEl = el;
561
- }, role: "slider", tabindex: this.disabled ? undefined : 0, "aria-label": this.ariaLabel, "aria-valuemin": 0, "aria-valuemax": 0, "aria-valuenow": 0, "aria-valuetext": this.getOptionValueText(this.activeItem), "aria-orientation": "vertical", onKeyDown: (ev) => this.onKeyDown(ev) }, h("div", { key: 'e30ce0b9cefbfe4d4441fa33acf595da31855c3f', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '8be2bd293c12c6ba720d9b31d0d561a96f42e97d', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '8afdcddddabbf646fbb55cb0ba4448309a2c1dd9', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("slot", { key: '6aa0dacc34d6848575ad5b122b9046982308ca43' }), h("div", { key: '92ec8a357414c1b779b11d1dd18fb87a7ee63982', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: 'b89457cb74b5907c25594ff6720ac54ca537e933', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '5bbc92e6bc24de08e39873bf08c5b668373ac0f8', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0")), h("slot", { key: 'd7bf2b519214f0f3576a4ca79844ad97827dd97f', name: "suffix" })));
561
+ }, role: "slider", tabindex: this.disabled ? undefined : 0, "aria-label": this.ariaLabel, "aria-valuemin": 0, "aria-valuemax": 0, "aria-valuenow": 0, "aria-valuetext": this.getOptionValueText(this.activeItem), "aria-orientation": "vertical", onKeyDown: (ev) => this.onKeyDown(ev) }, h("div", { key: '5297617462cc30e9444039ae032d8bdf718349af', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '55ea39ef867bcb1a11a912d52ecd20cb886c5fb3', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '3496730ce6182ebfd33e0ee4bafc130feb575a31', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("slot", { key: '44c3628aa957d60f799dc7019f72fe8b676c7843' }), h("div", { key: '5a1809f6c949678a67e0d4b5bfe93ea335c0161d', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '98fd57f1c66dbaebc2db2dd5da142671b3159fd1', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0"), h("div", { key: '85590708abddfa885994e549deac64866fec938f', class: "picker-item-empty", "aria-hidden": "true" }, "\u00A0")), h("slot", { key: 'bb7e674f543696a80fcbfb1f68f2e975826898a6', name: "suffix" })));
562
562
  }
563
563
  static get is() { return "ion-picker-column"; }
564
564
  static get encapsulation() { return "shadow"; }
@@ -206,6 +206,18 @@ export class RadioGroup {
206
206
  // to the bottom of the screen
207
207
  ev.preventDefault();
208
208
  }
209
+ // Inside a select interface, Enter commits the focused radio
210
+ // value (matching native <select>). The !ev.repeat guard stops
211
+ // a held Enter on the triggering ion-select from re-committing
212
+ // once focus lands in the opened popover/modal.
213
+ if (ev.key === 'Enter' && inSelectInterface && !ev.repeat) {
214
+ const previousValue = this.value;
215
+ this.value = current.value;
216
+ if (previousValue !== this.value) {
217
+ this.emitValueChange(ev);
218
+ }
219
+ ev.preventDefault();
220
+ }
209
221
  }
210
222
  }
211
223
  /** @internal */
@@ -238,7 +250,7 @@ export class RadioGroup {
238
250
  const { label, labelId, el, name, value } = this;
239
251
  const mode = getIonMode(this);
240
252
  renderHiddenInput(true, el, name, value, false);
241
- return (h(Host, { key: 'db593b3ed511e9395e3c7bfd91b787328692cd6d', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, class: mode }, this.renderHintText(), h("slot", { key: 'd683b01c1ba34fe843c4b320bce4661a117472a5' })));
253
+ return (h(Host, { key: '377e4aa3a656cc84b742f9d7a7d4be65d20c69f5', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, class: mode }, this.renderHintText(), h("slot", { key: 'c3187a2497773b4f15cea3b413b036502bcec8c0' })));
242
254
  }
243
255
  static get is() { return "ion-radio-group"; }
244
256
  static get originalStyleUrls() {
@@ -6,12 +6,12 @@ export class RadioFixture {
6
6
  constructor(page) {
7
7
  this.page = page;
8
8
  }
9
- async checkRadio(method, selector = 'ion-radio') {
9
+ async checkRadio(method, selector = 'ion-radio', key = 'Space') {
10
10
  const { page } = this;
11
11
  const radio = (this.radio = page.locator(selector));
12
12
  if (method === 'keyboard') {
13
13
  await radio.focus();
14
- await page.keyboard.press('Space');
14
+ await page.keyboard.press(key);
15
15
  }
16
16
  else {
17
17
  await radio.click();
@@ -7,6 +7,10 @@ import { safeCall } from "../../utils/overlays";
7
7
  import { getClassMap } from "../../utils/theme";
8
8
  export class SelectModal {
9
9
  constructor() {
10
+ // Tracks the option that received Enter-keydown so keyup only
11
+ // dismisses when the press started on the same option. Prevents
12
+ // Enter on the triggering ion-select from auto-dismissing.
13
+ this.pendingEnterTarget = null;
10
14
  /**
11
15
  * The text to display on the cancel button.
12
16
  */
@@ -56,15 +60,22 @@ export class SelectModal {
56
60
  return (h("ion-radio-group", { value: checked, onIonChange: (ev) => this.callOptionHandler(ev) }, this.options.map((option) => (h("ion-item", { lines: "none", class: Object.assign({
57
61
  // TODO FW-4784
58
62
  'item-radio-checked': option.value === checked
59
- }, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, justify: "start", labelPlacement: "end", onClick: () => this.closeModal(), onKeyUp: (ev) => {
63
+ }, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, justify: "start", labelPlacement: "end", onClick: () => this.closeModal(), onKeyDown: (ev) => {
64
+ if (ev.key === 'Enter' && !ev.repeat) {
65
+ this.pendingEnterTarget = ev.currentTarget;
66
+ }
67
+ }, onKeyUp: (ev) => {
60
68
  if (ev.key === ' ') {
61
- /**
62
- * Selecting a radio option with keyboard navigation,
63
- * either through the Enter or Space keys, should
64
- * dismiss the modal.
65
- */
69
+ // Space selects and dismisses in one press.
66
70
  this.closeModal();
67
71
  }
72
+ else if (ev.key === 'Enter') {
73
+ const shouldClose = this.pendingEnterTarget === ev.currentTarget;
74
+ this.pendingEnterTarget = null;
75
+ if (shouldClose) {
76
+ this.closeModal();
77
+ }
78
+ }
68
79
  } }, option.text))))));
69
80
  }
70
81
  renderCheckboxOptions() {
@@ -79,7 +90,7 @@ export class SelectModal {
79
90
  } }, option.text))));
80
91
  }
81
92
  render() {
82
- return (h(Host, { key: 'f8a4cd6ff23ff01eaa1bdaf3c046814e7b30b23b', class: getIonMode(this) }, h("ion-header", { key: '9e29a7e57ad5cf332641111882f16852187ec8ba' }, h("ion-toolbar", { key: 'e6af5d6eabbf4b10799fc8a0b8f91d29b12d41f5' }, this.header !== undefined && h("ion-title", { key: '6056e52d15dbf307571d25e0305d67228a79237d' }, this.header), h("ion-buttons", { key: 'c9aa4fb2e21a93f3a95c5a8f0ba8b7d5553c5a72', slot: "end" }, h("ion-button", { key: '5ffbf512719bcb053b652fc96b1b6154d0593095', onClick: () => this.closeModal() }, this.cancelText)))), h("ion-content", { key: '0ec9098798a4e6de7a83a0a7e9d10bdcd7c98a78' }, h("ion-list", { key: 'd60b1700d3c2f8655951632de810900707a101f0' }, this.multiple === true ? this.renderCheckboxOptions() : this.renderRadioOptions()))));
93
+ return (h(Host, { key: 'fda0bf6f93cd5ec9f3c64f88a52de849e0e140a2', class: getIonMode(this) }, h("ion-header", { key: '27c0b17175a53db9ff159feeeb96451a3f011dab' }, h("ion-toolbar", { key: '91a4155ebc317fbc9f1bb3e26a7e94754b953c9b' }, this.header !== undefined && h("ion-title", { key: 'f6dae8e4e381f322cc90efefd9bb6ef81d4d2f3e' }, this.header), h("ion-buttons", { key: 'e7760532fb2e7e7385ed6e62097d92d96ff20148', slot: "end" }, h("ion-button", { key: '4999b6fc46cba138186546dca67b7950855e6fb7', onClick: () => this.closeModal() }, this.cancelText)))), h("ion-content", { key: 'c73f80a4bc25b9061ea65cf11e5d811c1a4d8704' }, h("ion-list", { key: 'b21905d15b36ad5eb45845e768918d2763cf48b1' }, this.multiple === true ? this.renderCheckboxOptions() : this.renderRadioOptions()))));
83
94
  }
84
95
  static get is() { return "ion-select-modal"; }
85
96
  static get encapsulation() { return "scoped"; }
@@ -39,6 +39,10 @@ export class SelectModalPage {
39
39
  const option = this.getOption(value);
40
40
  await option.press('Space');
41
41
  }
42
+ async pressEnterOnOption(value) {
43
+ const option = this.getOption(value);
44
+ await option.press('Enter');
45
+ }
42
46
  getOption(value) {
43
47
  const { multiple, selectModal } = this;
44
48
  const selector = multiple ? 'ion-checkbox' : 'ion-radio';
@@ -10,6 +10,10 @@ import { getIonMode } from "../../global/ionic-global";
10
10
  */
11
11
  export class SelectPopover {
12
12
  constructor() {
13
+ // Tracks the option that received Enter-keydown so keyup only
14
+ // dismisses when the press started on the same option. Prevents
15
+ // Enter on the triggering ion-select from auto-dismissing.
16
+ this.pendingEnterTarget = null;
13
17
  /**
14
18
  * An array of options for the popover
15
19
  */
@@ -87,21 +91,28 @@ export class SelectPopover {
87
91
  return (h("ion-radio-group", { value: checked, onIonChange: (ev) => this.callOptionHandler(ev) }, options.map((option) => (h("ion-item", { class: Object.assign({
88
92
  // TODO FW-4784
89
93
  'item-radio-checked': option.value === checked
90
- }, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, onClick: () => this.dismissParentPopover(), onKeyUp: (ev) => {
94
+ }, getClassMap(option.cssClass)) }, h("ion-radio", { value: option.value, disabled: option.disabled, onClick: () => this.dismissParentPopover(), onKeyDown: (ev) => {
95
+ if (ev.key === 'Enter' && !ev.repeat) {
96
+ this.pendingEnterTarget = ev.currentTarget;
97
+ }
98
+ }, onKeyUp: (ev) => {
91
99
  if (ev.key === ' ') {
92
- /**
93
- * Selecting a radio option with keyboard navigation,
94
- * either through the Enter or Space keys, should
95
- * dismiss the popover.
96
- */
100
+ // Space selects and dismisses in one press.
97
101
  this.dismissParentPopover();
98
102
  }
103
+ else if (ev.key === 'Enter') {
104
+ const shouldDismiss = this.pendingEnterTarget === ev.currentTarget;
105
+ this.pendingEnterTarget = null;
106
+ if (shouldDismiss) {
107
+ this.dismissParentPopover();
108
+ }
109
+ }
99
110
  } }, option.text))))));
100
111
  }
101
112
  render() {
102
113
  const { header, message, options, subHeader } = this;
103
114
  const hasSubHeaderOrMessage = subHeader !== undefined || message !== undefined;
104
- return (h(Host, { key: '0c9845a40d3fc392b0a7d64e2a6ed27d94bb7634', class: getIonMode(this) }, h("ion-list", { key: '84a30f6661b0f8c00e6fa199658ed2adbcf27358' }, header !== undefined && h("ion-list-header", { key: '13f5f56bbfbc06751fa516291a2da72629b60ece' }, header), hasSubHeaderOrMessage && (h("ion-item", { key: '3d39d18e720e798bbde334e79e6832091c7dfb81' }, h("ion-label", { key: 'd3051b0d140120b44bf5e79572f6f287e7cfb03a', class: "ion-text-wrap" }, subHeader !== undefined && h("h3", { key: 'b16805956f3316f8ec703c123b76f717488e8637' }, subHeader), message !== undefined && h("p", { key: '2215ac4ab4146a14e75a79192e319a8016286b5f' }, message)))), this.renderOptions(options))));
115
+ return (h(Host, { key: 'e7449a1ecfcdbf45a79f8e26a00253c4e146448a', class: getIonMode(this) }, h("ion-list", { key: '52abdfc8668c3429a0dcefef8ddedb6647fdd894' }, header !== undefined && h("ion-list-header", { key: '978e5c03728756feafcc60a0e10e6ec59bf2ae11' }, header), hasSubHeaderOrMessage && (h("ion-item", { key: 'e93c44e7f07a76def16e4b11f0fb4780d84ed402' }, h("ion-label", { key: 'bba1aac43b0bc7f4f00978dd8301985233f3725c', class: "ion-text-wrap" }, subHeader !== undefined && h("h3", { key: 'ad96f6017cf2cc5219540bded2c4f1ca3b532de2' }, subHeader), message !== undefined && h("p", { key: '3fd038921dc40c4d0c29734433984b279ccaeec3' }, message)))), this.renderOptions(options))));
105
116
  }
106
117
  static get is() { return "ion-select-popover"; }
107
118
  static get encapsulation() { return "scoped"; }
@@ -39,6 +39,10 @@ export class SelectPopoverPage {
39
39
  const option = this.getOption(value);
40
40
  await option.press('Space');
41
41
  }
42
+ async pressEnterOnOption(value) {
43
+ const option = this.getOption(value);
44
+ await option.press('Enter');
45
+ }
42
46
  getOption(value) {
43
47
  const { multiple, selectPopover } = this;
44
48
  const selector = multiple ? 'ion-checkbox' : 'ion-radio';
package/dist/docs.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-03-25T21:11:03",
2
+ "timestamp": "2026-05-01T03:59:54",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
5
  "version": "4.43.0",
@@ -471,7 +471,7 @@ const ActionSheet = class {
471
471
  if (isRadio) {
472
472
  htmlAttrs['aria-checked'] = isActiveRadio ? 'true' : 'false';
473
473
  }
474
- return (h("button", Object.assign({}, htmlAttrs, { role: isRadio ? 'radio' : undefined, type: "button", id: buttonId, class: Object.assign(Object.assign({}, buttonClass(b)), { 'action-sheet-selected': isActiveRadio }), onClick: () => {
474
+ return (h("button", Object.assign({}, htmlAttrs, { role: isRadio ? 'radio' : undefined, type: "button", id: buttonId, class: Object.assign(Object.assign({}, buttonClass(b)), (isRadio && { 'action-sheet-selected': isActiveRadio })), onClick: () => {
475
475
  if (isRadio) {
476
476
  this.selectRadioButton(b);
477
477
  }
@@ -486,12 +486,12 @@ const ActionSheet = class {
486
486
  const cancelButton = allButtons.find((b) => b.role === 'cancel');
487
487
  const buttons = allButtons.filter((b) => b.role !== 'cancel');
488
488
  const headerID = `action-sheet-${overlayIndex}-header`;
489
- return (h(Host, Object.assign({ key: '173fcff5b1da7c33c267de4667591c946b8c8d03', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1" }, htmlAttributes, { style: {
489
+ return (h(Host, Object.assign({ key: 'a56ee2ab59c763036140dbd10306a708c26e3c17', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1" }, htmlAttributes, { style: {
490
490
  zIndex: `${20000 + this.overlayIndex}`,
491
- }, 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: '521ede659f747864f6c974e09016436eceb7158c', tappable: this.backdropDismiss }), h("div", { key: '7a7946fc434bc444f16a70638f5e948c69d33fcd', tabindex: "0", "aria-hidden": "true" }), h("div", { key: 'bcff39a580489dbafa255842e57aa8602c6d0f18', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, h("div", { key: '84bba13ce14261f0f0daa3f9c77648c9e7f36e0e', class: "action-sheet-container" }, h("div", { key: 'd9c8ac404fd6719a7adf8cb36549f67616f9a0c4', class: "action-sheet-group", ref: (el) => (this.groupEl = el), role: hasRadioButtons ? 'radiogroup' : undefined }, header !== undefined && (h("div", { key: '180433a8ad03ef5c54728a1a8f34715b6921d658', id: headerID, class: {
491
+ }, 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: 'c32eb4281fd6348c7d3989a3f509c211263048e6', tappable: this.backdropDismiss }), h("div", { key: '7f0123114a876fc7cfff3cfb564aded4a7017797', tabindex: "0", "aria-hidden": "true" }), h("div", { key: '645b1d5fde39a8907f21983d66e6ecb7a99aa05d', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, h("div", { key: 'a78fb02848462d1a4f9356ac4fa1c43a2e5d90e4', class: "action-sheet-container" }, h("div", { key: '5e846f53e067b211b985d6e1512b72b9d7c1a3aa', class: "action-sheet-group", ref: (el) => (this.groupEl = el), role: hasRadioButtons ? 'radiogroup' : undefined }, header !== undefined && (h("div", { key: 'a90a0e096e1b2fa78b9adb9253c0a517f16e62cb', id: headerID, class: {
492
492
  'action-sheet-title': true,
493
493
  'action-sheet-has-sub-title': this.subHeader !== undefined,
494
- } }, header, this.subHeader && h("div", { key: '7138e79e61b1a8f42bc5a9175c57fa2f15d7ec5a', class: "action-sheet-sub-title" }, this.subHeader))), this.renderActionSheetButtons(buttons)), cancelButton && (h("div", { key: 'b617c722f5b8028d73ed34b69310f312c65f34a7', class: "action-sheet-group action-sheet-group-cancel" }, h("button", Object.assign({ key: 'd0dd876fc48815df3710413c201c0b445a8e16c0' }, cancelButton.htmlAttributes, { type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) }), h("span", { key: 'e7b960157cc6fc5fe92a12090b2be55e8ae072e4', class: "action-sheet-button-inner" }, cancelButton.icon && (h("ion-icon", { key: '05498ffc60cab911dbff0ecbc6168dea59ada9a5', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && h("ion-ripple-effect", { key: '3d401346cea301be4ca03671f7370f6f4b0b6bde' })))))), h("div", { key: '971f3c5fcc07f36c28eb469a47ec0290c692e139', tabindex: "0", "aria-hidden": "true" })));
494
+ } }, header, this.subHeader && h("div", { key: '40f00b12341625c548546de1885b9c9d93bc169c', class: "action-sheet-sub-title" }, this.subHeader))), this.renderActionSheetButtons(buttons)), cancelButton && (h("div", { key: 'ef6974cb63089623df08087274b82745443cee8c', class: "action-sheet-group action-sheet-group-cancel" }, h("button", Object.assign({ key: 'b02911a6491d60f9dcb5da7d942392a9e96552c1' }, cancelButton.htmlAttributes, { type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) }), h("span", { key: '1187433e676eda55e52b5ae328a8e68bba22deb6', class: "action-sheet-button-inner" }, cancelButton.icon && (h("ion-icon", { key: '079ab2a6bd40b996950053617f1c1c8207ecb1f1', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && h("ion-ripple-effect", { key: '3bc473add8ac299f202f8c359d26708872c02f52' })))))), h("div", { key: '9b1ae7b4e3649e9b85632f0d65627ca81499e68d', tabindex: "0", "aria-hidden": "true" })));
495
495
  }
496
496
  get el() { return getElement(this); }
497
497
  static get watchers() { return {
@@ -157,7 +157,7 @@ Buttons.style = {
157
157
  md: buttonsMdCss()
158
158
  };
159
159
 
160
- const contentCss = () => `:host{--background:var(--ion-background-color, #fff);--color:var(--ion-text-color, #000);--padding-top:0px;--padding-bottom:0px;--padding-start:0px;--padding-end:0px;--keyboard-offset:0px;--offset-top:0px;--offset-bottom:0px;--overflow:auto;display:block;position:relative;-ms-flex:1;flex:1;width:100%;height:100%;margin:0 !important;padding:0 !important;font-family:var(--ion-font-family, inherit);contain:size style}:host(.ion-color) .inner-scroll{background:var(--ion-color-base);color:var(--ion-color-contrast)}#background-content{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);position:absolute;background:var(--background)}.inner-scroll{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end);padding-top:calc(var(--padding-top) + var(--offset-top));padding-bottom:calc(var(--padding-bottom) + var(--keyboard-offset) + var(--offset-bottom));position:absolute;color:var(--color);-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-ms-touch-action:pan-x pan-y pinch-zoom;touch-action:pan-x pan-y pinch-zoom}.scroll-y,.scroll-x{-webkit-overflow-scrolling:touch;z-index:0;will-change:scroll-position}.scroll-y{overflow-y:var(--overflow);overscroll-behavior-y:contain}.scroll-x{overflow-x:var(--overflow);overscroll-behavior-x:contain}.overscroll::before,.overscroll::after{position:absolute;width:1px;height:1px;content:""}.overscroll::before{bottom:-1px}.overscroll::after{top:-1px}:host(.content-sizing){display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-height:0;contain:none}:host(.content-sizing) .inner-scroll{position:relative;top:0;bottom:0;margin-top:calc(var(--offset-top) * -1);margin-bottom:calc(var(--offset-bottom) * -1)}.transition-effect{display:none;position:absolute;width:100%;height:100vh;opacity:0;pointer-events:none}:host(.content-ltr) .transition-effect{left:-100%;}:host(.content-rtl) .transition-effect{right:-100%;}.transition-cover{position:absolute;right:0;width:100%;height:100%;background:black;opacity:0.1}.transition-shadow{display:block;position:absolute;width:100%;height:100%;-webkit-box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03);box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03)}:host(.content-ltr) .transition-shadow{right:0;}:host(.content-rtl) .transition-shadow{left:0;-webkit-transform:scaleX(-1);transform:scaleX(-1)}::slotted([slot=fixed]){position:absolute;-webkit-transform:translateZ(0);transform:translateZ(0)}`;
160
+ const contentCss = () => `:host{--background:var(--ion-background-color, #fff);--color:var(--ion-text-color, #000);--padding-top:0px;--padding-bottom:0px;--padding-start:0px;--padding-end:0px;--keyboard-offset:0px;--offset-top:0px;--offset-bottom:0px;--overflow:auto;display:block;position:relative;-ms-flex:1;flex:1;width:100%;height:100%;margin:0 !important;padding:0 !important;font-family:var(--ion-font-family, inherit);contain:size style}:host(.ion-color) .inner-scroll{background:var(--ion-color-base);color:var(--ion-color-contrast)}#background-content{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);position:absolute;background:var(--background)}.inner-scroll{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end);padding-top:calc(var(--padding-top) + var(--offset-top));padding-bottom:calc(var(--padding-bottom) + var(--keyboard-offset) + var(--offset-bottom) + var(--ion-content-safe-area-padding-bottom, 0px));position:absolute;color:var(--color);-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-ms-touch-action:pan-x pan-y pinch-zoom;touch-action:pan-x pan-y pinch-zoom}.scroll-y,.scroll-x{-webkit-overflow-scrolling:touch;z-index:0;will-change:scroll-position}.scroll-y{overflow-y:var(--overflow);overscroll-behavior-y:contain}.scroll-x{overflow-x:var(--overflow);overscroll-behavior-x:contain}.overscroll::before,.overscroll::after{position:absolute;width:1px;height:1px;content:""}.overscroll::before{bottom:-1px}.overscroll::after{top:-1px}:host(.content-sizing){display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-height:0;contain:none}:host(.content-sizing) .inner-scroll{position:relative;top:0;bottom:0;margin-top:calc(var(--offset-top) * -1);margin-bottom:calc(var(--offset-bottom) * -1)}.transition-effect{display:none;position:absolute;width:100%;height:100vh;opacity:0;pointer-events:none}:host(.content-ltr) .transition-effect{left:-100%;}:host(.content-rtl) .transition-effect{right:-100%;}.transition-cover{position:absolute;right:0;width:100%;height:100%;background:black;opacity:0.1}.transition-shadow{display:block;position:absolute;width:100%;height:100%;-webkit-box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03);box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03)}:host(.content-ltr) .transition-shadow{right:0;}:host(.content-rtl) .transition-shadow{left:0;-webkit-transform:scaleX(-1);transform:scaleX(-1)}::slotted([slot=fixed]){position:absolute;-webkit-transform:translateZ(0);transform:translateZ(0)}`;
161
161
 
162
162
  const Content = class {
163
163
  constructor(hostRef) {
@@ -110,48 +110,55 @@ const Checkbox = class {
110
110
  this.onDivLabelClick = (ev) => {
111
111
  ev.stopPropagation();
112
112
  };
113
- this.onSlotChange = () => {
114
- this.hasLabelContent = this.el.textContent !== '';
115
- };
116
113
  }
117
114
  connectedCallback() {
118
115
  const { el } = this;
119
- // Watch for class changes to update validation state.
120
116
  if (typeof MutationObserver !== 'undefined') {
121
- this.validationObserver = new MutationObserver(() => {
122
- const newIsInvalid = checkInvalidState(el);
123
- if (this.isInvalid !== newIsInvalid) {
124
- this.isInvalid = newIsInvalid;
125
- /**
126
- * Screen readers tend to announce changes
127
- * to `aria-describedby` when the attribute
128
- * is changed during a blur event for a
129
- * native form control.
130
- * However, the announcement can be spotty
131
- * when using a non-native form control
132
- * and `forceUpdate()`.
133
- * This is due to `forceUpdate()` internally
134
- * rescheduling the DOM update to a lower
135
- * priority queue regardless if it's called
136
- * inside a Promise or not, thus causing
137
- * the screen reader to potentially miss the
138
- * change.
139
- * By using a State variable inside a Promise,
140
- * it guarantees a re-render immediately at
141
- * a higher priority.
142
- */
143
- Promise.resolve().then(() => {
144
- this.hintTextId = this.getHintTextId();
145
- });
117
+ this.validationObserver = new MutationObserver((mutations) => {
118
+ // Watch for label content changes
119
+ if (mutations.some((mutation) => mutation.type === 'characterData' || mutation.type === 'childList')) {
120
+ this.hasLabelContent = this.el.textContent !== '';
121
+ }
122
+ // Watch for class changes to update validation state.
123
+ if (mutations.some((mutation) => mutation.type === 'attributes' && mutation.target === el)) {
124
+ const newIsInvalid = checkInvalidState(el);
125
+ if (this.isInvalid !== newIsInvalid) {
126
+ this.isInvalid = newIsInvalid;
127
+ /**
128
+ * Screen readers tend to announce changes
129
+ * to `aria-describedby` when the attribute
130
+ * is changed during a blur event for a
131
+ * native form control.
132
+ * However, the announcement can be spotty
133
+ * when using a non-native form control
134
+ * and `forceUpdate()`.
135
+ * This is due to `forceUpdate()` internally
136
+ * rescheduling the DOM update to a lower
137
+ * priority queue regardless if it's called
138
+ * inside a Promise or not, thus causing
139
+ * the screen reader to potentially miss the
140
+ * change.
141
+ * By using a State variable inside a Promise,
142
+ * it guarantees a re-render immediately at
143
+ * a higher priority.
144
+ */
145
+ Promise.resolve().then(() => {
146
+ this.hintTextId = this.getHintTextId();
147
+ });
148
+ }
146
149
  }
147
150
  });
148
151
  this.validationObserver.observe(el, {
149
152
  attributes: true,
150
153
  attributeFilter: ['class'],
154
+ characterData: true,
155
+ childList: true,
156
+ subtree: true,
151
157
  });
152
158
  }
153
159
  // Always set initial state
154
160
  this.isInvalid = checkInvalidState(el);
161
+ this.hasLabelContent = this.el.textContent !== '';
155
162
  }
156
163
  componentWillLoad() {
157
164
  this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
@@ -201,7 +208,7 @@ const Checkbox = class {
201
208
  renderHiddenInput(true, el, name, checked ? value : '', disabled);
202
209
  // The host element must have a checkbox role to ensure proper VoiceOver
203
210
  // support in Safari for accessibility.
204
- return (h(Host, { key: '78eb720868106bcbe8357e50ebae2ab2fce8bdd6', role: "checkbox", "aria-checked": indeterminate ? 'mixed' : `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-labelledby": this.hasLabelContent ? this.inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, "aria-required": required ? 'true' : undefined, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: createColorClasses(color, {
211
+ return (h(Host, { key: '0da370f94c5cdf3b08bc9008395558334a300f35', role: "checkbox", "aria-checked": indeterminate ? 'mixed' : `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-labelledby": this.hasLabelContent ? this.inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, "aria-required": required ? 'true' : undefined, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: createColorClasses(color, {
205
212
  [mode]: true,
206
213
  'in-item': hostContext('ion-item', el),
207
214
  'checkbox-checked': checked,
@@ -211,10 +218,10 @@ const Checkbox = class {
211
218
  [`checkbox-justify-${justify}`]: justify !== undefined,
212
219
  [`checkbox-alignment-${alignment}`]: alignment !== undefined,
213
220
  [`checkbox-label-placement-${labelPlacement}`]: true,
214
- }) }, h("label", { key: '3bd09a96e126bc72a0589a9c9ff8459cb60e1084', class: "checkbox-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: 'f55c566f36dc6b0f069c75dccdebd2c5b3e7385e', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, required: required }, inheritedAttributes)), h("div", { key: '11db1139eabfe0b83688c574b81d1a6e8b7ae8c6', class: {
221
+ }) }, h("label", { key: '991f1763356671230af119a5fbdc22d0a39974e7', class: "checkbox-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: '982f8a7f84d013b272b17607936355d2b6c251f4', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, required: required }, inheritedAttributes)), h("div", { key: 'c8f9e8baa20ac68e69fd3c6fcf0e7a26a1084d83', class: {
215
222
  'label-text-wrapper': true,
216
223
  'label-text-wrapper-hidden': !this.hasLabelContent,
217
- }, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '6b3dd09e86063e2bc48014a1715cd788038ca01d', onSlotchange: this.onSlotChange }), this.renderHintText()), h("div", { key: 'bb2c75c8a893fd81e213c6b2ba807d5cba5a4966', class: "native-wrapper" }, h("svg", { key: '028a4c7d211c3697a91743d6242f202209e14c1a', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
224
+ }, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '6018205e0a73dec826c7881d687f1c2ca8dcb0ab' }), this.renderHintText()), h("div", { key: '57530b9d6ff59ee7ab98f960cd65d66ee87cfd1d', class: "native-wrapper" }, h("svg", { key: '63d719154ff44459e9ca448e3f5d7de94d9ab248', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
218
225
  }
219
226
  getSVGPath(mode, indeterminate) {
220
227
  let path = indeterminate ? (h("path", { d: "M6 12L18 12", part: "mark" })) : (h("path", { d: "M5.9,12.5l3.8,3.8l8.8-8.8", part: "mark" }));
@@ -811,6 +811,12 @@ const Datetime = class {
811
811
  this.el.classList.add('datetime-ready');
812
812
  });
813
813
  };
814
+ this.loadTimeoutCleanup = () => {
815
+ if (this.loadTimeout) {
816
+ clearTimeout(this.loadTimeout);
817
+ this.loadTimeout = undefined;
818
+ }
819
+ };
814
820
  this.processValue = (value) => {
815
821
  const hasValue = value !== null && value !== undefined && value !== '' && (!Array.isArray(value) || value.length > 0);
816
822
  const valueToProcess = hasValue ? parseDate(value) : this.defaultParts;
@@ -958,9 +964,10 @@ const Datetime = class {
958
964
  if (!prevMonth) {
959
965
  return;
960
966
  }
967
+ const left = prevMonth.offsetWidth * 2;
961
968
  calendarBodyRef.scrollTo({
962
969
  top: 0,
963
- left: 0,
970
+ left: left * (isRTL(this.el) ? 1 : -1),
964
971
  behavior: 'smooth',
965
972
  });
966
973
  };
@@ -1082,15 +1089,16 @@ const Datetime = class {
1082
1089
  }
1083
1090
  connectedCallback() {
1084
1091
  this.clearFocusVisible = startFocusVisible(this.el).destroy;
1092
+ this.loadTimeout = setTimeout(() => {
1093
+ this.ensureReadyIfVisible();
1094
+ }, 100);
1085
1095
  }
1086
1096
  disconnectedCallback() {
1087
1097
  if (this.clearFocusVisible) {
1088
1098
  this.clearFocusVisible();
1089
1099
  this.clearFocusVisible = undefined;
1090
1100
  }
1091
- if (this.loadTimeout) {
1092
- clearTimeout(this.loadTimeout);
1093
- }
1101
+ this.loadTimeoutCleanup();
1094
1102
  }
1095
1103
  initializeListeners() {
1096
1104
  this.initializeCalendarListener();
@@ -1138,7 +1146,10 @@ const Datetime = class {
1138
1146
  * we still initialize listeners and mark the component as ready.
1139
1147
  *
1140
1148
  * We schedule this after everything has had a chance to run.
1149
+ *
1150
+ * We also clean up the load timeout to ensure that we don't have multiple timeouts running.
1141
1151
  */
1152
+ this.loadTimeoutCleanup();
1142
1153
  this.loadTimeout = setTimeout(() => {
1143
1154
  this.ensureReadyIfVisible();
1144
1155
  }, 100);
@@ -1897,7 +1908,7 @@ const Datetime = class {
1897
1908
  const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
1898
1909
  const hasWheelVariant = hasDatePresentation && preferWheel;
1899
1910
  renderHiddenInput(true, el, name, formatValue(value), disabled);
1900
- return (h(Host, { key: '191a6d7ce7bc2d57bfaaebd8aee31e3c36f2b56a', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1911
+ return (h(Host, { key: '59e0811aa273e88dfb8e4b703e6824088a457380', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
1901
1912
  [mode]: true,
1902
1913
  ['datetime-readonly']: readonly,
1903
1914
  ['datetime-disabled']: disabled,
@@ -1907,7 +1918,7 @@ const Datetime = class {
1907
1918
  [`datetime-size-${size}`]: true,
1908
1919
  [`datetime-prefer-wheel`]: hasWheelVariant,
1909
1920
  [`datetime-grid`]: isGridStyle,
1910
- })) }, h("div", { key: '5c75290394cf7dc37c7dcba6372af003a50a9c04', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1921
+ })) }, h("div", { key: '3753ff3dde3085070916c3de83687a219a49e553', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
1911
1922
  }
1912
1923
  get el() { return getElement(this); }
1913
1924
  static get watchers() { return {
@@ -140,9 +140,18 @@ const InputOTP = class {
140
140
  * - Tab: Allows normal tab navigation between components
141
141
  */
142
142
  this.onKeyDown = (index) => (event) => {
143
- const { length } = this;
143
+ const { disabled, length, readonly } = this;
144
144
  const rtl = isRTL(this.el);
145
145
  const input = event.target;
146
+ if (disabled) {
147
+ return;
148
+ }
149
+ if (readonly) {
150
+ if (event.key === 'Backspace' || event.key === 'Delete') {
151
+ event.preventDefault();
152
+ return;
153
+ }
154
+ }
146
155
  // Meta shortcuts are used to copy, paste, and select text
147
156
  // We don't want to handle these keys here
148
157
  const metaShortcuts = ['a', 'c', 'v', 'x', 'r', 'z', 'y'];
@@ -205,10 +214,13 @@ const InputOTP = class {
205
214
  */
206
215
  this.onInput = (index) => (event) => {
207
216
  var _a, _b;
208
- const { length, validKeyPattern } = this;
217
+ const { disabled, length, readonly, validKeyPattern } = this;
209
218
  const input = event.target;
210
219
  const value = input.value;
211
220
  const previousValue = this.previousInputValues[index] || '';
221
+ if (disabled || readonly) {
222
+ return;
223
+ }
212
224
  // 1. Autofill handling
213
225
  // If the length of the value increases by more than 1 from the previous
214
226
  // value, treat this as autofill. This is to prevent the case where the
@@ -327,8 +339,11 @@ const InputOTP = class {
327
339
  */
328
340
  this.onPaste = (event) => {
329
341
  var _a, _b;
330
- const { inputRefs, length, validKeyPattern } = this;
342
+ const { disabled, inputRefs, length, readonly, validKeyPattern } = this;
331
343
  event.preventDefault();
344
+ if (disabled || readonly) {
345
+ return;
346
+ }
332
347
  const pastedText = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text');
333
348
  // If there is no pasted text, still emit the input change event
334
349
  // because this is how the native input element behaves
@@ -615,7 +630,7 @@ const InputOTP = class {
615
630
  const tabbableIndex = this.getTabbableIndex();
616
631
  const pattern = this.getPattern();
617
632
  const hasDescription = ((_b = (_a = el.querySelector('.input-otp-description')) === null || _a === void 0 ? void 0 : _a.textContent) === null || _b === void 0 ? void 0 : _b.trim()) !== '';
618
- return (h(Host, { key: 'f15a29fb17b681ef55885ca36d3d5f899cbaca83', class: createColorClasses(color, {
633
+ return (h(Host, { key: '5c1386ae6b8038ec33ca94fd818c9353b1b37f75', class: createColorClasses(color, {
619
634
  [mode]: true,
620
635
  'has-focus': hasFocus,
621
636
  [`input-otp-size-${size}`]: true,
@@ -623,10 +638,10 @@ const InputOTP = class {
623
638
  [`input-otp-fill-${fill}`]: true,
624
639
  'input-otp-disabled': disabled,
625
640
  'input-otp-readonly': readonly,
626
- }) }, h("div", Object.assign({ key: 'd7e1d4edd8aafcf2ed4313301287282e90fc7e82', role: "group", "aria-label": "One-time password input", class: "input-otp-group" }, inheritedAttributes), Array.from({ length }).map((_, index) => (h(Fragment, null, h("div", { class: "native-wrapper" }, h("input", { class: "native-input", id: `${inputId}-${index}`, "aria-label": `Input ${index + 1} of ${length}`, type: "text", autoCapitalize: autocapitalize, inputmode: inputmode, pattern: pattern, disabled: disabled, readOnly: readonly, tabIndex: index === tabbableIndex ? 0 : -1, value: inputValues[index] || '', autocomplete: "one-time-code", ref: (el) => (inputRefs[index] = el), onInput: this.onInput(index), onBlur: this.onBlur, onFocus: this.onFocus(index), onKeyDown: this.onKeyDown(index), onPaste: this.onPaste })), this.showSeparator(index) && h("div", { class: "input-otp-separator" }))))), h("div", { key: '3724a3159d02860971879a906092f9965f5a7c47', class: {
641
+ }) }, h("div", Object.assign({ key: '9a19129688e55095f8386826c73ef3f9744becff', role: "group", "aria-label": "One-time password input", class: "input-otp-group" }, inheritedAttributes), Array.from({ length }).map((_, index) => (h(Fragment, null, h("div", { class: "native-wrapper" }, h("input", { class: "native-input", id: `${inputId}-${index}`, "aria-label": `Input ${index + 1} of ${length}`, type: "text", autoCapitalize: autocapitalize, inputmode: inputmode, pattern: pattern, disabled: disabled, readOnly: readonly, tabIndex: index === tabbableIndex ? 0 : -1, value: inputValues[index] || '', autocomplete: "one-time-code", ref: (el) => (inputRefs[index] = el), onInput: this.onInput(index), onBlur: this.onBlur, onFocus: this.onFocus(index), onKeyDown: this.onKeyDown(index), onPaste: this.onPaste })), this.showSeparator(index) && h("div", { class: "input-otp-separator" }))))), h("div", { key: '7853819c3610c4691191f1836b947bf4ec17939d', class: {
627
642
  'input-otp-description': true,
628
643
  'input-otp-description-hidden': !hasDescription,
629
- } }, h("slot", { key: '11baa2624926a08274508afe0833d9237a8dc35c' }))));
644
+ } }, h("slot", { key: 'f4674d47d3d3991f21a0a79321ebc323968071dc' }))));
630
645
  }
631
646
  get el() { return getElement(this); }
632
647
  static get watchers() { return {