@ionic/core 8.7.7-nightly.20251015 → 8.7.8-dev.11760615726.158c2a8a

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 (62) hide show
  1. package/components/button.js +3 -7
  2. package/components/header.js +41 -3
  3. package/components/ion-accordion-group.js +28 -26
  4. package/components/ion-accordion.js +7 -3
  5. package/components/ion-input.js +6 -14
  6. package/components/ion-select.js +58 -10
  7. package/components/ion-textarea.js +5 -13
  8. package/components/{notch-controller.js → validity.js} +14 -1
  9. package/dist/cjs/ion-accordion_2.cjs.entry.js +35 -29
  10. package/dist/cjs/ion-app_8.cjs.entry.js +41 -3
  11. package/dist/cjs/ion-button_2.cjs.entry.js +3 -7
  12. package/dist/cjs/ion-input.cjs.entry.js +7 -15
  13. package/dist/cjs/ion-select_3.cjs.entry.js +56 -10
  14. package/dist/cjs/ion-textarea.cjs.entry.js +6 -14
  15. package/dist/cjs/ionic.cjs.js +1 -1
  16. package/dist/cjs/loader.cjs.js +1 -1
  17. package/dist/cjs/{notch-controller-Bzqhjm4f.js → validity-C8QoAYT2.js} +14 -0
  18. package/dist/collection/components/accordion/accordion.js +7 -3
  19. package/dist/collection/components/accordion-group/accordion-group.js +28 -26
  20. package/dist/collection/components/button/button.js +3 -7
  21. package/dist/collection/components/header/header.js +5 -4
  22. package/dist/collection/components/header/header.utils.js +37 -0
  23. package/dist/collection/components/input/input.js +6 -14
  24. package/dist/collection/components/select/select.js +59 -11
  25. package/dist/collection/components/textarea/textarea.js +5 -13
  26. package/dist/collection/utils/forms/index.js +1 -0
  27. package/dist/collection/utils/forms/validity.js +15 -0
  28. package/dist/docs.json +2 -2
  29. package/dist/esm/ion-accordion_2.entry.js +35 -29
  30. package/dist/esm/ion-app_8.entry.js +41 -3
  31. package/dist/esm/ion-button_2.entry.js +3 -7
  32. package/dist/esm/ion-input.entry.js +6 -14
  33. package/dist/esm/ion-select_3.entry.js +55 -9
  34. package/dist/esm/ion-textarea.entry.js +5 -13
  35. package/dist/esm/ionic.js +1 -1
  36. package/dist/esm/loader.js +1 -1
  37. package/dist/esm/{notch-controller-BwelN_JM.js → validity-B8oWougr.js} +14 -1
  38. package/dist/ionic/ionic.esm.js +1 -1
  39. package/dist/ionic/p-16280296.entry.js +4 -0
  40. package/dist/ionic/p-43ed1ef5.entry.js +4 -0
  41. package/dist/ionic/p-4c85d268.entry.js +4 -0
  42. package/dist/ionic/p-4cc26913.entry.js +4 -0
  43. package/dist/ionic/p-8bdfc8f6.entry.js +4 -0
  44. package/dist/ionic/{p-DCv9sLH2.js → p-DieJyvMP.js} +1 -1
  45. package/dist/ionic/p-f65f9308.entry.js +4 -0
  46. package/dist/types/components/accordion-group/accordion-group-interface.d.ts +1 -0
  47. package/dist/types/components/accordion-group/accordion-group.d.ts +1 -0
  48. package/dist/types/components/header/header.utils.d.ts +10 -0
  49. package/dist/types/components/input/input.d.ts +0 -4
  50. package/dist/types/components/select/select.d.ts +6 -0
  51. package/dist/types/components/textarea/textarea.d.ts +0 -4
  52. package/dist/types/utils/forms/index.d.ts +1 -0
  53. package/dist/types/utils/forms/validity.d.ts +10 -0
  54. package/hydrate/index.js +121 -70
  55. package/hydrate/index.mjs +121 -70
  56. package/package.json +2 -2
  57. package/dist/ionic/p-1c8a476d.entry.js +0 -4
  58. package/dist/ionic/p-62e50f80.entry.js +0 -4
  59. package/dist/ionic/p-7647da93.entry.js +0 -4
  60. package/dist/ionic/p-785026d7.entry.js +0 -4
  61. package/dist/ionic/p-78c74a3e.entry.js +0 -4
  62. package/dist/ionic/p-913a7c1e.entry.js +0 -4
@@ -4,6 +4,8 @@
4
4
  import { readTask, writeTask } from "@stencil/core";
5
5
  import { clamp } from "../../utils/helpers";
6
6
  const TRANSITION = 'all 0.2s ease-in-out';
7
+ const ROLE_NONE = 'none';
8
+ const ROLE_BANNER = 'banner';
7
9
  export const cloneElement = (tagName) => {
8
10
  const getCachedEl = document.querySelector(`${tagName}.ion-cloned-element`);
9
11
  if (getCachedEl !== null) {
@@ -130,6 +132,7 @@ export const setHeaderActive = (headerIndex, active = true) => {
130
132
  const toolbars = headerIndex.toolbars;
131
133
  const ionTitles = toolbars.map((toolbar) => toolbar.ionTitleEl);
132
134
  if (active) {
135
+ headerEl.setAttribute('role', ROLE_BANNER);
133
136
  headerEl.classList.remove('header-collapse-condense-inactive');
134
137
  ionTitles.forEach((ionTitle) => {
135
138
  if (ionTitle) {
@@ -138,6 +141,16 @@ export const setHeaderActive = (headerIndex, active = true) => {
138
141
  });
139
142
  }
140
143
  else {
144
+ /**
145
+ * There can only be one banner landmark per page.
146
+ * By default, all ion-headers have the banner role.
147
+ * This causes an accessibility issue when using a
148
+ * condensed header since there are two ion-headers
149
+ * on the page at once (active and inactive).
150
+ * To solve this, the role needs to be toggled
151
+ * based on which header is active.
152
+ */
153
+ headerEl.setAttribute('role', ROLE_NONE);
141
154
  headerEl.classList.add('header-collapse-condense-inactive');
142
155
  /**
143
156
  * The small title should only be accessed by screen readers
@@ -197,3 +210,27 @@ export const handleHeaderFade = (scrollEl, baseEl, condenseHeader) => {
197
210
  });
198
211
  });
199
212
  };
213
+ /**
214
+ * Get the role type for the ion-header.
215
+ *
216
+ * @param isInsideMenu If ion-header is inside ion-menu.
217
+ * @param isCondensed If ion-header has collapse="condense".
218
+ * @param mode The current mode.
219
+ * @returns 'none' if inside ion-menu or if condensed in md
220
+ * mode, otherwise 'banner'.
221
+ */
222
+ export const getRoleType = (isInsideMenu, isCondensed, mode) => {
223
+ // If the header is inside a menu, it should not have the banner role.
224
+ if (isInsideMenu) {
225
+ return ROLE_NONE;
226
+ }
227
+ /**
228
+ * Only apply role="none" to `md` mode condensed headers
229
+ * since the large header is never shown.
230
+ */
231
+ if (isCondensed && mode === 'md') {
232
+ return ROLE_NONE;
233
+ }
234
+ // Default to banner role.
235
+ return ROLE_BANNER;
236
+ };
@@ -2,7 +2,7 @@
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
4
  import { Build, Host, forceUpdate, h, } from "@stencil/core";
5
- import { createNotchController } from "../../utils/forms/index";
5
+ import { createNotchController, checkInvalidState } from "../../utils/forms/index";
6
6
  import { inheritAriaAttributes, debounceEvent, inheritAttributes, componentOnReady } from "../../utils/helpers";
7
7
  import { createSlotMutationController } from "../../utils/slot-mutation-controller";
8
8
  import { createColorClasses, hostContext } from "../../utils/theme";
@@ -227,14 +227,6 @@ export class Input {
227
227
  componentWillLoad() {
228
228
  this.inheritedAttributes = Object.assign(Object.assign({}, inheritAriaAttributes(this.el)), inheritAttributes(this.el, ['tabindex', 'title', 'data-form-type', 'dir']));
229
229
  }
230
- /**
231
- * Checks if the input is in an invalid state based on Ionic validation classes
232
- */
233
- checkInvalidState() {
234
- const hasIonTouched = this.el.classList.contains('ion-touched');
235
- const hasIonInvalid = this.el.classList.contains('ion-invalid');
236
- return hasIonTouched && hasIonInvalid;
237
- }
238
230
  connectedCallback() {
239
231
  const { el } = this;
240
232
  this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
@@ -242,7 +234,7 @@ export class Input {
242
234
  // Watch for class changes to update validation state
243
235
  if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
244
236
  this.validationObserver = new MutationObserver(() => {
245
- const newIsInvalid = this.checkInvalidState();
237
+ const newIsInvalid = checkInvalidState(el);
246
238
  if (this.isInvalid !== newIsInvalid) {
247
239
  this.isInvalid = newIsInvalid;
248
240
  // Force a re-render to update aria-describedby immediately
@@ -255,7 +247,7 @@ export class Input {
255
247
  });
256
248
  }
257
249
  // Always set initial state
258
- this.isInvalid = this.checkInvalidState();
250
+ this.isInvalid = checkInvalidState(el);
259
251
  this.debounceChanged();
260
252
  if (Build.isBrowser) {
261
253
  document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
@@ -519,7 +511,7 @@ export class Input {
519
511
  * TODO(FW-5592): Remove hasStartEndSlots condition
520
512
  */
521
513
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
522
- return (h(Host, { key: '8a51f0300d5bc66392f9ab9a6fa0b5d388072a33', class: createColorClasses(this.color, {
514
+ return (h(Host, { key: '97b5308021064d9e7434ef2d3d96f27045c1b0c4', class: createColorClasses(this.color, {
523
515
  [mode]: true,
524
516
  'has-value': hasValue,
525
517
  'has-focus': hasFocus,
@@ -530,14 +522,14 @@ export class Input {
530
522
  'in-item': inItem,
531
523
  'in-item-color': hostContext('ion-item.ion-color', this.el),
532
524
  'input-disabled': disabled,
533
- }) }, h("label", { key: '9f8cf88d7d0e27931b51bd9c67f048c7fc6f5703', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '7ad30bf9777774062a6ccf9a3ba804f251eef1bb', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: '8af0b0325d101df8eed7d24f2767d6ca4d307319', name: "start" }), h("input", Object.assign({ key: '1c53f7f9fa2567f3df19681cf4e7c21be382eae6', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: 'b081d0e1ec1444b4c9cca145fc9cd2ad4a68b3da', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
525
+ }) }, h("label", { key: '353f68726ce180299bd9adc81e5ff7d26a48f54f', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '2034b4bad04fc157f3298a1805819216b6f439d0', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: '96bb5e30176b2bd76dfb75bfbf6c1c3d4403f4bb', name: "start" }), h("input", Object.assign({ key: '1a1d75b0e414a95c89d5a760757c33548d234aca', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: '95f3df17b7691d9a2e7dcd4a51f16a94aa3ca36f', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
534
526
  /**
535
527
  * This prevents mobile browsers from
536
528
  * blurring the input when the clear
537
529
  * button is activated.
538
530
  */
539
531
  ev.preventDefault();
540
- }, onClick: this.clearTextInput }, h("ion-icon", { key: '01535299241c3635460c05646420acf62a1ff567', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '480f3eb58b08ae792866a5b9b4c068748c5567cc', name: "end" })), shouldRenderHighlight && h("div", { key: 'a8609cacee88e4a09f1cca65b6a47cb79a56f35e', class: "input-highlight" })), this.renderBottomContent()));
532
+ }, onClick: this.clearTextInput }, h("ion-icon", { key: '16b0af75eed50c8115fb5597f73b5fbf71c2530e', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: 'c48da0f8ddb3764ac43efa705bb4a6bb2d9cc2fd', name: "end" })), shouldRenderHighlight && h("div", { key: 'f15238481fc20de56ca7ecb6e350b3c024cc755e', class: "input-highlight" })), this.renderBottomContent()));
541
533
  }
542
534
  static get is() { return "ion-input"; }
543
535
  static get encapsulation() { return "scoped"; }
@@ -1,8 +1,8 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- import { Host, h, forceUpdate } from "@stencil/core";
5
- import { compareOptions, createNotchController, isOptionSelected } from "../../utils/forms/index";
4
+ import { Build, Host, h, forceUpdate } from "@stencil/core";
5
+ import { compareOptions, createNotchController, isOptionSelected, checkInvalidState } from "../../utils/forms/index";
6
6
  import { focusVisibleElement, renderHiddenInput, inheritAttributes } from "../../utils/helpers";
7
7
  import { printIonWarning } from "../../utils/logging/index";
8
8
  import { actionSheetController, alertController, popoverController, modalController } from "../../utils/overlays";
@@ -44,6 +44,10 @@ export class Select {
44
44
  * is applied in both cases.
45
45
  */
46
46
  this.hasFocus = false;
47
+ /**
48
+ * Track validation state for proper aria-live announcements.
49
+ */
50
+ this.isInvalid = false;
47
51
  /**
48
52
  * The text to display on the cancel button.
49
53
  */
@@ -173,9 +177,46 @@ export class Select {
173
177
  */
174
178
  forceUpdate(this);
175
179
  });
180
+ // Watch for class changes to update validation state.
181
+ if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
182
+ this.validationObserver = new MutationObserver(() => {
183
+ const newIsInvalid = checkInvalidState(this.el);
184
+ if (this.isInvalid !== newIsInvalid) {
185
+ this.isInvalid = newIsInvalid;
186
+ /**
187
+ * Screen readers tend to announce changes
188
+ * to `aria-describedby` when the attribute
189
+ * is changed during a blur event for a
190
+ * native form control.
191
+ * However, the announcement can be spotty
192
+ * when using a non-native form control
193
+ * and `forceUpdate()`.
194
+ * This is due to `forceUpdate()` internally
195
+ * rescheduling the DOM update to a lower
196
+ * priority queue regardless if it's called
197
+ * inside a Promise or not, thus causing
198
+ * the screen reader to potentially miss the
199
+ * change.
200
+ * By using a State variable inside a Promise,
201
+ * it guarantees a re-render immediately at
202
+ * a higher priority.
203
+ */
204
+ Promise.resolve().then(() => {
205
+ this.hintTextID = this.getHintTextID();
206
+ });
207
+ }
208
+ });
209
+ this.validationObserver.observe(el, {
210
+ attributes: true,
211
+ attributeFilter: ['class'],
212
+ });
213
+ }
214
+ // Always set initial state
215
+ this.isInvalid = checkInvalidState(this.el);
176
216
  }
177
217
  componentWillLoad() {
178
218
  this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
219
+ this.hintTextID = this.getHintTextID();
179
220
  }
180
221
  componentDidLoad() {
181
222
  /**
@@ -199,6 +240,11 @@ export class Select {
199
240
  this.notchController.destroy();
200
241
  this.notchController = undefined;
201
242
  }
243
+ // Clean up validation observer to prevent memory leaks.
244
+ if (this.validationObserver) {
245
+ this.validationObserver.disconnect();
246
+ this.validationObserver = undefined;
247
+ }
202
248
  }
203
249
  /**
204
250
  * Open the select overlay. The overlay is either an alert, action sheet, or popover,
@@ -715,11 +761,11 @@ export class Select {
715
761
  }
716
762
  renderListbox() {
717
763
  const { disabled, inputId, isExpanded, required } = this;
718
- return (h("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "dialog", "aria-expanded": `${isExpanded}`, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId, "aria-required": `${required}`, onFocus: this.onFocus, onBlur: this.onBlur, ref: (focusEl) => (this.focusEl = focusEl) }));
764
+ return (h("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "dialog", "aria-expanded": `${isExpanded}`, "aria-describedby": this.hintTextID, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-required": `${required}`, onFocus: this.onFocus, onBlur: this.onBlur, ref: (focusEl) => (this.focusEl = focusEl) }));
719
765
  }
720
766
  getHintTextID() {
721
- const { el, helperText, errorText, helperTextId, errorTextId } = this;
722
- if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
767
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
768
+ if (isInvalid && errorText) {
723
769
  return errorTextId;
724
770
  }
725
771
  if (helperText) {
@@ -731,10 +777,10 @@ export class Select {
731
777
  * Renders the helper text or error text values
732
778
  */
733
779
  renderHintText() {
734
- const { helperText, errorText, helperTextId, errorTextId } = this;
780
+ const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
735
781
  return [
736
- h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText),
737
- h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText),
782
+ h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null),
783
+ h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null),
738
784
  ];
739
785
  }
740
786
  /**
@@ -782,7 +828,7 @@ export class Select {
782
828
  * TODO(FW-5592): Remove hasStartEndSlots condition
783
829
  */
784
830
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
785
- return (h(Host, { key: 'c03fb65e8fc9f9aab295e07b282377d57d910519', onClick: this.onClick, class: createColorClasses(this.color, {
831
+ return (h(Host, { key: '35b5e18e6f79a802ff2d46d1242e80ff755cc0b9', onClick: this.onClick, class: createColorClasses(this.color, {
786
832
  [mode]: true,
787
833
  'in-item': inItem,
788
834
  'in-item-color': hostContext('ion-item.ion-color', el),
@@ -800,7 +846,7 @@ export class Select {
800
846
  [`select-justify-${justify}`]: justifyEnabled,
801
847
  [`select-shape-${shape}`]: shape !== undefined,
802
848
  [`select-label-placement-${labelPlacement}`]: true,
803
- }) }, h("label", { key: '0d0c8ec55269adcac625f2899a547f4e7f3e3741', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'f6dfc93c0e23cbe75a2947abde67d842db2dad78', class: "select-wrapper-inner" }, h("slot", { key: '957bfadf9f101f519091419a362d3abdc2be66f6', name: "start" }), h("div", { key: 'ca349202a484e7f2e884533fd330f0b136754f7d', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), h("slot", { key: 'f0e62a6533ff1c8f62bd2d27f60b23385c4fa9ed', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && h("div", { key: 'fb840d46bafafb09898ebeebbe8c181906a3d8a2', class: "select-highlight" })), this.renderBottomContent()));
849
+ }) }, 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()));
804
850
  }
805
851
  static get is() { return "ion-select"; }
806
852
  static get encapsulation() { return "shadow"; }
@@ -1268,7 +1314,9 @@ export class Select {
1268
1314
  static get states() {
1269
1315
  return {
1270
1316
  "isExpanded": {},
1271
- "hasFocus": {}
1317
+ "hasFocus": {},
1318
+ "isInvalid": {},
1319
+ "hintTextID": {}
1272
1320
  };
1273
1321
  }
1274
1322
  static get events() {
@@ -2,7 +2,7 @@
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
4
  import { Build, Host, forceUpdate, h, writeTask, } from "@stencil/core";
5
- import { createNotchController } from "../../utils/forms/index";
5
+ import { createNotchController, checkInvalidState } from "../../utils/forms/index";
6
6
  import { inheritAriaAttributes, debounceEvent, inheritAttributes, componentOnReady } from "../../utils/helpers";
7
7
  import { createSlotMutationController } from "../../utils/slot-mutation-controller";
8
8
  import { createColorClasses, hostContext } from "../../utils/theme";
@@ -187,14 +187,6 @@ export class Textarea {
187
187
  this.el.click();
188
188
  }
189
189
  }
190
- /**
191
- * Checks if the textarea is in an invalid state based on Ionic validation classes
192
- */
193
- checkValidationState() {
194
- const hasIonTouched = this.el.classList.contains('ion-touched');
195
- const hasIonInvalid = this.el.classList.contains('ion-invalid');
196
- return hasIonTouched && hasIonInvalid;
197
- }
198
190
  connectedCallback() {
199
191
  const { el } = this;
200
192
  this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
@@ -202,7 +194,7 @@ export class Textarea {
202
194
  // Watch for class changes to update validation state
203
195
  if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
204
196
  this.validationObserver = new MutationObserver(() => {
205
- const newIsInvalid = this.checkValidationState();
197
+ const newIsInvalid = checkInvalidState(this.el);
206
198
  if (this.isInvalid !== newIsInvalid) {
207
199
  this.isInvalid = newIsInvalid;
208
200
  // Force a re-render to update aria-describedby immediately
@@ -215,7 +207,7 @@ export class Textarea {
215
207
  });
216
208
  }
217
209
  // Always set initial state
218
- this.isInvalid = this.checkValidationState();
210
+ this.isInvalid = checkInvalidState(this.el);
219
211
  this.debounceChanged();
220
212
  if (Build.isBrowser) {
221
213
  document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
@@ -479,7 +471,7 @@ export class Textarea {
479
471
  * TODO(FW-5592): Remove hasStartEndSlots condition
480
472
  */
481
473
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
482
- return (h(Host, { key: '26b46666a92b3f652775bb1c46661f9a30392104', class: createColorClasses(this.color, {
474
+ return (h(Host, { key: 'a70a62d7aae3831a50acd74f60b930925ada1326', class: createColorClasses(this.color, {
483
475
  [mode]: true,
484
476
  'has-value': hasValue,
485
477
  'has-focus': hasFocus,
@@ -488,7 +480,7 @@ export class Textarea {
488
480
  [`textarea-shape-${shape}`]: shape !== undefined,
489
481
  [`textarea-label-placement-${labelPlacement}`]: true,
490
482
  'textarea-disabled': disabled,
491
- }) }, h("label", { key: '2649da816216959ebe1f34cafd9dedbac20ec3c2', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: 'dca98593efece1b044dbcda045fa70882d715cb2', class: "textarea-wrapper-inner" }, h("div", { key: '2019daf87fddca5ec0b2e336f0376fd9642bae1b', class: "start-slot-wrapper" }, h("slot", { key: '36c423c394a71d08261705b9d6729e756bf65924', name: "start" })), h("div", { key: '0c3ea34105c7eddfa4094371c5d288c50ed10db3', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: 'ce173b83b16aff43d293fa1edef9b66c6676227b', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes), value)), h("div", { key: '756e343cfd208bb5ad9ecf08d77cbb0a9606dc7b', class: "end-slot-wrapper" }, h("slot", { key: '0eb596814a037fa4634ff8c5bac0045540edfe21', name: "end" }))), shouldRenderHighlight && h("div", { key: 'df62f896eb6e0e2d1217aa487c198eb82a52bcb8', class: "textarea-highlight" })), this.renderBottomContent()));
483
+ }) }, h("label", { key: '8a2dd59a60f7469df84018eb0ede3a9ec3862703', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '1bfc368236e3da7a225a45118c27fbfc1fe5fa46', class: "textarea-wrapper-inner" }, h("div", { key: '215cbb2635ff52e31a8973376989b85e7245d40f', class: "start-slot-wrapper" }, h("slot", { key: '9f6b461cdee9d629deb695d2bea054ece2f32305', name: "start" })), h("div", { key: 'c1af35a2d5bc452bebe0b22a26d15ff52b4e9fc8', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: '69a69b3cf0932baafbe37e6e846f1a571608d3f2', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined }, this.inheritedAttributes), value)), h("div", { key: 'c053ea8b865d0e29763aed2e4939cc9c9e374c15', class: "end-slot-wrapper" }, h("slot", { key: '930aa641833b0df54b9ea10368fc2f46d5f491f6', name: "end" }))), shouldRenderHighlight && h("div", { key: '8d12597d15f5f429d80e8272ea99e64ed924e482', class: "textarea-highlight" })), this.renderBottomContent()));
492
484
  }
493
485
  static get is() { return "ion-textarea"; }
494
486
  static get encapsulation() { return "scoped"; }
@@ -3,3 +3,4 @@
3
3
  */
4
4
  export * from './notch-controller';
5
5
  export * from './compare-with-utils';
6
+ export * from './validity';
@@ -0,0 +1,15 @@
1
+ /*!
2
+ * (C) Ionic http://ionicframework.com - MIT License
3
+ */
4
+ /**
5
+ * Checks if the form element is in an invalid state based on
6
+ * Ionic validation classes.
7
+ *
8
+ * @param el The form element to check.
9
+ * @returns `true` if the element is invalid, `false` otherwise.
10
+ */
11
+ export const checkInvalidState = (el) => {
12
+ const hasIonTouched = el.classList.contains('ion-touched');
13
+ const hasIonInvalid = el.classList.contains('ion-invalid');
14
+ return hasIonTouched && hasIonInvalid;
15
+ };
package/dist/docs.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2025-10-15T06:11:13",
2
+ "timestamp": "2025-10-16T11:57:15",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
5
  "version": "4.38.0",
@@ -37226,7 +37226,7 @@
37226
37226
  ],
37227
37227
  "typeLibrary": {
37228
37228
  "src/components/accordion-group/accordion-group-interface.ts::AccordionGroupChangeEventDetail": {
37229
- "declaration": "export interface AccordionGroupChangeEventDetail<T = any> {\n value: T;\n}",
37229
+ "declaration": "export interface AccordionGroupChangeEventDetail<T = any> {\n value: T;\n initial?: boolean;\n}",
37230
37230
  "docstring": "",
37231
37231
  "path": "src/components/accordion-group/accordion-group-interface.ts"
37232
37232
  },
@@ -13,7 +13,11 @@ const accordionMdCss = ":host{display:block;position:relative;width:100%;backgro
13
13
  const Accordion = class {
14
14
  constructor(hostRef) {
15
15
  registerInstance(this, hostRef);
16
- this.updateListener = () => this.updateState(false);
16
+ this.updateListener = (ev) => {
17
+ var _a, _b;
18
+ const initialUpdate = (_b = (_a = ev.detail) === null || _a === void 0 ? void 0 : _a.initial) !== null && _b !== void 0 ? _b : false;
19
+ this.updateState(initialUpdate);
20
+ };
17
21
  this.state = 1 /* AccordionState.Collapsed */;
18
22
  this.isNext = false;
19
23
  this.isPrevious = false;
@@ -315,7 +319,7 @@ const Accordion = class {
315
319
  const headerPart = expanded ? 'header expanded' : 'header';
316
320
  const contentPart = expanded ? 'content expanded' : 'content';
317
321
  this.setAria(expanded);
318
- return (h(Host, { key: '073e1d02c18dcbc20c68648426e87c14750c031d', class: {
322
+ return (h(Host, { key: '4c8a2978e1c428f1b856d80adcee31d7abb3925d', class: {
319
323
  [mode]: true,
320
324
  'accordion-expanding': this.state === 8 /* AccordionState.Expanding */,
321
325
  'accordion-expanded': this.state === 4 /* AccordionState.Expanded */,
@@ -326,7 +330,7 @@ const Accordion = class {
326
330
  'accordion-disabled': disabled,
327
331
  'accordion-readonly': readonly,
328
332
  'accordion-animated': this.shouldAnimate(),
329
- } }, h("div", { key: '9b4cf326de8bb6b4033992903c0c1bfd7eea9bcc', onClick: () => this.toggleExpanded(), id: "header", part: headerPart, "aria-controls": "content", ref: (headerEl) => (this.headerEl = headerEl) }, h("slot", { key: '464c32a37f64655eacf4218284214f5f30b14a1e', name: "header" })), h("div", { key: '8bb52e6a62d7de0106b253201a89a32e79d9a594', id: "content", part: contentPart, role: "region", "aria-labelledby": "header", ref: (contentEl) => (this.contentEl = contentEl) }, h("div", { key: '1d9dfd952ad493754aaeea7a8f625b33c2dd90a0', id: "content-wrapper", ref: (contentElWrapper) => (this.contentElWrapper = contentElWrapper) }, h("slot", { key: '970dfbc55a612d739d0ca3b7b1a08e5c96d0c479', name: "content" })))));
333
+ } }, h("div", { key: '789c5cec6e54d2aa7528d63e880e1f2cf1924ff2', onClick: () => this.toggleExpanded(), id: "header", part: headerPart, "aria-controls": "content", ref: (headerEl) => (this.headerEl = headerEl) }, h("slot", { key: 'c98907a0e54d2edc0e6e50b8dce1af6e81588eba', name: "header" })), h("div", { key: 'adfe9e7083d5addc1b7cbba0cfc9c65d5809f8f6', id: "content", part: contentPart, role: "region", "aria-labelledby": "header", ref: (contentEl) => (this.contentEl = contentEl) }, h("div", { key: 'c77a044fae8d5173ea180bc57c84d24b39abb494', id: "content-wrapper", ref: (contentElWrapper) => (this.contentElWrapper = contentElWrapper) }, h("slot", { key: '8214cfd99fcb7a77fc08b1145b2c3d58ddf7f185', name: "content" })))));
330
334
  }
331
335
  static get delegatesFocus() { return true; }
332
336
  get el() { return getElement(this); }
@@ -372,26 +376,7 @@ const AccordionGroup = class {
372
376
  this.expand = 'compact';
373
377
  }
374
378
  valueChanged() {
375
- const { value, multiple } = this;
376
- if (!multiple && Array.isArray(value)) {
377
- /**
378
- * We do some processing on the `value` array so
379
- * that it looks more like an array when logged to
380
- * the console.
381
- * Example given ['a', 'b']
382
- * Default toString() behavior: a,b
383
- * Custom behavior: ['a', 'b']
384
- */
385
- printIonWarning(`[ion-accordion-group] - An array of values was passed, but multiple is "false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false".
386
-
387
- Value Passed: [${value.map((v) => `'${v}'`).join(', ')}]
388
- `, this.el);
389
- }
390
- /**
391
- * Do not use `value` here as that will be
392
- * not account for the adjustment we make above.
393
- */
394
- this.ionValueChange.emit({ value: this.value });
379
+ this.emitValueChange(false);
395
380
  }
396
381
  async disabledChanged() {
397
382
  const { disabled } = this;
@@ -465,11 +450,10 @@ const AccordionGroup = class {
465
450
  * it is possible for the value to be set after the Web Component
466
451
  * initializes but before the value watcher is set up in Stencil.
467
452
  * As a result, the watcher callback may not be fired.
468
- * We work around this by manually calling the watcher
469
- * callback when the component has loaded and the watcher
470
- * is configured.
453
+ * We work around this by manually emitting a value change when the component
454
+ * has loaded and the watcher is configured.
471
455
  */
472
- this.valueChanged();
456
+ this.emitValueChange(true);
473
457
  }
474
458
  /**
475
459
  * Sets the value property and emits ionChange.
@@ -549,15 +533,37 @@ const AccordionGroup = class {
549
533
  async getAccordions() {
550
534
  return Array.from(this.el.querySelectorAll(':scope > ion-accordion'));
551
535
  }
536
+ emitValueChange(initial) {
537
+ const { value, multiple } = this;
538
+ if (!multiple && Array.isArray(value)) {
539
+ /**
540
+ * We do some processing on the `value` array so
541
+ * that it looks more like an array when logged to
542
+ * the console.
543
+ * Example given ['a', 'b']
544
+ * Default toString() behavior: a,b
545
+ * Custom behavior: ['a', 'b']
546
+ */
547
+ printIonWarning(`[ion-accordion-group] - An array of values was passed, but multiple is "false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false".
548
+
549
+ Value Passed: [${value.map((v) => `'${v}'`).join(', ')}]
550
+ `, this.el);
551
+ }
552
+ /**
553
+ * Do not use `value` here as that will not account
554
+ * for the adjustment we make above.
555
+ */
556
+ this.ionValueChange.emit({ value: this.value, initial });
557
+ }
552
558
  render() {
553
559
  const { disabled, readonly, expand } = this;
554
560
  const mode = getIonMode(this);
555
- return (h(Host, { key: 'd1a79a93179474fbba66fcf11a92f4871dacc975', class: {
561
+ return (h(Host, { key: 'c69c3fa4c844cb2e88f778af9a8c757d752bb261', class: {
556
562
  [mode]: true,
557
563
  'accordion-group-disabled': disabled,
558
564
  'accordion-group-readonly': readonly,
559
565
  [`accordion-group-expand-${expand}`]: true,
560
- }, role: "presentation" }, h("slot", { key: 'e6b8954b686d1fbb4fc92adb07fddc97a24b0a31' })));
566
+ }, role: "presentation" }, h("slot", { key: 'fe661334ae44c291cc4e61f22f17a7c51f62acb7' })));
561
567
  }
562
568
  get el() { return getElement(this); }
563
569
  static get watchers() { return {
@@ -708,6 +708,8 @@ Footer.style = {
708
708
  };
709
709
 
710
710
  const TRANSITION = 'all 0.2s ease-in-out';
711
+ const ROLE_NONE = 'none';
712
+ const ROLE_BANNER = 'banner';
711
713
  const cloneElement = (tagName) => {
712
714
  const getCachedEl = document.querySelector(`${tagName}.ion-cloned-element`);
713
715
  if (getCachedEl !== null) {
@@ -834,6 +836,7 @@ const setHeaderActive = (headerIndex, active = true) => {
834
836
  const toolbars = headerIndex.toolbars;
835
837
  const ionTitles = toolbars.map((toolbar) => toolbar.ionTitleEl);
836
838
  if (active) {
839
+ headerEl.setAttribute('role', ROLE_BANNER);
837
840
  headerEl.classList.remove('header-collapse-condense-inactive');
838
841
  ionTitles.forEach((ionTitle) => {
839
842
  if (ionTitle) {
@@ -842,6 +845,16 @@ const setHeaderActive = (headerIndex, active = true) => {
842
845
  });
843
846
  }
844
847
  else {
848
+ /**
849
+ * There can only be one banner landmark per page.
850
+ * By default, all ion-headers have the banner role.
851
+ * This causes an accessibility issue when using a
852
+ * condensed header since there are two ion-headers
853
+ * on the page at once (active and inactive).
854
+ * To solve this, the role needs to be toggled
855
+ * based on which header is active.
856
+ */
857
+ headerEl.setAttribute('role', ROLE_NONE);
845
858
  headerEl.classList.add('header-collapse-condense-inactive');
846
859
  /**
847
860
  * The small title should only be accessed by screen readers
@@ -901,6 +914,30 @@ const handleHeaderFade = (scrollEl, baseEl, condenseHeader) => {
901
914
  });
902
915
  });
903
916
  };
917
+ /**
918
+ * Get the role type for the ion-header.
919
+ *
920
+ * @param isInsideMenu If ion-header is inside ion-menu.
921
+ * @param isCondensed If ion-header has collapse="condense".
922
+ * @param mode The current mode.
923
+ * @returns 'none' if inside ion-menu or if condensed in md
924
+ * mode, otherwise 'banner'.
925
+ */
926
+ const getRoleType = (isInsideMenu, isCondensed, mode) => {
927
+ // If the header is inside a menu, it should not have the banner role.
928
+ if (isInsideMenu) {
929
+ return ROLE_NONE;
930
+ }
931
+ /**
932
+ * Only apply role="none" to `md` mode condensed headers
933
+ * since the large header is never shown.
934
+ */
935
+ if (isCondensed && mode === 'md') {
936
+ return ROLE_NONE;
937
+ }
938
+ // Default to banner role.
939
+ return ROLE_BANNER;
940
+ };
904
941
 
905
942
  const headerIosCss = "ion-header{display:block;position:relative;-ms-flex-order:-1;order:-1;width:100%;z-index:10}ion-header ion-toolbar:first-of-type{padding-top:var(--ion-safe-area-top, 0)}.header-ios ion-toolbar:last-of-type{--border-width:0 0 0.55px}@supports ((-webkit-backdrop-filter: blur(0)) or (backdrop-filter: blur(0))){.header-background{left:0;right:0;top:0;bottom:0;position:absolute;-webkit-backdrop-filter:saturate(180%) blur(20px);backdrop-filter:saturate(180%) blur(20px)}.header-translucent-ios ion-toolbar{--opacity:.8}.header-collapse-condense-inactive .header-background{-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px)}}.header-ios.ion-no-border ion-toolbar:last-of-type{--border-width:0}.header-collapse-fade ion-toolbar{--opacity-scale:inherit}.header-collapse-fade.header-transitioning ion-toolbar{--background:transparent;--border-style:none}.header-collapse-condense{z-index:9}.header-collapse-condense ion-toolbar{position:-webkit-sticky;position:sticky;top:0}.header-collapse-condense ion-toolbar:first-of-type{padding-top:0px;z-index:1}.header-collapse-condense ion-toolbar{z-index:0}.header-collapse-condense ion-toolbar:last-of-type{--border-width:0px}.header-collapse-condense ion-toolbar ion-searchbar{padding-top:0px;padding-bottom:13px}.header-collapse-main{--opacity-scale:1}.header-collapse-main ion-toolbar{--opacity-scale:inherit}.header-collapse-main ion-toolbar.in-toolbar ion-title,.header-collapse-main ion-toolbar.in-toolbar ion-buttons{-webkit-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out}.header-collapse-condense ion-toolbar,.header-collapse-condense-inactive.header-transitioning:not(.header-collapse-condense) ion-toolbar{--background:var(--ion-background-color, #fff)}.header-collapse-condense-inactive.header-transitioning:not(.header-collapse-condense) ion-toolbar{--border-style:none;--opacity-scale:1}.header-collapse-condense-inactive:not(.header-collapse-condense) ion-toolbar.in-toolbar ion-title,.header-collapse-condense-inactive:not(.header-collapse-condense) ion-toolbar.in-toolbar ion-buttons.buttons-collapse{opacity:0;pointer-events:none}.header-collapse-condense-inactive.header-collapse-condense ion-toolbar.in-toolbar ion-title,.header-collapse-condense-inactive.header-collapse-condense ion-toolbar.in-toolbar ion-buttons.buttons-collapse{visibility:hidden}ion-header.header-ios:not(.header-collapse-main):has(~ion-content ion-header.header-ios[collapse=condense],~ion-content ion-header.header-ios.header-collapse-condense){opacity:0}";
906
943
 
@@ -1042,16 +1079,17 @@ const Header = class {
1042
1079
  const { translucent, inheritedAttributes } = this;
1043
1080
  const mode = getIonMode(this);
1044
1081
  const collapse = this.collapse || 'none';
1082
+ const isCondensed = collapse === 'condense';
1045
1083
  // banner role must be at top level, so remove role if inside a menu
1046
- const roleType = hostContext('ion-menu', this.el) ? 'none' : 'banner';
1047
- return (h(Host, Object.assign({ key: 'b6cc27f0b08afc9fcc889683525da765d80ba672', role: roleType, class: {
1084
+ const roleType = getRoleType(hostContext('ion-menu', this.el), isCondensed, mode);
1085
+ return (h(Host, Object.assign({ key: '863c4568cd7b8c0ec55109f193bbbaed68a1346e', role: roleType, class: {
1048
1086
  [mode]: true,
1049
1087
  // Used internally for styling
1050
1088
  [`header-${mode}`]: true,
1051
1089
  [`header-translucent`]: this.translucent,
1052
1090
  [`header-collapse-${collapse}`]: true,
1053
1091
  [`header-translucent-${mode}`]: this.translucent,
1054
- } }, inheritedAttributes), mode === 'ios' && translucent && h("div", { key: '395766d4dcee3398bc91960db21f922095292f14', class: "header-background" }), h("slot", { key: '09a67ece27b258ff1248805d43d92a49b2c6859a' })));
1092
+ } }, inheritedAttributes), mode === 'ios' && translucent && h("div", { key: '25c3bdce328b0b35607d154c8b8374679313d881', class: "header-background" }), h("slot", { key: 'b44fab0a9be7920b9650da26117c783e751e1702' })));
1055
1093
  }
1056
1094
  get el() { return getElement(this); }
1057
1095
  };
@@ -213,11 +213,7 @@ const Button = class {
213
213
  target,
214
214
  };
215
215
  let fill = this.fill;
216
- /**
217
- * We check both undefined and null to
218
- * work around https://github.com/ionic-team/stencil/issues/3586.
219
- */
220
- if (fill == null) {
216
+ if (fill === undefined) {
221
217
  fill = this.inToolbar || this.inListHeader ? 'clear' : 'solid';
222
218
  }
223
219
  /**
@@ -230,7 +226,7 @@ const Button = class {
230
226
  {
231
227
  type !== 'button' && this.renderHiddenButton();
232
228
  }
233
- return (h(Host, { key: 'b105ad09215adb3ca2298acdadf0dc9154bbb9b0', onClick: this.handleClick, "aria-disabled": disabled ? 'true' : null, class: createColorClasses$1(color, {
229
+ return (h(Host, { key: 'ed82ea53705523f9afc5f1a9addff44cc6424f27', onClick: this.handleClick, "aria-disabled": disabled ? 'true' : null, class: createColorClasses$1(color, {
234
230
  [mode]: true,
235
231
  [buttonType]: true,
236
232
  [`${buttonType}-${expand}`]: expand !== undefined,
@@ -245,7 +241,7 @@ const Button = class {
245
241
  'button-disabled': disabled,
246
242
  'ion-activatable': true,
247
243
  'ion-focusable': true,
248
- }) }, h(TagType, Object.assign({ key: '66b4e7112bcb9e41d5a723fbbadb0a3104f9ee1d' }, attrs, { class: "button-native", part: "native", disabled: disabled, onFocus: this.onFocus, onBlur: this.onBlur }, inheritedAttributes), h("span", { key: '1439fc3da280221028dcf7ce8ec9dab273c4d4bb', class: "button-inner" }, h("slot", { key: 'd5269ae1afc87ec7b99746032f59cbae93720a9f', name: "icon-only", onSlotchange: this.slotChanged }), h("slot", { key: '461c83e97aa246aa86d83e14f1e15a288d35041e', name: "start" }), h("slot", { key: '807170d47101f9f6a333dd4ff489c89284f306fe' }), h("slot", { key: 'e67f116dd0349a0d27893e4f3ff0ccef1d402f80', name: "end" })), mode === 'md' && h("ion-ripple-effect", { key: '273f0bd9645a36c1bfd18a5c2ab4f81e22b7b989', type: this.rippleType }))));
244
+ }) }, h(TagType, Object.assign({ key: 'fadec13053469dd0405bbbc61b70ced568aa4826' }, attrs, { class: "button-native", part: "native", disabled: disabled, onFocus: this.onFocus, onBlur: this.onBlur }, inheritedAttributes), h("span", { key: '6bf0e5144fb1148002e88038522402b789689d2c', class: "button-inner" }, h("slot", { key: '25da0ca155cfa9e2754842c34f4fd09f576ac2d2', name: "icon-only", onSlotchange: this.slotChanged }), h("slot", { key: '51414065bb11953ec9d818f8d9353589bc9072c5', name: "start" }), h("slot", { key: 'c9b5f8842aeabd20628df2f4600f1257ea913d8d' }), h("slot", { key: '478dd3671c7be1909fc84e672f0fa8dfe6082263', name: "end" })), mode === 'md' && h("ion-ripple-effect", { key: 'e1d55f85a55144d743f58a5914cd116cb065fa8c', type: this.rippleType }))));
249
245
  }
250
246
  get el() { return getElement(this); }
251
247
  static get watchers() { return {