@ionic/core 8.7.7-nightly.20251014 → 8.7.7
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.
- package/components/button.js +3 -7
- package/components/header.js +42 -4
- package/components/index2.js +74 -3
- package/components/ion-input.js +6 -14
- package/components/ion-select.js +58 -10
- package/components/ion-textarea.js +5 -13
- package/components/{notch-controller.js → validity.js} +14 -1
- package/dist/cjs/{index-CD5Rjp23.js → index-094mMFB-.js} +76 -5
- package/dist/cjs/index.cjs.js +3 -3
- package/dist/cjs/ion-app_8.cjs.entry.js +43 -5
- package/dist/cjs/ion-button_2.cjs.entry.js +3 -7
- package/dist/cjs/ion-input.cjs.entry.js +7 -15
- package/dist/cjs/ion-modal.cjs.entry.js +1 -1
- package/dist/cjs/ion-nav_2.cjs.entry.js +1 -1
- package/dist/cjs/ion-popover.cjs.entry.js +1 -1
- package/dist/cjs/ion-select_3.cjs.entry.js +56 -10
- package/dist/cjs/ion-textarea.cjs.entry.js +6 -14
- package/dist/cjs/ionic.cjs.js +1 -1
- package/dist/cjs/{ios.transition-j9CclgEW.js → ios.transition-BOt_uW73.js} +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/{md.transition-CwFyRSfv.js → md.transition-Dt968VXB.js} +1 -1
- package/dist/cjs/{notch-controller-Bzqhjm4f.js → validity-C8QoAYT2.js} +14 -0
- package/dist/collection/components/button/button.js +3 -7
- package/dist/collection/components/header/header.ios.css +27 -1
- package/dist/collection/components/header/header.js +5 -4
- package/dist/collection/components/header/header.utils.js +37 -0
- package/dist/collection/components/input/input.js +6 -14
- package/dist/collection/components/select/select.js +59 -11
- package/dist/collection/components/textarea/textarea.js +5 -13
- package/dist/collection/utils/forms/index.js +1 -0
- package/dist/collection/utils/forms/validity.js +15 -0
- package/dist/collection/utils/transition/index.js +74 -3
- package/dist/docs.json +1 -1
- package/dist/esm/{index-D6G2seR8.js → index-r2D9DEro.js} +76 -5
- package/dist/esm/index.js +3 -3
- package/dist/esm/ion-app_8.entry.js +43 -5
- package/dist/esm/ion-button_2.entry.js +3 -7
- package/dist/esm/ion-input.entry.js +6 -14
- package/dist/esm/ion-modal.entry.js +1 -1
- package/dist/esm/ion-nav_2.entry.js +1 -1
- package/dist/esm/ion-popover.entry.js +1 -1
- package/dist/esm/ion-select_3.entry.js +55 -9
- package/dist/esm/ion-textarea.entry.js +5 -13
- package/dist/esm/ionic.js +1 -1
- package/dist/esm/{ios.transition-Bpq9ixwv.js → ios.transition-BDzw0_Hm.js} +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/{md.transition-zOA0oanq.js → md.transition-BzDYi3qq.js} +1 -1
- package/dist/esm/{notch-controller-BwelN_JM.js → validity-B8oWougr.js} +14 -1
- package/dist/ionic/index.esm.js +1 -1
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-43ed1ef5.entry.js +4 -0
- package/dist/ionic/p-4c85d268.entry.js +4 -0
- package/dist/ionic/p-4cc26913.entry.js +4 -0
- package/dist/ionic/p-8bdfc8f6.entry.js +4 -0
- package/dist/ionic/{p-DPhQmGJN.js → p-C7hRNDhM.js} +1 -1
- package/dist/ionic/p-DUt5fQmA.js +4 -0
- package/dist/ionic/{p-9R1XyICs.js → p-DZRJwG4S.js} +1 -1
- package/dist/ionic/{p-DCv9sLH2.js → p-DieJyvMP.js} +1 -1
- package/dist/ionic/{p-c59314fd.entry.js → p-a80f1b04.entry.js} +1 -1
- package/dist/ionic/{p-c85c40ee.entry.js → p-dbbe606a.entry.js} +1 -1
- package/dist/ionic/{p-de7b5fa3.entry.js → p-e16b69e1.entry.js} +1 -1
- package/dist/ionic/p-f65f9308.entry.js +4 -0
- package/dist/types/components/header/header.utils.d.ts +10 -0
- package/dist/types/components/input/input.d.ts +0 -4
- package/dist/types/components/select/select.d.ts +6 -0
- package/dist/types/components/textarea/textarea.d.ts +0 -4
- package/dist/types/utils/forms/index.d.ts +1 -0
- package/dist/types/utils/forms/validity.d.ts +10 -0
- package/dist/types/utils/transition/index.d.ts +9 -0
- package/hydrate/index.js +161 -45
- package/hydrate/index.mjs +161 -45
- package/package.json +2 -2
- package/dist/ionic/p-1c8a476d.entry.js +0 -4
- package/dist/ionic/p-49f0149c.entry.js +0 -4
- package/dist/ionic/p-785026d7.entry.js +0 -4
- package/dist/ionic/p-78c74a3e.entry.js +0 -4
- package/dist/ionic/p-913a7c1e.entry.js +0 -4
- package/dist/ionic/p-CMhMiYSX.js +0 -4
package/components/button.js
CHANGED
|
@@ -219,11 +219,7 @@ const Button = /*@__PURE__*/ proxyCustomElement(class Button extends HTMLElement
|
|
|
219
219
|
target,
|
|
220
220
|
};
|
|
221
221
|
let fill = this.fill;
|
|
222
|
-
|
|
223
|
-
* We check both undefined and null to
|
|
224
|
-
* work around https://github.com/ionic-team/stencil/issues/3586.
|
|
225
|
-
*/
|
|
226
|
-
if (fill == null) {
|
|
222
|
+
if (fill === undefined) {
|
|
227
223
|
fill = this.inToolbar || this.inListHeader ? 'clear' : 'solid';
|
|
228
224
|
}
|
|
229
225
|
/**
|
|
@@ -236,7 +232,7 @@ const Button = /*@__PURE__*/ proxyCustomElement(class Button extends HTMLElement
|
|
|
236
232
|
{
|
|
237
233
|
type !== 'button' && this.renderHiddenButton();
|
|
238
234
|
}
|
|
239
|
-
return (h(Host, { key: '
|
|
235
|
+
return (h(Host, { key: 'ed82ea53705523f9afc5f1a9addff44cc6424f27', onClick: this.handleClick, "aria-disabled": disabled ? 'true' : null, class: createColorClasses(color, {
|
|
240
236
|
[mode]: true,
|
|
241
237
|
[buttonType]: true,
|
|
242
238
|
[`${buttonType}-${expand}`]: expand !== undefined,
|
|
@@ -251,7 +247,7 @@ const Button = /*@__PURE__*/ proxyCustomElement(class Button extends HTMLElement
|
|
|
251
247
|
'button-disabled': disabled,
|
|
252
248
|
'ion-activatable': true,
|
|
253
249
|
'ion-focusable': true,
|
|
254
|
-
}) }, h(TagType, Object.assign({ key: '
|
|
250
|
+
}) }, 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 }))));
|
|
255
251
|
}
|
|
256
252
|
get el() { return this; }
|
|
257
253
|
static get watchers() { return {
|
package/components/header.js
CHANGED
|
@@ -8,6 +8,8 @@ import { h as hostContext } from './theme.js';
|
|
|
8
8
|
import { b as getIonMode } from './ionic-global.js';
|
|
9
9
|
|
|
10
10
|
const TRANSITION = 'all 0.2s ease-in-out';
|
|
11
|
+
const ROLE_NONE = 'none';
|
|
12
|
+
const ROLE_BANNER = 'banner';
|
|
11
13
|
const cloneElement = (tagName) => {
|
|
12
14
|
const getCachedEl = document.querySelector(`${tagName}.ion-cloned-element`);
|
|
13
15
|
if (getCachedEl !== null) {
|
|
@@ -134,6 +136,7 @@ const setHeaderActive = (headerIndex, active = true) => {
|
|
|
134
136
|
const toolbars = headerIndex.toolbars;
|
|
135
137
|
const ionTitles = toolbars.map((toolbar) => toolbar.ionTitleEl);
|
|
136
138
|
if (active) {
|
|
139
|
+
headerEl.setAttribute('role', ROLE_BANNER);
|
|
137
140
|
headerEl.classList.remove('header-collapse-condense-inactive');
|
|
138
141
|
ionTitles.forEach((ionTitle) => {
|
|
139
142
|
if (ionTitle) {
|
|
@@ -142,6 +145,16 @@ const setHeaderActive = (headerIndex, active = true) => {
|
|
|
142
145
|
});
|
|
143
146
|
}
|
|
144
147
|
else {
|
|
148
|
+
/**
|
|
149
|
+
* There can only be one banner landmark per page.
|
|
150
|
+
* By default, all ion-headers have the banner role.
|
|
151
|
+
* This causes an accessibility issue when using a
|
|
152
|
+
* condensed header since there are two ion-headers
|
|
153
|
+
* on the page at once (active and inactive).
|
|
154
|
+
* To solve this, the role needs to be toggled
|
|
155
|
+
* based on which header is active.
|
|
156
|
+
*/
|
|
157
|
+
headerEl.setAttribute('role', ROLE_NONE);
|
|
145
158
|
headerEl.classList.add('header-collapse-condense-inactive');
|
|
146
159
|
/**
|
|
147
160
|
* The small title should only be accessed by screen readers
|
|
@@ -201,8 +214,32 @@ const handleHeaderFade = (scrollEl, baseEl, condenseHeader) => {
|
|
|
201
214
|
});
|
|
202
215
|
});
|
|
203
216
|
};
|
|
217
|
+
/**
|
|
218
|
+
* Get the role type for the ion-header.
|
|
219
|
+
*
|
|
220
|
+
* @param isInsideMenu If ion-header is inside ion-menu.
|
|
221
|
+
* @param isCondensed If ion-header has collapse="condense".
|
|
222
|
+
* @param mode The current mode.
|
|
223
|
+
* @returns 'none' if inside ion-menu or if condensed in md
|
|
224
|
+
* mode, otherwise 'banner'.
|
|
225
|
+
*/
|
|
226
|
+
const getRoleType = (isInsideMenu, isCondensed, mode) => {
|
|
227
|
+
// If the header is inside a menu, it should not have the banner role.
|
|
228
|
+
if (isInsideMenu) {
|
|
229
|
+
return ROLE_NONE;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Only apply role="none" to `md` mode condensed headers
|
|
233
|
+
* since the large header is never shown.
|
|
234
|
+
*/
|
|
235
|
+
if (isCondensed && mode === 'md') {
|
|
236
|
+
return ROLE_NONE;
|
|
237
|
+
}
|
|
238
|
+
// Default to banner role.
|
|
239
|
+
return ROLE_BANNER;
|
|
240
|
+
};
|
|
204
241
|
|
|
205
|
-
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-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{
|
|
242
|
+
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}";
|
|
206
243
|
|
|
207
244
|
const headerMdCss = "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-md{-webkit-box-shadow:0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12);box-shadow:0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12)}.header-collapse-condense{display:none}.header-md.ion-no-border{-webkit-box-shadow:none;box-shadow:none}";
|
|
208
245
|
|
|
@@ -345,16 +382,17 @@ const Header = /*@__PURE__*/ proxyCustomElement(class Header extends HTMLElement
|
|
|
345
382
|
const { translucent, inheritedAttributes } = this;
|
|
346
383
|
const mode = getIonMode(this);
|
|
347
384
|
const collapse = this.collapse || 'none';
|
|
385
|
+
const isCondensed = collapse === 'condense';
|
|
348
386
|
// banner role must be at top level, so remove role if inside a menu
|
|
349
|
-
const roleType = hostContext('ion-menu', this.el)
|
|
350
|
-
return (h(Host, Object.assign({ key: '
|
|
387
|
+
const roleType = getRoleType(hostContext('ion-menu', this.el), isCondensed, mode);
|
|
388
|
+
return (h(Host, Object.assign({ key: '863c4568cd7b8c0ec55109f193bbbaed68a1346e', role: roleType, class: {
|
|
351
389
|
[mode]: true,
|
|
352
390
|
// Used internally for styling
|
|
353
391
|
[`header-${mode}`]: true,
|
|
354
392
|
[`header-translucent`]: this.translucent,
|
|
355
393
|
[`header-collapse-${collapse}`]: true,
|
|
356
394
|
[`header-translucent-${mode}`]: this.translucent,
|
|
357
|
-
} }, inheritedAttributes), mode === 'ios' && translucent && h("div", { key: '
|
|
395
|
+
} }, inheritedAttributes), mode === 'ios' && translucent && h("div", { key: '25c3bdce328b0b35607d154c8b8374679313d881', class: "header-background" }), h("slot", { key: 'b44fab0a9be7920b9650da26117c783e751e1702' })));
|
|
358
396
|
}
|
|
359
397
|
get el() { return this; }
|
|
360
398
|
static get style() { return {
|
package/components/index2.js
CHANGED
|
@@ -123,11 +123,22 @@ const iosTransitionAnimation = () => import('./ios.transition.js');
|
|
|
123
123
|
const mdTransitionAnimation = () => import('./md.transition.js');
|
|
124
124
|
const focusController = createFocusController();
|
|
125
125
|
// TODO(FW-2832): types
|
|
126
|
+
/**
|
|
127
|
+
* Executes the main page transition.
|
|
128
|
+
* It also manages the lifecycle of header visibility (if any)
|
|
129
|
+
* to prevent visual flickering in iOS. The flickering only
|
|
130
|
+
* occurs for a condensed header that is placed above the content.
|
|
131
|
+
*
|
|
132
|
+
* @param opts Options for the transition.
|
|
133
|
+
* @returns A promise that resolves when the transition is complete.
|
|
134
|
+
*/
|
|
126
135
|
const transition = (opts) => {
|
|
127
136
|
return new Promise((resolve, reject) => {
|
|
128
137
|
writeTask(() => {
|
|
129
|
-
|
|
130
|
-
|
|
138
|
+
const transitioningInactiveHeader = getIosIonHeader(opts);
|
|
139
|
+
beforeTransition(opts, transitioningInactiveHeader);
|
|
140
|
+
runTransition(opts)
|
|
141
|
+
.then((result) => {
|
|
131
142
|
if (result.animation) {
|
|
132
143
|
result.animation.destroy();
|
|
133
144
|
}
|
|
@@ -136,15 +147,21 @@ const transition = (opts) => {
|
|
|
136
147
|
}, (error) => {
|
|
137
148
|
afterTransition(opts);
|
|
138
149
|
reject(error);
|
|
150
|
+
})
|
|
151
|
+
.finally(() => {
|
|
152
|
+
// Ensure that the header is restored to its original state.
|
|
153
|
+
setHeaderTransitionClass(transitioningInactiveHeader, false);
|
|
139
154
|
});
|
|
140
155
|
});
|
|
141
156
|
});
|
|
142
157
|
};
|
|
143
|
-
const beforeTransition = (opts) => {
|
|
158
|
+
const beforeTransition = (opts, transitioningInactiveHeader) => {
|
|
144
159
|
const enteringEl = opts.enteringEl;
|
|
145
160
|
const leavingEl = opts.leavingEl;
|
|
146
161
|
focusController.saveViewFocus(leavingEl);
|
|
147
162
|
setZIndex(enteringEl, leavingEl, opts.direction);
|
|
163
|
+
// Prevent flickering of the header by adding a class.
|
|
164
|
+
setHeaderTransitionClass(transitioningInactiveHeader, true);
|
|
148
165
|
if (opts.showGoBack) {
|
|
149
166
|
enteringEl.classList.add('can-go-back');
|
|
150
167
|
}
|
|
@@ -333,6 +350,39 @@ const setZIndex = (enteringEl, leavingEl, direction) => {
|
|
|
333
350
|
leavingEl.style.zIndex = '100';
|
|
334
351
|
}
|
|
335
352
|
};
|
|
353
|
+
/**
|
|
354
|
+
* Add a class to ensure that the header (if any)
|
|
355
|
+
* does not flicker during the transition. By adding the
|
|
356
|
+
* transitioning class, we ensure that the header has
|
|
357
|
+
* the necessary styles to prevent the following flickers:
|
|
358
|
+
* 1. When entering a page with a condensed header, the
|
|
359
|
+
* header should never be visible. However,
|
|
360
|
+
* it briefly renders the background color while
|
|
361
|
+
* the transition is occurring.
|
|
362
|
+
* 2. When leaving a page with a condensed header, the
|
|
363
|
+
* header has an opacity of 0 and the pages
|
|
364
|
+
* have a z-index which causes the entering page to
|
|
365
|
+
* briefly show it's content underneath the leaving page.
|
|
366
|
+
* 3. When entering a page or leaving a page with a fade
|
|
367
|
+
* header, the header should not have a background color.
|
|
368
|
+
* However, it briefly shows the background color while
|
|
369
|
+
* the transition is occurring.
|
|
370
|
+
*
|
|
371
|
+
* @param header The header element to modify.
|
|
372
|
+
* @param isTransitioning Whether the transition is occurring.
|
|
373
|
+
*/
|
|
374
|
+
const setHeaderTransitionClass = (header, isTransitioning) => {
|
|
375
|
+
if (!header) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const transitionClass = 'header-transitioning';
|
|
379
|
+
if (isTransitioning) {
|
|
380
|
+
header.classList.add(transitionClass);
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
header.classList.remove(transitionClass);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
336
386
|
const getIonPageElement = (element) => {
|
|
337
387
|
if (element.classList.contains('ion-page')) {
|
|
338
388
|
return element;
|
|
@@ -344,5 +394,26 @@ const getIonPageElement = (element) => {
|
|
|
344
394
|
// idk, return the original element so at least something animates and we don't have a null pointer
|
|
345
395
|
return element;
|
|
346
396
|
};
|
|
397
|
+
/**
|
|
398
|
+
* Retrieves the ion-header element from a page based on the
|
|
399
|
+
* direction of the transition.
|
|
400
|
+
*
|
|
401
|
+
* @param opts Options for the transition.
|
|
402
|
+
* @returns The ion-header element or null if not found or not in 'ios' mode.
|
|
403
|
+
*/
|
|
404
|
+
const getIosIonHeader = (opts) => {
|
|
405
|
+
const enteringEl = opts.enteringEl;
|
|
406
|
+
const leavingEl = opts.leavingEl;
|
|
407
|
+
const direction = opts.direction;
|
|
408
|
+
const mode = opts.mode;
|
|
409
|
+
if (mode !== 'ios') {
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
const element = direction === 'back' ? leavingEl : enteringEl;
|
|
413
|
+
if (!element) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
return element.querySelector('ion-header');
|
|
417
|
+
};
|
|
347
418
|
|
|
348
419
|
export { LIFECYCLE_WILL_ENTER as L, LIFECYCLE_DID_ENTER as a, LIFECYCLE_WILL_LEAVE as b, LIFECYCLE_DID_LEAVE as c, LIFECYCLE_WILL_UNLOAD as d, deepReady as e, getIonPageElement as g, lifecycle as l, setPageHidden as s, transition as t, waitForMount as w };
|
package/components/ion-input.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
4
|
import { proxyCustomElement, HTMLElement, createEvent, forceUpdate, Build, h, Host } from '@stencil/core/internal/client';
|
|
5
|
-
import { c as createNotchController } from './
|
|
5
|
+
import { c as createNotchController, a as checkInvalidState } from './validity.js';
|
|
6
6
|
import { l as debounceEvent, i as inheritAriaAttributes, d as inheritAttributes, c as componentOnReady } from './helpers.js';
|
|
7
7
|
import { c as createSlotMutationController, g as getCounterText } from './input.utils.js';
|
|
8
8
|
import { h as hostContext, c as createColorClasses } from './theme.js';
|
|
@@ -233,14 +233,6 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
233
233
|
componentWillLoad() {
|
|
234
234
|
this.inheritedAttributes = Object.assign(Object.assign({}, inheritAriaAttributes(this.el)), inheritAttributes(this.el, ['tabindex', 'title', 'data-form-type', 'dir']));
|
|
235
235
|
}
|
|
236
|
-
/**
|
|
237
|
-
* Checks if the input is in an invalid state based on Ionic validation classes
|
|
238
|
-
*/
|
|
239
|
-
checkInvalidState() {
|
|
240
|
-
const hasIonTouched = this.el.classList.contains('ion-touched');
|
|
241
|
-
const hasIonInvalid = this.el.classList.contains('ion-invalid');
|
|
242
|
-
return hasIonTouched && hasIonInvalid;
|
|
243
|
-
}
|
|
244
236
|
connectedCallback() {
|
|
245
237
|
const { el } = this;
|
|
246
238
|
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
|
|
@@ -248,7 +240,7 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
248
240
|
// Watch for class changes to update validation state
|
|
249
241
|
if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
|
|
250
242
|
this.validationObserver = new MutationObserver(() => {
|
|
251
|
-
const newIsInvalid =
|
|
243
|
+
const newIsInvalid = checkInvalidState(el);
|
|
252
244
|
if (this.isInvalid !== newIsInvalid) {
|
|
253
245
|
this.isInvalid = newIsInvalid;
|
|
254
246
|
// Force a re-render to update aria-describedby immediately
|
|
@@ -261,7 +253,7 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
261
253
|
});
|
|
262
254
|
}
|
|
263
255
|
// Always set initial state
|
|
264
|
-
this.isInvalid =
|
|
256
|
+
this.isInvalid = checkInvalidState(el);
|
|
265
257
|
this.debounceChanged();
|
|
266
258
|
if (Build.isBrowser) {
|
|
267
259
|
document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
|
|
@@ -525,7 +517,7 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
525
517
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
526
518
|
*/
|
|
527
519
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
|
528
|
-
return (h(Host, { key: '
|
|
520
|
+
return (h(Host, { key: '97b5308021064d9e7434ef2d3d96f27045c1b0c4', class: createColorClasses(this.color, {
|
|
529
521
|
[mode]: true,
|
|
530
522
|
'has-value': hasValue,
|
|
531
523
|
'has-focus': hasFocus,
|
|
@@ -536,14 +528,14 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
536
528
|
'in-item': inItem,
|
|
537
529
|
'in-item-color': hostContext('ion-item.ion-color', this.el),
|
|
538
530
|
'input-disabled': disabled,
|
|
539
|
-
}) }, h("label", { key: '
|
|
531
|
+
}) }, 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) => {
|
|
540
532
|
/**
|
|
541
533
|
* This prevents mobile browsers from
|
|
542
534
|
* blurring the input when the clear
|
|
543
535
|
* button is activated.
|
|
544
536
|
*/
|
|
545
537
|
ev.preventDefault();
|
|
546
|
-
}, onClick: this.clearTextInput }, h("ion-icon", { key: '
|
|
538
|
+
}, 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()));
|
|
547
539
|
}
|
|
548
540
|
get el() { return this; }
|
|
549
541
|
static get watchers() { return {
|
package/components/ion-select.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
|
-
import { proxyCustomElement, HTMLElement, createEvent, h, Host, forceUpdate } from '@stencil/core/internal/client';
|
|
5
|
-
import { c as createNotchController } from './
|
|
4
|
+
import { proxyCustomElement, HTMLElement, createEvent, Build, h, Host, forceUpdate } from '@stencil/core/internal/client';
|
|
5
|
+
import { c as createNotchController, a as checkInvalidState } from './validity.js';
|
|
6
6
|
import { i as isOptionSelected, d as defineCustomElement$8, c as compareOptions } from './radio.js';
|
|
7
7
|
import { d as inheritAttributes, e as renderHiddenInput, h as focusVisibleElement } from './helpers.js';
|
|
8
8
|
import { p as printIonWarning } from './index4.js';
|
|
@@ -65,6 +65,10 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
|
|
|
65
65
|
* is applied in both cases.
|
|
66
66
|
*/
|
|
67
67
|
this.hasFocus = false;
|
|
68
|
+
/**
|
|
69
|
+
* Track validation state for proper aria-live announcements.
|
|
70
|
+
*/
|
|
71
|
+
this.isInvalid = false;
|
|
68
72
|
/**
|
|
69
73
|
* The text to display on the cancel button.
|
|
70
74
|
*/
|
|
@@ -194,9 +198,46 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
|
|
|
194
198
|
*/
|
|
195
199
|
forceUpdate(this);
|
|
196
200
|
});
|
|
201
|
+
// Watch for class changes to update validation state.
|
|
202
|
+
if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
|
|
203
|
+
this.validationObserver = new MutationObserver(() => {
|
|
204
|
+
const newIsInvalid = checkInvalidState(this.el);
|
|
205
|
+
if (this.isInvalid !== newIsInvalid) {
|
|
206
|
+
this.isInvalid = newIsInvalid;
|
|
207
|
+
/**
|
|
208
|
+
* Screen readers tend to announce changes
|
|
209
|
+
* to `aria-describedby` when the attribute
|
|
210
|
+
* is changed during a blur event for a
|
|
211
|
+
* native form control.
|
|
212
|
+
* However, the announcement can be spotty
|
|
213
|
+
* when using a non-native form control
|
|
214
|
+
* and `forceUpdate()`.
|
|
215
|
+
* This is due to `forceUpdate()` internally
|
|
216
|
+
* rescheduling the DOM update to a lower
|
|
217
|
+
* priority queue regardless if it's called
|
|
218
|
+
* inside a Promise or not, thus causing
|
|
219
|
+
* the screen reader to potentially miss the
|
|
220
|
+
* change.
|
|
221
|
+
* By using a State variable inside a Promise,
|
|
222
|
+
* it guarantees a re-render immediately at
|
|
223
|
+
* a higher priority.
|
|
224
|
+
*/
|
|
225
|
+
Promise.resolve().then(() => {
|
|
226
|
+
this.hintTextID = this.getHintTextID();
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
this.validationObserver.observe(el, {
|
|
231
|
+
attributes: true,
|
|
232
|
+
attributeFilter: ['class'],
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
// Always set initial state
|
|
236
|
+
this.isInvalid = checkInvalidState(this.el);
|
|
197
237
|
}
|
|
198
238
|
componentWillLoad() {
|
|
199
239
|
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
|
|
240
|
+
this.hintTextID = this.getHintTextID();
|
|
200
241
|
}
|
|
201
242
|
componentDidLoad() {
|
|
202
243
|
/**
|
|
@@ -220,6 +261,11 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
|
|
|
220
261
|
this.notchController.destroy();
|
|
221
262
|
this.notchController = undefined;
|
|
222
263
|
}
|
|
264
|
+
// Clean up validation observer to prevent memory leaks.
|
|
265
|
+
if (this.validationObserver) {
|
|
266
|
+
this.validationObserver.disconnect();
|
|
267
|
+
this.validationObserver = undefined;
|
|
268
|
+
}
|
|
223
269
|
}
|
|
224
270
|
/**
|
|
225
271
|
* Open the select overlay. The overlay is either an alert, action sheet, or popover,
|
|
@@ -690,11 +736,11 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
|
|
|
690
736
|
}
|
|
691
737
|
renderListbox() {
|
|
692
738
|
const { disabled, inputId, isExpanded, required } = this;
|
|
693
|
-
return (h("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "dialog", "aria-expanded": `${isExpanded}`, "aria-describedby": this.
|
|
739
|
+
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) }));
|
|
694
740
|
}
|
|
695
741
|
getHintTextID() {
|
|
696
|
-
const {
|
|
697
|
-
if (
|
|
742
|
+
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
743
|
+
if (isInvalid && errorText) {
|
|
698
744
|
return errorTextId;
|
|
699
745
|
}
|
|
700
746
|
if (helperText) {
|
|
@@ -706,10 +752,10 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
|
|
|
706
752
|
* Renders the helper text or error text values
|
|
707
753
|
*/
|
|
708
754
|
renderHintText() {
|
|
709
|
-
const { helperText, errorText, helperTextId, errorTextId } = this;
|
|
755
|
+
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
710
756
|
return [
|
|
711
|
-
h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText),
|
|
712
|
-
h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText),
|
|
757
|
+
h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null),
|
|
758
|
+
h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null),
|
|
713
759
|
];
|
|
714
760
|
}
|
|
715
761
|
/**
|
|
@@ -757,7 +803,7 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
|
|
|
757
803
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
758
804
|
*/
|
|
759
805
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
|
|
760
|
-
return (h(Host, { key: '
|
|
806
|
+
return (h(Host, { key: '35b5e18e6f79a802ff2d46d1242e80ff755cc0b9', onClick: this.onClick, class: createColorClasses(this.color, {
|
|
761
807
|
[mode]: true,
|
|
762
808
|
'in-item': inItem,
|
|
763
809
|
'in-item-color': hostContext('ion-item.ion-color', el),
|
|
@@ -775,7 +821,7 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
|
|
|
775
821
|
[`select-justify-${justify}`]: justifyEnabled,
|
|
776
822
|
[`select-shape-${shape}`]: shape !== undefined,
|
|
777
823
|
[`select-label-placement-${labelPlacement}`]: true,
|
|
778
|
-
}) }, h("label", { key: '
|
|
824
|
+
}) }, 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()));
|
|
779
825
|
}
|
|
780
826
|
get el() { return this; }
|
|
781
827
|
static get watchers() { return {
|
|
@@ -813,6 +859,8 @@ const Select = /*@__PURE__*/ proxyCustomElement(class Select extends HTMLElement
|
|
|
813
859
|
"required": [4],
|
|
814
860
|
"isExpanded": [32],
|
|
815
861
|
"hasFocus": [32],
|
|
862
|
+
"isInvalid": [32],
|
|
863
|
+
"hintTextID": [32],
|
|
816
864
|
"open": [64]
|
|
817
865
|
}, undefined, {
|
|
818
866
|
"disabled": ["styleChanged"],
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
4
|
import { proxyCustomElement, HTMLElement, createEvent, forceUpdate, Build, writeTask, h, Host } from '@stencil/core/internal/client';
|
|
5
|
-
import { c as createNotchController } from './
|
|
5
|
+
import { c as createNotchController, a as checkInvalidState } from './validity.js';
|
|
6
6
|
import { l as debounceEvent, i as inheritAriaAttributes, d as inheritAttributes, c as componentOnReady } from './helpers.js';
|
|
7
7
|
import { c as createSlotMutationController, g as getCounterText } from './input.utils.js';
|
|
8
8
|
import { h as hostContext, c as createColorClasses } from './theme.js';
|
|
@@ -192,14 +192,6 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
192
192
|
this.el.click();
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
|
-
/**
|
|
196
|
-
* Checks if the textarea is in an invalid state based on Ionic validation classes
|
|
197
|
-
*/
|
|
198
|
-
checkValidationState() {
|
|
199
|
-
const hasIonTouched = this.el.classList.contains('ion-touched');
|
|
200
|
-
const hasIonInvalid = this.el.classList.contains('ion-invalid');
|
|
201
|
-
return hasIonTouched && hasIonInvalid;
|
|
202
|
-
}
|
|
203
195
|
connectedCallback() {
|
|
204
196
|
const { el } = this;
|
|
205
197
|
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
|
|
@@ -207,7 +199,7 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
207
199
|
// Watch for class changes to update validation state
|
|
208
200
|
if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
|
|
209
201
|
this.validationObserver = new MutationObserver(() => {
|
|
210
|
-
const newIsInvalid = this.
|
|
202
|
+
const newIsInvalid = checkInvalidState(this.el);
|
|
211
203
|
if (this.isInvalid !== newIsInvalid) {
|
|
212
204
|
this.isInvalid = newIsInvalid;
|
|
213
205
|
// Force a re-render to update aria-describedby immediately
|
|
@@ -220,7 +212,7 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
220
212
|
});
|
|
221
213
|
}
|
|
222
214
|
// Always set initial state
|
|
223
|
-
this.isInvalid = this.
|
|
215
|
+
this.isInvalid = checkInvalidState(this.el);
|
|
224
216
|
this.debounceChanged();
|
|
225
217
|
if (Build.isBrowser) {
|
|
226
218
|
document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
|
|
@@ -484,7 +476,7 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
484
476
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
485
477
|
*/
|
|
486
478
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
|
487
|
-
return (h(Host, { key: '
|
|
479
|
+
return (h(Host, { key: 'a70a62d7aae3831a50acd74f60b930925ada1326', class: createColorClasses(this.color, {
|
|
488
480
|
[mode]: true,
|
|
489
481
|
'has-value': hasValue,
|
|
490
482
|
'has-focus': hasFocus,
|
|
@@ -493,7 +485,7 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
|
|
|
493
485
|
[`textarea-shape-${shape}`]: shape !== undefined,
|
|
494
486
|
[`textarea-label-placement-${labelPlacement}`]: true,
|
|
495
487
|
'textarea-disabled': disabled,
|
|
496
|
-
}) }, h("label", { key: '
|
|
488
|
+
}) }, 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()));
|
|
497
489
|
}
|
|
498
490
|
get el() { return this; }
|
|
499
491
|
static get watchers() { return {
|
|
@@ -150,4 +150,17 @@ const createNotchController = (el, getNotchSpacerEl, getLabelSlot) => {
|
|
|
150
150
|
};
|
|
151
151
|
};
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Checks if the form element is in an invalid state based on
|
|
155
|
+
* Ionic validation classes.
|
|
156
|
+
*
|
|
157
|
+
* @param el The form element to check.
|
|
158
|
+
* @returns `true` if the element is invalid, `false` otherwise.
|
|
159
|
+
*/
|
|
160
|
+
const checkInvalidState = (el) => {
|
|
161
|
+
const hasIonTouched = el.classList.contains('ion-touched');
|
|
162
|
+
const hasIonInvalid = el.classList.contains('ion-invalid');
|
|
163
|
+
return hasIonTouched && hasIonInvalid;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export { checkInvalidState as a, createNotchController as c };
|