@ionic/core 8.7.12-dev.11765219790.17cbe2e9 → 8.7.12-dev.11765231260.1def96ab

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.
@@ -118,6 +118,7 @@ const ActionSheet = /*@__PURE__*/ proxyCustomElement(class ActionSheet extends H
118
118
  this.delegateController = createDelegateController(this);
119
119
  this.lockController = createLockController();
120
120
  this.triggerController = createTriggerController();
121
+ this.hasRadioButtons = false;
121
122
  this.presented = false;
122
123
  /** @internal */
123
124
  this.hasController = false;
@@ -162,6 +163,19 @@ const ActionSheet = /*@__PURE__*/ proxyCustomElement(class ActionSheet extends H
162
163
  }
163
164
  };
164
165
  }
166
+ buttonsChanged() {
167
+ const radioButtons = this.getRadioButtons();
168
+ this.hasRadioButtons = radioButtons.length > 0;
169
+ // Initialize activeRadioId when buttons change
170
+ if (this.hasRadioButtons) {
171
+ const checkedButton = radioButtons.find((b) => { var _a; return ((_a = b.htmlAttributes) === null || _a === void 0 ? void 0 : _a['aria-checked']) === 'true'; });
172
+ if (checkedButton) {
173
+ const allButtons = this.getButtons();
174
+ const checkedIndex = allButtons.indexOf(checkedButton);
175
+ this.activeRadioId = this.getButtonId(checkedButton, checkedIndex);
176
+ }
177
+ }
178
+ }
165
179
  onIsOpenChange(newValue, oldValue) {
166
180
  if (newValue === true && oldValue === false) {
167
181
  this.present();
@@ -242,11 +256,122 @@ const ActionSheet = /*@__PURE__*/ proxyCustomElement(class ActionSheet extends H
242
256
  }
243
257
  return true;
244
258
  }
259
+ /**
260
+ * Get all buttons regardless of role.
261
+ */
245
262
  getButtons() {
246
263
  return this.buttons.map((b) => {
247
264
  return typeof b === 'string' ? { text: b } : b;
248
265
  });
249
266
  }
267
+ /**
268
+ * Get all radio buttons (buttons with role="radio").
269
+ */
270
+ getRadioButtons() {
271
+ return this.getButtons().filter((b) => {
272
+ var _a;
273
+ const role = (_a = b.htmlAttributes) === null || _a === void 0 ? void 0 : _a.role;
274
+ return role === 'radio' && !isCancel(role);
275
+ });
276
+ }
277
+ /**
278
+ * Handle radio button selection and update aria-checked state.
279
+ *
280
+ * @param button The radio button that was selected.
281
+ */
282
+ selectRadioButton(button) {
283
+ const buttonId = this.getButtonId(button);
284
+ // Set the active radio ID (this will trigger a re-render and update aria-checked)
285
+ this.activeRadioId = buttonId;
286
+ }
287
+ /**
288
+ * Get or generate an ID for a button.
289
+ *
290
+ * @param button The button for which to get the ID.
291
+ * @param index Optional index of the button in the buttons array.
292
+ * @returns The ID of the button.
293
+ */
294
+ getButtonId(button, index) {
295
+ if (button.id) {
296
+ return button.id;
297
+ }
298
+ const allButtons = this.getButtons();
299
+ const buttonIndex = index !== undefined ? index : allButtons.indexOf(button);
300
+ return `action-sheet-button-${this.overlayIndex}-${buttonIndex}`;
301
+ }
302
+ /**
303
+ * When the action sheet has radio buttons, we want to follow the
304
+ * keyboard navigation pattern for radio groups:
305
+ * - Arrow Down/Right: Move to the next radio button (wrap to first if at end)
306
+ * - Arrow Up/Left: Move to the previous radio button (wrap to last if at start)
307
+ * - Space/Enter: Select the focused radio button and trigger its handler
308
+ */
309
+ onKeydown(ev) {
310
+ // Only handle keyboard navigation if we have radio buttons
311
+ if (!this.hasRadioButtons || !this.presented) {
312
+ return;
313
+ }
314
+ const target = ev.target;
315
+ // Ignore if the target element is not within the action sheet or not a radio button
316
+ if (!this.el.contains(target) ||
317
+ !target.classList.contains('action-sheet-button') ||
318
+ target.getAttribute('role') !== 'radio') {
319
+ return;
320
+ }
321
+ // Get all radio button elements and filter out disabled ones
322
+ const radios = Array.from(this.el.querySelectorAll('.action-sheet-button[role="radio"]')).filter((el) => !el.disabled);
323
+ const currentIndex = radios.findIndex((radio) => radio.id === target.id);
324
+ if (currentIndex === -1) {
325
+ return;
326
+ }
327
+ const allButtons = this.getButtons();
328
+ const radioButtons = this.getRadioButtons();
329
+ /**
330
+ * Build a map of button element IDs to their ActionSheetButton
331
+ * config objects.
332
+ * This allows us to quickly look up which button config corresponds
333
+ * to a DOM element when handling keyboard navigation
334
+ * (e.g., whenuser presses Space/Enter or arrow keys).
335
+ * The key is the ID that was set on the DOM element during render,
336
+ * and the value is the ActionSheetButton config that contains the
337
+ * handler and other properties.
338
+ */
339
+ const buttonIdMap = new Map();
340
+ radioButtons.forEach((b) => {
341
+ const allIndex = allButtons.indexOf(b);
342
+ const buttonId = this.getButtonId(b, allIndex);
343
+ buttonIdMap.set(buttonId, b);
344
+ });
345
+ let nextEl;
346
+ if (['ArrowDown', 'ArrowRight'].includes(ev.key)) {
347
+ ev.preventDefault();
348
+ ev.stopPropagation();
349
+ nextEl = currentIndex === radios.length - 1 ? radios[0] : radios[currentIndex + 1];
350
+ }
351
+ else if (['ArrowUp', 'ArrowLeft'].includes(ev.key)) {
352
+ ev.preventDefault();
353
+ ev.stopPropagation();
354
+ nextEl = currentIndex === 0 ? radios[radios.length - 1] : radios[currentIndex - 1];
355
+ }
356
+ else if (ev.key === ' ' || ev.key === 'Enter') {
357
+ ev.preventDefault();
358
+ ev.stopPropagation();
359
+ const button = buttonIdMap.get(target.id);
360
+ if (button) {
361
+ this.selectRadioButton(button);
362
+ this.buttonClick(button);
363
+ }
364
+ return;
365
+ }
366
+ // Focus the next radio button
367
+ if (nextEl) {
368
+ const button = buttonIdMap.get(nextEl.id);
369
+ if (button) {
370
+ this.selectRadioButton(button);
371
+ nextEl.focus();
372
+ }
373
+ }
374
+ }
250
375
  connectedCallback() {
251
376
  prepareOverlay(this.el);
252
377
  this.triggerChanged();
@@ -263,6 +388,8 @@ const ActionSheet = /*@__PURE__*/ proxyCustomElement(class ActionSheet extends H
263
388
  if (!((_a = this.htmlAttributes) === null || _a === void 0 ? void 0 : _a.id)) {
264
389
  setOverlayId(this.el);
265
390
  }
391
+ // Initialize activeRadioId for radio buttons
392
+ this.buttonsChanged();
266
393
  }
267
394
  componentDidLoad() {
268
395
  /**
@@ -300,22 +427,74 @@ const ActionSheet = /*@__PURE__*/ proxyCustomElement(class ActionSheet extends H
300
427
  */
301
428
  this.triggerChanged();
302
429
  }
430
+ renderActionSheetButtons(filteredButtons) {
431
+ const mode = getIonMode(this);
432
+ const { activeRadioId } = this;
433
+ return filteredButtons.map((b, index) => {
434
+ var _a;
435
+ const isRadio = ((_a = b.htmlAttributes) === null || _a === void 0 ? void 0 : _a.role) === 'radio';
436
+ const buttonId = this.getButtonId(b, index);
437
+ const radioButtons = this.getRadioButtons();
438
+ const isActiveRadio = isRadio && buttonId === activeRadioId;
439
+ const isFirstRadio = isRadio && b === radioButtons[0];
440
+ // For radio buttons, set tabindex: 0 for the active one, -1 for others
441
+ // For non-radio buttons, use default tabindex (undefined, which means 0)
442
+ /**
443
+ * For radio buttons, set tabindex based on activeRadioId
444
+ * - If the button is the active radio, tabindex is 0
445
+ * - If no radio is active, the first radio button should have tabindex 0
446
+ * - All other radio buttons have tabindex -1
447
+ * For non-radio buttons, use default tabindex (undefined, which means 0)
448
+ */
449
+ let tabIndex;
450
+ if (isRadio) {
451
+ // Focus on the active radio button
452
+ if (isActiveRadio) {
453
+ tabIndex = 0;
454
+ }
455
+ else if (!activeRadioId && isFirstRadio) {
456
+ // No active radio, first radio gets focus
457
+ tabIndex = 0;
458
+ }
459
+ else {
460
+ // All other radios are not focusable
461
+ tabIndex = -1;
462
+ }
463
+ }
464
+ else {
465
+ tabIndex = undefined;
466
+ }
467
+ // For radio buttons, set aria-checked based on activeRadioId
468
+ // Otherwise, use the value from htmlAttributes if provided
469
+ const htmlAttrs = Object.assign({}, b.htmlAttributes);
470
+ if (isRadio) {
471
+ htmlAttrs['aria-checked'] = isActiveRadio ? 'true' : 'false';
472
+ }
473
+ 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
+ if (isRadio) {
475
+ this.selectRadioButton(b);
476
+ }
477
+ this.buttonClick(b);
478
+ }, disabled: b.disabled, tabIndex: tabIndex }), 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)));
479
+ });
480
+ }
303
481
  render() {
304
- const { header, htmlAttributes, overlayIndex } = this;
482
+ const { header, htmlAttributes, overlayIndex, hasRadioButtons } = this;
305
483
  const mode = getIonMode(this);
306
484
  const allButtons = this.getButtons();
307
485
  const cancelButton = allButtons.find((b) => b.role === 'cancel');
308
486
  const buttons = allButtons.filter((b) => b.role !== 'cancel');
309
487
  const headerID = `action-sheet-${overlayIndex}-header`;
310
- return (h(Host, Object.assign({ key: '9fef156b2a1f09ca4a6c1fe1f37c374139bde03c', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1" }, htmlAttributes, { style: {
488
+ return (h(Host, Object.assign({ key: '173fcff5b1da7c33c267de4667591c946b8c8d03', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1" }, htmlAttributes, { style: {
311
489
  zIndex: `${20000 + this.overlayIndex}`,
312
- }, 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: '81cf3f7d19864e041813987b46d2d115b8466819', tappable: this.backdropDismiss }), h("div", { key: '791c6a976683646fc306a42c15c5078b6f06a45f', tabindex: "0", "aria-hidden": "true" }), h("div", { key: 'a350b489ef7852eab9dc2227ce6d92da27dd9bf9', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, h("div", { key: '69ba51ee13510c1a411d87cb4845b11b7302a36f', class: "action-sheet-container" }, h("div", { key: 'bded15b8306c36591e526f0f99e1eeabcbab3915', class: "action-sheet-group", ref: (el) => (this.groupEl = el) }, header !== undefined && (h("div", { key: '06b5147c0f6d9180fe8f12e75c9b4a0310226adc', id: headerID, class: {
490
+ }, 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: {
313
491
  'action-sheet-title': true,
314
492
  'action-sheet-has-sub-title': this.subHeader !== undefined,
315
- } }, header, this.subHeader && h("div", { key: '54874362a75c679aba803bf4f8768f5404d2dd28', 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: '67b0de298eb424f3dea846a841b7a06d70e3930d', class: "action-sheet-group action-sheet-group-cancel" }, h("button", Object.assign({ key: 'e7e3f9a5495eea9b97dbf885ef36944f2e420eff' }, cancelButton.htmlAttributes, { type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) }), h("span", { key: 'f889d29ed6c3d14bbc1d805888351d87f5122377', class: "action-sheet-button-inner" }, cancelButton.icon && (h("ion-icon", { key: '7c05cf424b38c37fd40aaeb42a494387291571fb', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && h("ion-ripple-effect", { key: 'bed927b477dc2708a5123ef560274fca9819b3d6' })))))), h("div", { key: 'c5df1b11dc15a93892d57065d3dd5fbe02e43b39', tabindex: "0", "aria-hidden": "true" })));
493
+ } }, 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" })));
316
494
  }
317
495
  get el() { return this; }
318
496
  static get watchers() { return {
497
+ "buttons": ["buttonsChanged"],
319
498
  "isOpen": ["onIsOpenChange"],
320
499
  "trigger": ["triggerChanged"]
321
500
  }; }
@@ -340,11 +519,13 @@ const ActionSheet = /*@__PURE__*/ proxyCustomElement(class ActionSheet extends H
340
519
  "htmlAttributes": [16],
341
520
  "isOpen": [4, "is-open"],
342
521
  "trigger": [1],
522
+ "activeRadioId": [32],
343
523
  "present": [64],
344
524
  "dismiss": [64],
345
525
  "onDidDismiss": [64],
346
526
  "onWillDismiss": [64]
347
- }, undefined, {
527
+ }, [[0, "keydown", "onKeydown"]], {
528
+ "buttons": ["buttonsChanged"],
348
529
  "isOpen": ["onIsOpenChange"],
349
530
  "trigger": ["triggerChanged"]
350
531
  }]);
@@ -417,13 +417,18 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
417
417
  .filter((cls) => cls !== 'hydrated')
418
418
  .join(' ');
419
419
  const optClass = `${OPTION_CLASS} ${copyClasses}`;
420
+ const isSelected = isOptionSelected(selectValue, value, this.compareWith);
420
421
  return {
421
- role: isOptionSelected(selectValue, value, this.compareWith) ? 'selected' : '',
422
+ role: isSelected ? 'selected' : '',
422
423
  text: option.textContent,
423
424
  cssClass: optClass,
424
425
  handler: () => {
425
426
  this.setValue(value);
426
427
  },
428
+ htmlAttributes: {
429
+ 'aria-checked': isSelected ? 'true' : 'false',
430
+ role: 'radio',
431
+ },
427
432
  };
428
433
  });
429
434
  // Add "cancel" button
@@ -804,7 +809,7 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
804
809
  * TODO(FW-5592): Remove hasStartEndSlots condition
805
810
  */
806
811
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
807
- return (h(Host, { key: '35b5e18e6f79a802ff2d46d1242e80ff755cc0b9', onClick: this.onClick, class: createColorClasses(this.color, {
812
+ return (h(Host, { key: 'd8026835993d0e6dce747098f741a06ae4e4f54d', onClick: this.onClick, class: createColorClasses(this.color, {
808
813
  [mode]: true,
809
814
  'in-item': inItem,
810
815
  'in-item-color': hostContext('ion-item.ion-color', el),
@@ -822,7 +827,7 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
822
827
  [`select-justify-${justify}`]: justifyEnabled,
823
828
  [`select-shape-${shape}`]: shape !== undefined,
824
829
  [`select-label-placement-${labelPlacement}`]: true,
825
- }) }, h("label", { key: '6005b34a0c50bc4d7653a4276bc232ecd02e083c', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'c7e07aa81ae856c057f16275dd058f37c5670a47', class: "select-wrapper-inner" }, h("slot", { key: '7fc2deefe0424404caacdbbd9e08ed43ba55d28a', name: "start" }), h("div", { key: '157d74ee717b1bc30b5f1c233a09b0c8456aa68e', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), h("slot", { key: 'ea66db304528b82bf9317730b6dce3db2612f235', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && h("div", { key: '786eb1530b7476f0615d4e7c0bf4e7e4dc66509c', class: "select-highlight" })), this.renderBottomContent()));
830
+ }) }, h("label", { key: 'fcfb40209d6d07d49c7fdca4884b31abf6ac2567', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'f191664f2290c3890bde1156157c83a6ff17dbe2', class: "select-wrapper-inner" }, h("slot", { key: '317a28d1115b4214f291e228ce0fe6fc782e57d5', name: "start" }), h("div", { key: 'db68e18abd5ca3a1023d7c7b58bf89893ae18073', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), h("slot", { key: '4274e042267c2234a198b0f65c89477898d08130', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && h("div", { key: '2e2eb1ee2b2791e0683d9afb186fde6e938ca59c', class: "select-highlight" })), this.renderBottomContent()));
826
831
  }
827
832
  get el() { return this; }
828
833
  static get watchers() { return {
@@ -2288,6 +2288,17 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
2288
2288
  this.cachedOriginalParent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
2289
2289
  return;
2290
2290
  }
2291
+ /**
2292
+ * Don't observe for controller-based modals or when the parent is the
2293
+ * app root (document.body or ion-app). These parents won't be removed,
2294
+ * and observing document.body with subtree: true causes performance
2295
+ * issues with frameworks like Angular during change detection.
2296
+ */
2297
+ if (this.hasController ||
2298
+ this.cachedOriginalParent === document.body ||
2299
+ this.cachedOriginalParent.tagName === 'ION-APP') {
2300
+ return;
2301
+ }
2291
2302
  this.parentRemovalObserver = new MutationObserver((mutations) => {
2292
2303
  mutations.forEach((mutation) => {
2293
2304
  if (mutation.type === 'childList' && mutation.removedNodes.length > 0) {
@@ -2329,20 +2340,20 @@ const Modal = /*@__PURE__*/ proxyCustomElement(class Modal extends HTMLElement {
2329
2340
  const isCardModal = presentingElement !== undefined && mode === 'ios';
2330
2341
  const isHandleCycle = handleBehavior === 'cycle';
2331
2342
  const isSheetModalWithHandle = isSheetModal && showHandle;
2332
- return (h(Host, Object.assign({ key: '9e9a7bd591eb17a225a00b4fa2e379e94601d17f', "no-router": true,
2343
+ return (h(Host, Object.assign({ key: '5d8261a1a174d83642c0f7f2aa4f6c265f50fa57', "no-router": true,
2333
2344
  // Allow the modal to be navigable when the handle is focusable
2334
2345
  tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
2335
2346
  zIndex: `${20000 + this.overlayIndex}`,
2336
- }, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), h("ion-backdrop", { key: 'e5eae2c14f830f75e308fcd7f4c10c86fac5b962', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: 'e268f9cd310c3cf4e051b5b92524ce4fb70d005e', class: "modal-shadow" }), h("div", Object.assign({ key: '9c380f36c18144c153077b15744d1c3346bce63e',
2347
+ }, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), h("ion-backdrop", { key: 'bc165dd344e752c13076ca5ae37ea7d68d618d55', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: '4a2f6edaeeec2978f0cd7b2b93a44c2f0da3ab54', class: "modal-shadow" }), h("div", Object.assign({ key: '13d662aa50871e97567270dbbce6825633c62bad',
2337
2348
  /*
2338
2349
  role and aria-modal must be used on the
2339
2350
  same element. They must also be set inside the
2340
2351
  shadow DOM otherwise ion-button will not be highlighted
2341
2352
  when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
2342
2353
  */
2343
- role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '2d5ee6d5959d97309c306e8ce72eb0f2c19be144', class: "modal-handle",
2354
+ role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: 'e678cd3c64a0ab56636f68f9fa416741589f783c', class: "modal-handle",
2344
2355
  // Prevents the handle from receiving keyboard focus when it does not cycle
2345
- tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), h("slot", { key: '5590434c35ea04c42fc006498bc189038e15a298', onSlotchange: this.onSlotChange }))));
2356
+ tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), h("slot", { key: '1ebc3549c5c67ea286384b9917ed4dd8958ea2ae', onSlotchange: this.onSlotChange }))));
2346
2357
  }
2347
2358
  get el() { return this; }
2348
2359
  static get watchers() { return {
@@ -121,6 +121,7 @@ const ActionSheet = class {
121
121
  this.delegateController = overlays.createDelegateController(this);
122
122
  this.lockController = lockController.createLockController();
123
123
  this.triggerController = overlays.createTriggerController();
124
+ this.hasRadioButtons = false;
124
125
  this.presented = false;
125
126
  /** @internal */
126
127
  this.hasController = false;
@@ -165,6 +166,19 @@ const ActionSheet = class {
165
166
  }
166
167
  };
167
168
  }
169
+ buttonsChanged() {
170
+ const radioButtons = this.getRadioButtons();
171
+ this.hasRadioButtons = radioButtons.length > 0;
172
+ // Initialize activeRadioId when buttons change
173
+ if (this.hasRadioButtons) {
174
+ const checkedButton = radioButtons.find((b) => { var _a; return ((_a = b.htmlAttributes) === null || _a === void 0 ? void 0 : _a['aria-checked']) === 'true'; });
175
+ if (checkedButton) {
176
+ const allButtons = this.getButtons();
177
+ const checkedIndex = allButtons.indexOf(checkedButton);
178
+ this.activeRadioId = this.getButtonId(checkedButton, checkedIndex);
179
+ }
180
+ }
181
+ }
168
182
  onIsOpenChange(newValue, oldValue) {
169
183
  if (newValue === true && oldValue === false) {
170
184
  this.present();
@@ -245,11 +259,122 @@ const ActionSheet = class {
245
259
  }
246
260
  return true;
247
261
  }
262
+ /**
263
+ * Get all buttons regardless of role.
264
+ */
248
265
  getButtons() {
249
266
  return this.buttons.map((b) => {
250
267
  return typeof b === 'string' ? { text: b } : b;
251
268
  });
252
269
  }
270
+ /**
271
+ * Get all radio buttons (buttons with role="radio").
272
+ */
273
+ getRadioButtons() {
274
+ return this.getButtons().filter((b) => {
275
+ var _a;
276
+ const role = (_a = b.htmlAttributes) === null || _a === void 0 ? void 0 : _a.role;
277
+ return role === 'radio' && !overlays.isCancel(role);
278
+ });
279
+ }
280
+ /**
281
+ * Handle radio button selection and update aria-checked state.
282
+ *
283
+ * @param button The radio button that was selected.
284
+ */
285
+ selectRadioButton(button) {
286
+ const buttonId = this.getButtonId(button);
287
+ // Set the active radio ID (this will trigger a re-render and update aria-checked)
288
+ this.activeRadioId = buttonId;
289
+ }
290
+ /**
291
+ * Get or generate an ID for a button.
292
+ *
293
+ * @param button The button for which to get the ID.
294
+ * @param index Optional index of the button in the buttons array.
295
+ * @returns The ID of the button.
296
+ */
297
+ getButtonId(button, index) {
298
+ if (button.id) {
299
+ return button.id;
300
+ }
301
+ const allButtons = this.getButtons();
302
+ const buttonIndex = index !== undefined ? index : allButtons.indexOf(button);
303
+ return `action-sheet-button-${this.overlayIndex}-${buttonIndex}`;
304
+ }
305
+ /**
306
+ * When the action sheet has radio buttons, we want to follow the
307
+ * keyboard navigation pattern for radio groups:
308
+ * - Arrow Down/Right: Move to the next radio button (wrap to first if at end)
309
+ * - Arrow Up/Left: Move to the previous radio button (wrap to last if at start)
310
+ * - Space/Enter: Select the focused radio button and trigger its handler
311
+ */
312
+ onKeydown(ev) {
313
+ // Only handle keyboard navigation if we have radio buttons
314
+ if (!this.hasRadioButtons || !this.presented) {
315
+ return;
316
+ }
317
+ const target = ev.target;
318
+ // Ignore if the target element is not within the action sheet or not a radio button
319
+ if (!this.el.contains(target) ||
320
+ !target.classList.contains('action-sheet-button') ||
321
+ target.getAttribute('role') !== 'radio') {
322
+ return;
323
+ }
324
+ // Get all radio button elements and filter out disabled ones
325
+ const radios = Array.from(this.el.querySelectorAll('.action-sheet-button[role="radio"]')).filter((el) => !el.disabled);
326
+ const currentIndex = radios.findIndex((radio) => radio.id === target.id);
327
+ if (currentIndex === -1) {
328
+ return;
329
+ }
330
+ const allButtons = this.getButtons();
331
+ const radioButtons = this.getRadioButtons();
332
+ /**
333
+ * Build a map of button element IDs to their ActionSheetButton
334
+ * config objects.
335
+ * This allows us to quickly look up which button config corresponds
336
+ * to a DOM element when handling keyboard navigation
337
+ * (e.g., whenuser presses Space/Enter or arrow keys).
338
+ * The key is the ID that was set on the DOM element during render,
339
+ * and the value is the ActionSheetButton config that contains the
340
+ * handler and other properties.
341
+ */
342
+ const buttonIdMap = new Map();
343
+ radioButtons.forEach((b) => {
344
+ const allIndex = allButtons.indexOf(b);
345
+ const buttonId = this.getButtonId(b, allIndex);
346
+ buttonIdMap.set(buttonId, b);
347
+ });
348
+ let nextEl;
349
+ if (['ArrowDown', 'ArrowRight'].includes(ev.key)) {
350
+ ev.preventDefault();
351
+ ev.stopPropagation();
352
+ nextEl = currentIndex === radios.length - 1 ? radios[0] : radios[currentIndex + 1];
353
+ }
354
+ else if (['ArrowUp', 'ArrowLeft'].includes(ev.key)) {
355
+ ev.preventDefault();
356
+ ev.stopPropagation();
357
+ nextEl = currentIndex === 0 ? radios[radios.length - 1] : radios[currentIndex - 1];
358
+ }
359
+ else if (ev.key === ' ' || ev.key === 'Enter') {
360
+ ev.preventDefault();
361
+ ev.stopPropagation();
362
+ const button = buttonIdMap.get(target.id);
363
+ if (button) {
364
+ this.selectRadioButton(button);
365
+ this.buttonClick(button);
366
+ }
367
+ return;
368
+ }
369
+ // Focus the next radio button
370
+ if (nextEl) {
371
+ const button = buttonIdMap.get(nextEl.id);
372
+ if (button) {
373
+ this.selectRadioButton(button);
374
+ nextEl.focus();
375
+ }
376
+ }
377
+ }
253
378
  connectedCallback() {
254
379
  overlays.prepareOverlay(this.el);
255
380
  this.triggerChanged();
@@ -266,6 +391,8 @@ const ActionSheet = class {
266
391
  if (!((_a = this.htmlAttributes) === null || _a === void 0 ? void 0 : _a.id)) {
267
392
  overlays.setOverlayId(this.el);
268
393
  }
394
+ // Initialize activeRadioId for radio buttons
395
+ this.buttonsChanged();
269
396
  }
270
397
  componentDidLoad() {
271
398
  /**
@@ -303,22 +430,74 @@ const ActionSheet = class {
303
430
  */
304
431
  this.triggerChanged();
305
432
  }
433
+ renderActionSheetButtons(filteredButtons) {
434
+ const mode = ionicGlobal.getIonMode(this);
435
+ const { activeRadioId } = this;
436
+ return filteredButtons.map((b, index$1) => {
437
+ var _a;
438
+ const isRadio = ((_a = b.htmlAttributes) === null || _a === void 0 ? void 0 : _a.role) === 'radio';
439
+ const buttonId = this.getButtonId(b, index$1);
440
+ const radioButtons = this.getRadioButtons();
441
+ const isActiveRadio = isRadio && buttonId === activeRadioId;
442
+ const isFirstRadio = isRadio && b === radioButtons[0];
443
+ // For radio buttons, set tabindex: 0 for the active one, -1 for others
444
+ // For non-radio buttons, use default tabindex (undefined, which means 0)
445
+ /**
446
+ * For radio buttons, set tabindex based on activeRadioId
447
+ * - If the button is the active radio, tabindex is 0
448
+ * - If no radio is active, the first radio button should have tabindex 0
449
+ * - All other radio buttons have tabindex -1
450
+ * For non-radio buttons, use default tabindex (undefined, which means 0)
451
+ */
452
+ let tabIndex;
453
+ if (isRadio) {
454
+ // Focus on the active radio button
455
+ if (isActiveRadio) {
456
+ tabIndex = 0;
457
+ }
458
+ else if (!activeRadioId && isFirstRadio) {
459
+ // No active radio, first radio gets focus
460
+ tabIndex = 0;
461
+ }
462
+ else {
463
+ // All other radios are not focusable
464
+ tabIndex = -1;
465
+ }
466
+ }
467
+ else {
468
+ tabIndex = undefined;
469
+ }
470
+ // For radio buttons, set aria-checked based on activeRadioId
471
+ // Otherwise, use the value from htmlAttributes if provided
472
+ const htmlAttrs = Object.assign({}, b.htmlAttributes);
473
+ if (isRadio) {
474
+ htmlAttrs['aria-checked'] = isActiveRadio ? 'true' : 'false';
475
+ }
476
+ return (index.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: () => {
477
+ if (isRadio) {
478
+ this.selectRadioButton(b);
479
+ }
480
+ this.buttonClick(b);
481
+ }, disabled: b.disabled, tabIndex: tabIndex }), index.h("span", { class: "action-sheet-button-inner" }, b.icon && index.h("ion-icon", { icon: b.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" }), b.text), mode === 'md' && index.h("ion-ripple-effect", null)));
482
+ });
483
+ }
306
484
  render() {
307
- const { header, htmlAttributes, overlayIndex } = this;
485
+ const { header, htmlAttributes, overlayIndex, hasRadioButtons } = this;
308
486
  const mode = ionicGlobal.getIonMode(this);
309
487
  const allButtons = this.getButtons();
310
488
  const cancelButton = allButtons.find((b) => b.role === 'cancel');
311
489
  const buttons = allButtons.filter((b) => b.role !== 'cancel');
312
490
  const headerID = `action-sheet-${overlayIndex}-header`;
313
- return (index.h(index.Host, Object.assign({ key: '9fef156b2a1f09ca4a6c1fe1f37c374139bde03c', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1" }, htmlAttributes, { style: {
491
+ return (index.h(index.Host, Object.assign({ key: '173fcff5b1da7c33c267de4667591c946b8c8d03', role: "dialog", "aria-modal": "true", "aria-labelledby": header !== undefined ? headerID : null, tabindex: "-1" }, htmlAttributes, { style: {
314
492
  zIndex: `${20000 + this.overlayIndex}`,
315
- }, class: Object.assign(Object.assign({ [mode]: true }, theme.getClassMap(this.cssClass)), { 'overlay-hidden': true, 'action-sheet-translucent': this.translucent }), onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }), index.h("ion-backdrop", { key: '81cf3f7d19864e041813987b46d2d115b8466819', tappable: this.backdropDismiss }), index.h("div", { key: '791c6a976683646fc306a42c15c5078b6f06a45f', tabindex: "0", "aria-hidden": "true" }), index.h("div", { key: 'a350b489ef7852eab9dc2227ce6d92da27dd9bf9', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, index.h("div", { key: '69ba51ee13510c1a411d87cb4845b11b7302a36f', class: "action-sheet-container" }, index.h("div", { key: 'bded15b8306c36591e526f0f99e1eeabcbab3915', class: "action-sheet-group", ref: (el) => (this.groupEl = el) }, header !== undefined && (index.h("div", { key: '06b5147c0f6d9180fe8f12e75c9b4a0310226adc', id: headerID, class: {
493
+ }, class: Object.assign(Object.assign({ [mode]: true }, theme.getClassMap(this.cssClass)), { 'overlay-hidden': true, 'action-sheet-translucent': this.translucent }), onIonActionSheetWillDismiss: this.dispatchCancelHandler, onIonBackdropTap: this.onBackdropTap }), index.h("ion-backdrop", { key: '521ede659f747864f6c974e09016436eceb7158c', tappable: this.backdropDismiss }), index.h("div", { key: '7a7946fc434bc444f16a70638f5e948c69d33fcd', tabindex: "0", "aria-hidden": "true" }), index.h("div", { key: 'bcff39a580489dbafa255842e57aa8602c6d0f18', class: "action-sheet-wrapper ion-overlay-wrapper", ref: (el) => (this.wrapperEl = el) }, index.h("div", { key: '84bba13ce14261f0f0daa3f9c77648c9e7f36e0e', class: "action-sheet-container" }, index.h("div", { key: 'd9c8ac404fd6719a7adf8cb36549f67616f9a0c4', class: "action-sheet-group", ref: (el) => (this.groupEl = el), role: hasRadioButtons ? 'radiogroup' : undefined }, header !== undefined && (index.h("div", { key: '180433a8ad03ef5c54728a1a8f34715b6921d658', id: headerID, class: {
316
494
  'action-sheet-title': true,
317
495
  'action-sheet-has-sub-title': this.subHeader !== undefined,
318
- } }, header, this.subHeader && index.h("div", { key: '54874362a75c679aba803bf4f8768f5404d2dd28', class: "action-sheet-sub-title" }, this.subHeader))), buttons.map((b) => (index.h("button", Object.assign({}, b.htmlAttributes, { type: "button", id: b.id, class: buttonClass(b), onClick: () => this.buttonClick(b), disabled: b.disabled }), index.h("span", { class: "action-sheet-button-inner" }, b.icon && index.h("ion-icon", { icon: b.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" }), b.text), mode === 'md' && index.h("ion-ripple-effect", null))))), cancelButton && (index.h("div", { key: '67b0de298eb424f3dea846a841b7a06d70e3930d', class: "action-sheet-group action-sheet-group-cancel" }, index.h("button", Object.assign({ key: 'e7e3f9a5495eea9b97dbf885ef36944f2e420eff' }, cancelButton.htmlAttributes, { type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) }), index.h("span", { key: 'f889d29ed6c3d14bbc1d805888351d87f5122377', class: "action-sheet-button-inner" }, cancelButton.icon && (index.h("ion-icon", { key: '7c05cf424b38c37fd40aaeb42a494387291571fb', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && index.h("ion-ripple-effect", { key: 'bed927b477dc2708a5123ef560274fca9819b3d6' })))))), index.h("div", { key: 'c5df1b11dc15a93892d57065d3dd5fbe02e43b39', tabindex: "0", "aria-hidden": "true" })));
496
+ } }, header, this.subHeader && index.h("div", { key: '7138e79e61b1a8f42bc5a9175c57fa2f15d7ec5a', class: "action-sheet-sub-title" }, this.subHeader))), this.renderActionSheetButtons(buttons)), cancelButton && (index.h("div", { key: 'b617c722f5b8028d73ed34b69310f312c65f34a7', class: "action-sheet-group action-sheet-group-cancel" }, index.h("button", Object.assign({ key: 'd0dd876fc48815df3710413c201c0b445a8e16c0' }, cancelButton.htmlAttributes, { type: "button", class: buttonClass(cancelButton), onClick: () => this.buttonClick(cancelButton) }), index.h("span", { key: 'e7b960157cc6fc5fe92a12090b2be55e8ae072e4', class: "action-sheet-button-inner" }, cancelButton.icon && (index.h("ion-icon", { key: '05498ffc60cab911dbff0ecbc6168dea59ada9a5', icon: cancelButton.icon, "aria-hidden": "true", lazy: false, class: "action-sheet-icon" })), cancelButton.text), mode === 'md' && index.h("ion-ripple-effect", { key: '3d401346cea301be4ca03671f7370f6f4b0b6bde' })))))), index.h("div", { key: '971f3c5fcc07f36c28eb469a47ec0290c692e139', tabindex: "0", "aria-hidden": "true" })));
319
497
  }
320
498
  get el() { return index.getElement(this); }
321
499
  static get watchers() { return {
500
+ "buttons": ["buttonsChanged"],
322
501
  "isOpen": ["onIsOpenChange"],
323
502
  "trigger": ["triggerChanged"]
324
503
  }; }
@@ -2287,6 +2287,17 @@ const Modal = class {
2287
2287
  this.cachedOriginalParent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
2288
2288
  return;
2289
2289
  }
2290
+ /**
2291
+ * Don't observe for controller-based modals or when the parent is the
2292
+ * app root (document.body or ion-app). These parents won't be removed,
2293
+ * and observing document.body with subtree: true causes performance
2294
+ * issues with frameworks like Angular during change detection.
2295
+ */
2296
+ if (this.hasController ||
2297
+ this.cachedOriginalParent === document.body ||
2298
+ this.cachedOriginalParent.tagName === 'ION-APP') {
2299
+ return;
2300
+ }
2290
2301
  this.parentRemovalObserver = new MutationObserver((mutations) => {
2291
2302
  mutations.forEach((mutation) => {
2292
2303
  if (mutation.type === 'childList' && mutation.removedNodes.length > 0) {
@@ -2328,20 +2339,20 @@ const Modal = class {
2328
2339
  const isCardModal = presentingElement !== undefined && mode === 'ios';
2329
2340
  const isHandleCycle = handleBehavior === 'cycle';
2330
2341
  const isSheetModalWithHandle = isSheetModal && showHandle;
2331
- return (index$3.h(index$3.Host, Object.assign({ key: '9e9a7bd591eb17a225a00b4fa2e379e94601d17f', "no-router": true,
2342
+ return (index$3.h(index$3.Host, Object.assign({ key: '5d8261a1a174d83642c0f7f2aa4f6c265f50fa57', "no-router": true,
2332
2343
  // Allow the modal to be navigable when the handle is focusable
2333
2344
  tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
2334
2345
  zIndex: `${20000 + this.overlayIndex}`,
2335
- }, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [overlays.FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, theme.getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), index$3.h("ion-backdrop", { key: 'e5eae2c14f830f75e308fcd7f4c10c86fac5b962', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && index$3.h("div", { key: 'e268f9cd310c3cf4e051b5b92524ce4fb70d005e', class: "modal-shadow" }), index$3.h("div", Object.assign({ key: '9c380f36c18144c153077b15744d1c3346bce63e',
2346
+ }, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [overlays.FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, theme.getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), index$3.h("ion-backdrop", { key: 'bc165dd344e752c13076ca5ae37ea7d68d618d55', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && index$3.h("div", { key: '4a2f6edaeeec2978f0cd7b2b93a44c2f0da3ab54', class: "modal-shadow" }), index$3.h("div", Object.assign({ key: '13d662aa50871e97567270dbbce6825633c62bad',
2336
2347
  /*
2337
2348
  role and aria-modal must be used on the
2338
2349
  same element. They must also be set inside the
2339
2350
  shadow DOM otherwise ion-button will not be highlighted
2340
2351
  when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
2341
2352
  */
2342
- role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (index$3.h("button", { key: '2d5ee6d5959d97309c306e8ce72eb0f2c19be144', class: "modal-handle",
2353
+ role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (index$3.h("button", { key: 'e678cd3c64a0ab56636f68f9fa416741589f783c', class: "modal-handle",
2343
2354
  // Prevents the handle from receiving keyboard focus when it does not cycle
2344
- tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), index$3.h("slot", { key: '5590434c35ea04c42fc006498bc189038e15a298', onSlotchange: this.onSlotChange }))));
2355
+ tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), index$3.h("slot", { key: '1ebc3549c5c67ea286384b9917ed4dd8958ea2ae', onSlotchange: this.onSlotChange }))));
2345
2356
  }
2346
2357
  get el() { return index$3.getElement(this); }
2347
2358
  static get watchers() { return {