@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.
- package/components/button.js +3 -7
- package/components/header.js +41 -3
- package/components/ion-accordion-group.js +28 -26
- package/components/ion-accordion.js +7 -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/ion-accordion_2.cjs.entry.js +35 -29
- package/dist/cjs/ion-app_8.cjs.entry.js +41 -3
- 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-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/loader.cjs.js +1 -1
- package/dist/cjs/{notch-controller-Bzqhjm4f.js → validity-C8QoAYT2.js} +14 -0
- package/dist/collection/components/accordion/accordion.js +7 -3
- package/dist/collection/components/accordion-group/accordion-group.js +28 -26
- package/dist/collection/components/button/button.js +3 -7
- 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/docs.json +2 -2
- package/dist/esm/ion-accordion_2.entry.js +35 -29
- package/dist/esm/ion-app_8.entry.js +41 -3
- package/dist/esm/ion-button_2.entry.js +3 -7
- package/dist/esm/ion-input.entry.js +6 -14
- 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/loader.js +1 -1
- package/dist/esm/{notch-controller-BwelN_JM.js → validity-B8oWougr.js} +14 -1
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-16280296.entry.js +4 -0
- 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-DCv9sLH2.js → p-DieJyvMP.js} +1 -1
- package/dist/ionic/p-f65f9308.entry.js +4 -0
- package/dist/types/components/accordion-group/accordion-group-interface.d.ts +1 -0
- package/dist/types/components/accordion-group/accordion-group.d.ts +1 -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/hydrate/index.js +121 -70
- package/hydrate/index.mjs +121 -70
- package/package.json +2 -2
- package/dist/ionic/p-1c8a476d.entry.js +0 -4
- package/dist/ionic/p-62e50f80.entry.js +0 -4
- package/dist/ionic/p-7647da93.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
|
@@ -15,7 +15,11 @@ const accordionMdCss = ":host{display:block;position:relative;width:100%;backgro
|
|
|
15
15
|
const Accordion = class {
|
|
16
16
|
constructor(hostRef) {
|
|
17
17
|
index.registerInstance(this, hostRef);
|
|
18
|
-
this.updateListener = () =>
|
|
18
|
+
this.updateListener = (ev) => {
|
|
19
|
+
var _a, _b;
|
|
20
|
+
const initialUpdate = (_b = (_a = ev.detail) === null || _a === void 0 ? void 0 : _a.initial) !== null && _b !== void 0 ? _b : false;
|
|
21
|
+
this.updateState(initialUpdate);
|
|
22
|
+
};
|
|
19
23
|
this.state = 1 /* AccordionState.Collapsed */;
|
|
20
24
|
this.isNext = false;
|
|
21
25
|
this.isPrevious = false;
|
|
@@ -317,7 +321,7 @@ const Accordion = class {
|
|
|
317
321
|
const headerPart = expanded ? 'header expanded' : 'header';
|
|
318
322
|
const contentPart = expanded ? 'content expanded' : 'content';
|
|
319
323
|
this.setAria(expanded);
|
|
320
|
-
return (index.h(index.Host, { key: '
|
|
324
|
+
return (index.h(index.Host, { key: '4c8a2978e1c428f1b856d80adcee31d7abb3925d', class: {
|
|
321
325
|
[mode]: true,
|
|
322
326
|
'accordion-expanding': this.state === 8 /* AccordionState.Expanding */,
|
|
323
327
|
'accordion-expanded': this.state === 4 /* AccordionState.Expanded */,
|
|
@@ -328,7 +332,7 @@ const Accordion = class {
|
|
|
328
332
|
'accordion-disabled': disabled,
|
|
329
333
|
'accordion-readonly': readonly,
|
|
330
334
|
'accordion-animated': this.shouldAnimate(),
|
|
331
|
-
} }, index.h("div", { key: '
|
|
335
|
+
} }, index.h("div", { key: '789c5cec6e54d2aa7528d63e880e1f2cf1924ff2', onClick: () => this.toggleExpanded(), id: "header", part: headerPart, "aria-controls": "content", ref: (headerEl) => (this.headerEl = headerEl) }, index.h("slot", { key: 'c98907a0e54d2edc0e6e50b8dce1af6e81588eba', name: "header" })), index.h("div", { key: 'adfe9e7083d5addc1b7cbba0cfc9c65d5809f8f6', id: "content", part: contentPart, role: "region", "aria-labelledby": "header", ref: (contentEl) => (this.contentEl = contentEl) }, index.h("div", { key: 'c77a044fae8d5173ea180bc57c84d24b39abb494', id: "content-wrapper", ref: (contentElWrapper) => (this.contentElWrapper = contentElWrapper) }, index.h("slot", { key: '8214cfd99fcb7a77fc08b1145b2c3d58ddf7f185', name: "content" })))));
|
|
332
336
|
}
|
|
333
337
|
static get delegatesFocus() { return true; }
|
|
334
338
|
get el() { return index.getElement(this); }
|
|
@@ -374,26 +378,7 @@ const AccordionGroup = class {
|
|
|
374
378
|
this.expand = 'compact';
|
|
375
379
|
}
|
|
376
380
|
valueChanged() {
|
|
377
|
-
|
|
378
|
-
if (!multiple && Array.isArray(value)) {
|
|
379
|
-
/**
|
|
380
|
-
* We do some processing on the `value` array so
|
|
381
|
-
* that it looks more like an array when logged to
|
|
382
|
-
* the console.
|
|
383
|
-
* Example given ['a', 'b']
|
|
384
|
-
* Default toString() behavior: a,b
|
|
385
|
-
* Custom behavior: ['a', 'b']
|
|
386
|
-
*/
|
|
387
|
-
index.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".
|
|
388
|
-
|
|
389
|
-
Value Passed: [${value.map((v) => `'${v}'`).join(', ')}]
|
|
390
|
-
`, this.el);
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Do not use `value` here as that will be
|
|
394
|
-
* not account for the adjustment we make above.
|
|
395
|
-
*/
|
|
396
|
-
this.ionValueChange.emit({ value: this.value });
|
|
381
|
+
this.emitValueChange(false);
|
|
397
382
|
}
|
|
398
383
|
async disabledChanged() {
|
|
399
384
|
const { disabled } = this;
|
|
@@ -467,11 +452,10 @@ const AccordionGroup = class {
|
|
|
467
452
|
* it is possible for the value to be set after the Web Component
|
|
468
453
|
* initializes but before the value watcher is set up in Stencil.
|
|
469
454
|
* As a result, the watcher callback may not be fired.
|
|
470
|
-
* We work around this by manually
|
|
471
|
-
*
|
|
472
|
-
* is configured.
|
|
455
|
+
* We work around this by manually emitting a value change when the component
|
|
456
|
+
* has loaded and the watcher is configured.
|
|
473
457
|
*/
|
|
474
|
-
this.
|
|
458
|
+
this.emitValueChange(true);
|
|
475
459
|
}
|
|
476
460
|
/**
|
|
477
461
|
* Sets the value property and emits ionChange.
|
|
@@ -551,15 +535,37 @@ const AccordionGroup = class {
|
|
|
551
535
|
async getAccordions() {
|
|
552
536
|
return Array.from(this.el.querySelectorAll(':scope > ion-accordion'));
|
|
553
537
|
}
|
|
538
|
+
emitValueChange(initial) {
|
|
539
|
+
const { value, multiple } = this;
|
|
540
|
+
if (!multiple && Array.isArray(value)) {
|
|
541
|
+
/**
|
|
542
|
+
* We do some processing on the `value` array so
|
|
543
|
+
* that it looks more like an array when logged to
|
|
544
|
+
* the console.
|
|
545
|
+
* Example given ['a', 'b']
|
|
546
|
+
* Default toString() behavior: a,b
|
|
547
|
+
* Custom behavior: ['a', 'b']
|
|
548
|
+
*/
|
|
549
|
+
index.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".
|
|
550
|
+
|
|
551
|
+
Value Passed: [${value.map((v) => `'${v}'`).join(', ')}]
|
|
552
|
+
`, this.el);
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Do not use `value` here as that will not account
|
|
556
|
+
* for the adjustment we make above.
|
|
557
|
+
*/
|
|
558
|
+
this.ionValueChange.emit({ value: this.value, initial });
|
|
559
|
+
}
|
|
554
560
|
render() {
|
|
555
561
|
const { disabled, readonly, expand } = this;
|
|
556
562
|
const mode = ionicGlobal.getIonMode(this);
|
|
557
|
-
return (index.h(index.Host, { key: '
|
|
563
|
+
return (index.h(index.Host, { key: 'c69c3fa4c844cb2e88f778af9a8c757d752bb261', class: {
|
|
558
564
|
[mode]: true,
|
|
559
565
|
'accordion-group-disabled': disabled,
|
|
560
566
|
'accordion-group-readonly': readonly,
|
|
561
567
|
[`accordion-group-expand-${expand}`]: true,
|
|
562
|
-
}, role: "presentation" }, index.h("slot", { key: '
|
|
568
|
+
}, role: "presentation" }, index.h("slot", { key: 'fe661334ae44c291cc4e61f22f17a7c51f62acb7' })));
|
|
563
569
|
}
|
|
564
570
|
get el() { return index.getElement(this); }
|
|
565
571
|
static get watchers() { return {
|
|
@@ -710,6 +710,8 @@ Footer.style = {
|
|
|
710
710
|
};
|
|
711
711
|
|
|
712
712
|
const TRANSITION = 'all 0.2s ease-in-out';
|
|
713
|
+
const ROLE_NONE = 'none';
|
|
714
|
+
const ROLE_BANNER = 'banner';
|
|
713
715
|
const cloneElement = (tagName) => {
|
|
714
716
|
const getCachedEl = document.querySelector(`${tagName}.ion-cloned-element`);
|
|
715
717
|
if (getCachedEl !== null) {
|
|
@@ -836,6 +838,7 @@ const setHeaderActive = (headerIndex, active = true) => {
|
|
|
836
838
|
const toolbars = headerIndex.toolbars;
|
|
837
839
|
const ionTitles = toolbars.map((toolbar) => toolbar.ionTitleEl);
|
|
838
840
|
if (active) {
|
|
841
|
+
headerEl.setAttribute('role', ROLE_BANNER);
|
|
839
842
|
headerEl.classList.remove('header-collapse-condense-inactive');
|
|
840
843
|
ionTitles.forEach((ionTitle) => {
|
|
841
844
|
if (ionTitle) {
|
|
@@ -844,6 +847,16 @@ const setHeaderActive = (headerIndex, active = true) => {
|
|
|
844
847
|
});
|
|
845
848
|
}
|
|
846
849
|
else {
|
|
850
|
+
/**
|
|
851
|
+
* There can only be one banner landmark per page.
|
|
852
|
+
* By default, all ion-headers have the banner role.
|
|
853
|
+
* This causes an accessibility issue when using a
|
|
854
|
+
* condensed header since there are two ion-headers
|
|
855
|
+
* on the page at once (active and inactive).
|
|
856
|
+
* To solve this, the role needs to be toggled
|
|
857
|
+
* based on which header is active.
|
|
858
|
+
*/
|
|
859
|
+
headerEl.setAttribute('role', ROLE_NONE);
|
|
847
860
|
headerEl.classList.add('header-collapse-condense-inactive');
|
|
848
861
|
/**
|
|
849
862
|
* The small title should only be accessed by screen readers
|
|
@@ -903,6 +916,30 @@ const handleHeaderFade = (scrollEl, baseEl, condenseHeader) => {
|
|
|
903
916
|
});
|
|
904
917
|
});
|
|
905
918
|
};
|
|
919
|
+
/**
|
|
920
|
+
* Get the role type for the ion-header.
|
|
921
|
+
*
|
|
922
|
+
* @param isInsideMenu If ion-header is inside ion-menu.
|
|
923
|
+
* @param isCondensed If ion-header has collapse="condense".
|
|
924
|
+
* @param mode The current mode.
|
|
925
|
+
* @returns 'none' if inside ion-menu or if condensed in md
|
|
926
|
+
* mode, otherwise 'banner'.
|
|
927
|
+
*/
|
|
928
|
+
const getRoleType = (isInsideMenu, isCondensed, mode) => {
|
|
929
|
+
// If the header is inside a menu, it should not have the banner role.
|
|
930
|
+
if (isInsideMenu) {
|
|
931
|
+
return ROLE_NONE;
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* Only apply role="none" to `md` mode condensed headers
|
|
935
|
+
* since the large header is never shown.
|
|
936
|
+
*/
|
|
937
|
+
if (isCondensed && mode === 'md') {
|
|
938
|
+
return ROLE_NONE;
|
|
939
|
+
}
|
|
940
|
+
// Default to banner role.
|
|
941
|
+
return ROLE_BANNER;
|
|
942
|
+
};
|
|
906
943
|
|
|
907
944
|
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}";
|
|
908
945
|
|
|
@@ -1044,16 +1081,17 @@ const Header = class {
|
|
|
1044
1081
|
const { translucent, inheritedAttributes } = this;
|
|
1045
1082
|
const mode = ionicGlobal.getIonMode(this);
|
|
1046
1083
|
const collapse = this.collapse || 'none';
|
|
1084
|
+
const isCondensed = collapse === 'condense';
|
|
1047
1085
|
// banner role must be at top level, so remove role if inside a menu
|
|
1048
|
-
const roleType = theme.hostContext('ion-menu', this.el)
|
|
1049
|
-
return (index.h(index.Host, Object.assign({ key: '
|
|
1086
|
+
const roleType = getRoleType(theme.hostContext('ion-menu', this.el), isCondensed, mode);
|
|
1087
|
+
return (index.h(index.Host, Object.assign({ key: '863c4568cd7b8c0ec55109f193bbbaed68a1346e', role: roleType, class: {
|
|
1050
1088
|
[mode]: true,
|
|
1051
1089
|
// Used internally for styling
|
|
1052
1090
|
[`header-${mode}`]: true,
|
|
1053
1091
|
[`header-translucent`]: this.translucent,
|
|
1054
1092
|
[`header-collapse-${collapse}`]: true,
|
|
1055
1093
|
[`header-translucent-${mode}`]: this.translucent,
|
|
1056
|
-
} }, inheritedAttributes), mode === 'ios' && translucent && index.h("div", { key: '
|
|
1094
|
+
} }, inheritedAttributes), mode === 'ios' && translucent && index.h("div", { key: '25c3bdce328b0b35607d154c8b8374679313d881', class: "header-background" }), index.h("slot", { key: 'b44fab0a9be7920b9650da26117c783e751e1702' })));
|
|
1057
1095
|
}
|
|
1058
1096
|
get el() { return index.getElement(this); }
|
|
1059
1097
|
};
|
|
@@ -215,11 +215,7 @@ const Button = class {
|
|
|
215
215
|
target,
|
|
216
216
|
};
|
|
217
217
|
let fill = this.fill;
|
|
218
|
-
|
|
219
|
-
* We check both undefined and null to
|
|
220
|
-
* work around https://github.com/ionic-team/stencil/issues/3586.
|
|
221
|
-
*/
|
|
222
|
-
if (fill == null) {
|
|
218
|
+
if (fill === undefined) {
|
|
223
219
|
fill = this.inToolbar || this.inListHeader ? 'clear' : 'solid';
|
|
224
220
|
}
|
|
225
221
|
/**
|
|
@@ -232,7 +228,7 @@ const Button = class {
|
|
|
232
228
|
{
|
|
233
229
|
type !== 'button' && this.renderHiddenButton();
|
|
234
230
|
}
|
|
235
|
-
return (index.h(index.Host, { key: '
|
|
231
|
+
return (index.h(index.Host, { key: 'ed82ea53705523f9afc5f1a9addff44cc6424f27', onClick: this.handleClick, "aria-disabled": disabled ? 'true' : null, class: theme.createColorClasses(color, {
|
|
236
232
|
[mode]: true,
|
|
237
233
|
[buttonType]: true,
|
|
238
234
|
[`${buttonType}-${expand}`]: expand !== undefined,
|
|
@@ -247,7 +243,7 @@ const Button = class {
|
|
|
247
243
|
'button-disabled': disabled,
|
|
248
244
|
'ion-activatable': true,
|
|
249
245
|
'ion-focusable': true,
|
|
250
|
-
}) }, index.h(TagType, Object.assign({ key: '
|
|
246
|
+
}) }, index.h(TagType, Object.assign({ key: 'fadec13053469dd0405bbbc61b70ced568aa4826' }, attrs, { class: "button-native", part: "native", disabled: disabled, onFocus: this.onFocus, onBlur: this.onBlur }, inheritedAttributes), index.h("span", { key: '6bf0e5144fb1148002e88038522402b789689d2c', class: "button-inner" }, index.h("slot", { key: '25da0ca155cfa9e2754842c34f4fd09f576ac2d2', name: "icon-only", onSlotchange: this.slotChanged }), index.h("slot", { key: '51414065bb11953ec9d818f8d9353589bc9072c5', name: "start" }), index.h("slot", { key: 'c9b5f8842aeabd20628df2f4600f1257ea913d8d' }), index.h("slot", { key: '478dd3671c7be1909fc84e672f0fa8dfe6082263', name: "end" })), mode === 'md' && index.h("ion-ripple-effect", { key: 'e1d55f85a55144d743f58a5914cd116cb065fa8c', type: this.rippleType }))));
|
|
251
247
|
}
|
|
252
248
|
get el() { return index.getElement(this); }
|
|
253
249
|
static get watchers() { return {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
var index = require('./index-D6Wc6v08.js');
|
|
7
|
-
var
|
|
7
|
+
var validity = require('./validity-C8QoAYT2.js');
|
|
8
8
|
var helpers = require('./helpers-DrTqNghc.js');
|
|
9
9
|
var input_utils = require('./input.utils-B_QROI2g.js');
|
|
10
10
|
var theme = require('./theme-CeDs6Hcv.js');
|
|
@@ -232,22 +232,14 @@ const Input = class {
|
|
|
232
232
|
componentWillLoad() {
|
|
233
233
|
this.inheritedAttributes = Object.assign(Object.assign({}, helpers.inheritAriaAttributes(this.el)), helpers.inheritAttributes(this.el, ['tabindex', 'title', 'data-form-type', 'dir']));
|
|
234
234
|
}
|
|
235
|
-
/**
|
|
236
|
-
* Checks if the input is in an invalid state based on Ionic validation classes
|
|
237
|
-
*/
|
|
238
|
-
checkInvalidState() {
|
|
239
|
-
const hasIonTouched = this.el.classList.contains('ion-touched');
|
|
240
|
-
const hasIonInvalid = this.el.classList.contains('ion-invalid');
|
|
241
|
-
return hasIonTouched && hasIonInvalid;
|
|
242
|
-
}
|
|
243
235
|
connectedCallback() {
|
|
244
236
|
const { el } = this;
|
|
245
237
|
this.slotMutationController = input_utils.createSlotMutationController(el, ['label', 'start', 'end'], () => index.forceUpdate(this));
|
|
246
|
-
this.notchController =
|
|
238
|
+
this.notchController = validity.createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
|
|
247
239
|
// Watch for class changes to update validation state
|
|
248
240
|
if (typeof MutationObserver !== 'undefined') {
|
|
249
241
|
this.validationObserver = new MutationObserver(() => {
|
|
250
|
-
const newIsInvalid =
|
|
242
|
+
const newIsInvalid = validity.checkInvalidState(el);
|
|
251
243
|
if (this.isInvalid !== newIsInvalid) {
|
|
252
244
|
this.isInvalid = newIsInvalid;
|
|
253
245
|
// Force a re-render to update aria-describedby immediately
|
|
@@ -260,7 +252,7 @@ const Input = class {
|
|
|
260
252
|
});
|
|
261
253
|
}
|
|
262
254
|
// Always set initial state
|
|
263
|
-
this.isInvalid =
|
|
255
|
+
this.isInvalid = validity.checkInvalidState(el);
|
|
264
256
|
this.debounceChanged();
|
|
265
257
|
{
|
|
266
258
|
document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
|
|
@@ -524,7 +516,7 @@ const Input = class {
|
|
|
524
516
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
525
517
|
*/
|
|
526
518
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
|
527
|
-
return (index.h(index.Host, { key: '
|
|
519
|
+
return (index.h(index.Host, { key: '97b5308021064d9e7434ef2d3d96f27045c1b0c4', class: theme.createColorClasses(this.color, {
|
|
528
520
|
[mode]: true,
|
|
529
521
|
'has-value': hasValue,
|
|
530
522
|
'has-focus': hasFocus,
|
|
@@ -535,14 +527,14 @@ const Input = class {
|
|
|
535
527
|
'in-item': inItem,
|
|
536
528
|
'in-item-color': theme.hostContext('ion-item.ion-color', this.el),
|
|
537
529
|
'input-disabled': disabled,
|
|
538
|
-
}) }, index.h("label", { key: '
|
|
530
|
+
}) }, index.h("label", { key: '353f68726ce180299bd9adc81e5ff7d26a48f54f', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), index.h("div", { key: '2034b4bad04fc157f3298a1805819216b6f439d0', class: "native-wrapper", onClick: this.onLabelClick }, index.h("slot", { key: '96bb5e30176b2bd76dfb75bfbf6c1c3d4403f4bb', name: "start" }), index.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 && (index.h("button", { key: '95f3df17b7691d9a2e7dcd4a51f16a94aa3ca36f', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
|
|
539
531
|
/**
|
|
540
532
|
* This prevents mobile browsers from
|
|
541
533
|
* blurring the input when the clear
|
|
542
534
|
* button is activated.
|
|
543
535
|
*/
|
|
544
536
|
ev.preventDefault();
|
|
545
|
-
}, onClick: this.clearTextInput }, index.h("ion-icon", { key: '
|
|
537
|
+
}, onClick: this.clearTextInput }, index.h("ion-icon", { key: '16b0af75eed50c8115fb5597f73b5fbf71c2530e', "aria-hidden": "true", icon: clearIconData }))), index.h("slot", { key: 'c48da0f8ddb3764ac43efa705bb4a6bb2d9cc2fd', name: "end" })), shouldRenderHighlight && index.h("div", { key: 'f15238481fc20de56ca7ecb6e350b3c024cc755e', class: "input-highlight" })), this.renderBottomContent()));
|
|
546
538
|
}
|
|
547
539
|
get el() { return index.getElement(this); }
|
|
548
540
|
static get watchers() { return {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
var index = require('./index-D6Wc6v08.js');
|
|
7
|
-
var
|
|
7
|
+
var validity = require('./validity-C8QoAYT2.js');
|
|
8
8
|
var compareWithUtils = require('./compare-with-utils-DSicavqM.js');
|
|
9
9
|
var helpers = require('./helpers-DrTqNghc.js');
|
|
10
10
|
var overlays = require('./overlays-DxIZwUXI.js');
|
|
@@ -45,6 +45,10 @@ const Select = class {
|
|
|
45
45
|
* is applied in both cases.
|
|
46
46
|
*/
|
|
47
47
|
this.hasFocus = false;
|
|
48
|
+
/**
|
|
49
|
+
* Track validation state for proper aria-live announcements.
|
|
50
|
+
*/
|
|
51
|
+
this.isInvalid = false;
|
|
48
52
|
/**
|
|
49
53
|
* The text to display on the cancel button.
|
|
50
54
|
*/
|
|
@@ -161,7 +165,7 @@ const Select = class {
|
|
|
161
165
|
}
|
|
162
166
|
async connectedCallback() {
|
|
163
167
|
const { el } = this;
|
|
164
|
-
this.notchController =
|
|
168
|
+
this.notchController = validity.createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
|
|
165
169
|
this.updateOverlayOptions();
|
|
166
170
|
this.emitStyle();
|
|
167
171
|
this.mutationO = watchOptions.watchForOptions(this.el, 'ion-select-option', async () => {
|
|
@@ -174,9 +178,46 @@ const Select = class {
|
|
|
174
178
|
*/
|
|
175
179
|
index.forceUpdate(this);
|
|
176
180
|
});
|
|
181
|
+
// Watch for class changes to update validation state.
|
|
182
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
183
|
+
this.validationObserver = new MutationObserver(() => {
|
|
184
|
+
const newIsInvalid = validity.checkInvalidState(this.el);
|
|
185
|
+
if (this.isInvalid !== newIsInvalid) {
|
|
186
|
+
this.isInvalid = newIsInvalid;
|
|
187
|
+
/**
|
|
188
|
+
* Screen readers tend to announce changes
|
|
189
|
+
* to `aria-describedby` when the attribute
|
|
190
|
+
* is changed during a blur event for a
|
|
191
|
+
* native form control.
|
|
192
|
+
* However, the announcement can be spotty
|
|
193
|
+
* when using a non-native form control
|
|
194
|
+
* and `forceUpdate()`.
|
|
195
|
+
* This is due to `forceUpdate()` internally
|
|
196
|
+
* rescheduling the DOM update to a lower
|
|
197
|
+
* priority queue regardless if it's called
|
|
198
|
+
* inside a Promise or not, thus causing
|
|
199
|
+
* the screen reader to potentially miss the
|
|
200
|
+
* change.
|
|
201
|
+
* By using a State variable inside a Promise,
|
|
202
|
+
* it guarantees a re-render immediately at
|
|
203
|
+
* a higher priority.
|
|
204
|
+
*/
|
|
205
|
+
Promise.resolve().then(() => {
|
|
206
|
+
this.hintTextID = this.getHintTextID();
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
this.validationObserver.observe(el, {
|
|
211
|
+
attributes: true,
|
|
212
|
+
attributeFilter: ['class'],
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
// Always set initial state
|
|
216
|
+
this.isInvalid = validity.checkInvalidState(this.el);
|
|
177
217
|
}
|
|
178
218
|
componentWillLoad() {
|
|
179
219
|
this.inheritedAttributes = helpers.inheritAttributes(this.el, ['aria-label']);
|
|
220
|
+
this.hintTextID = this.getHintTextID();
|
|
180
221
|
}
|
|
181
222
|
componentDidLoad() {
|
|
182
223
|
/**
|
|
@@ -200,6 +241,11 @@ const Select = class {
|
|
|
200
241
|
this.notchController.destroy();
|
|
201
242
|
this.notchController = undefined;
|
|
202
243
|
}
|
|
244
|
+
// Clean up validation observer to prevent memory leaks.
|
|
245
|
+
if (this.validationObserver) {
|
|
246
|
+
this.validationObserver.disconnect();
|
|
247
|
+
this.validationObserver = undefined;
|
|
248
|
+
}
|
|
203
249
|
}
|
|
204
250
|
/**
|
|
205
251
|
* Open the select overlay. The overlay is either an alert, action sheet, or popover,
|
|
@@ -670,11 +716,11 @@ const Select = class {
|
|
|
670
716
|
}
|
|
671
717
|
renderListbox() {
|
|
672
718
|
const { disabled, inputId, isExpanded, required } = this;
|
|
673
|
-
return (index.h("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "dialog", "aria-expanded": `${isExpanded}`, "aria-describedby": this.
|
|
719
|
+
return (index.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) }));
|
|
674
720
|
}
|
|
675
721
|
getHintTextID() {
|
|
676
|
-
const {
|
|
677
|
-
if (
|
|
722
|
+
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
723
|
+
if (isInvalid && errorText) {
|
|
678
724
|
return errorTextId;
|
|
679
725
|
}
|
|
680
726
|
if (helperText) {
|
|
@@ -686,10 +732,10 @@ const Select = class {
|
|
|
686
732
|
* Renders the helper text or error text values
|
|
687
733
|
*/
|
|
688
734
|
renderHintText() {
|
|
689
|
-
const { helperText, errorText, helperTextId, errorTextId } = this;
|
|
735
|
+
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
690
736
|
return [
|
|
691
|
-
index.h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText),
|
|
692
|
-
index.h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText),
|
|
737
|
+
index.h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null),
|
|
738
|
+
index.h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null),
|
|
693
739
|
];
|
|
694
740
|
}
|
|
695
741
|
/**
|
|
@@ -737,7 +783,7 @@ const Select = class {
|
|
|
737
783
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
738
784
|
*/
|
|
739
785
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || isExpanded || hasStartEndSlots));
|
|
740
|
-
return (index.h(index.Host, { key: '
|
|
786
|
+
return (index.h(index.Host, { key: '35b5e18e6f79a802ff2d46d1242e80ff755cc0b9', onClick: this.onClick, class: theme.createColorClasses(this.color, {
|
|
741
787
|
[mode]: true,
|
|
742
788
|
'in-item': inItem,
|
|
743
789
|
'in-item-color': theme.hostContext('ion-item.ion-color', el),
|
|
@@ -755,7 +801,7 @@ const Select = class {
|
|
|
755
801
|
[`select-justify-${justify}`]: justifyEnabled,
|
|
756
802
|
[`select-shape-${shape}`]: shape !== undefined,
|
|
757
803
|
[`select-label-placement-${labelPlacement}`]: true,
|
|
758
|
-
}) }, index.h("label", { key: '
|
|
804
|
+
}) }, index.h("label", { key: '6005b34a0c50bc4d7653a4276bc232ecd02e083c', class: "select-wrapper", id: "select-label", onClick: this.onLabelClick }, this.renderLabelContainer(), index.h("div", { key: 'c7e07aa81ae856c057f16275dd058f37c5670a47', class: "select-wrapper-inner" }, index.h("slot", { key: '7fc2deefe0424404caacdbbd9e08ed43ba55d28a', name: "start" }), index.h("div", { key: '157d74ee717b1bc30b5f1c233a09b0c8456aa68e', class: "native-wrapper", ref: (el) => (this.nativeWrapperEl = el), part: "container" }, this.renderSelectText(), this.renderListbox()), index.h("slot", { key: 'ea66db304528b82bf9317730b6dce3db2612f235', name: "end" }), !hasFloatingOrStackedLabel && this.renderSelectIcon()), hasFloatingOrStackedLabel && this.renderSelectIcon(), shouldRenderHighlight && index.h("div", { key: '786eb1530b7476f0615d4e7c0bf4e7e4dc66509c', class: "select-highlight" })), this.renderBottomContent()));
|
|
759
805
|
}
|
|
760
806
|
get el() { return index.getElement(this); }
|
|
761
807
|
static get watchers() { return {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
var index = require('./index-D6Wc6v08.js');
|
|
7
|
-
var
|
|
7
|
+
var validity = require('./validity-C8QoAYT2.js');
|
|
8
8
|
var helpers = require('./helpers-DrTqNghc.js');
|
|
9
9
|
var input_utils = require('./input.utils-B_QROI2g.js');
|
|
10
10
|
var theme = require('./theme-CeDs6Hcv.js');
|
|
@@ -192,22 +192,14 @@ const Textarea = class {
|
|
|
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 = input_utils.createSlotMutationController(el, ['label', 'start', 'end'], () => index.forceUpdate(this));
|
|
206
|
-
this.notchController =
|
|
198
|
+
this.notchController = validity.createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
|
|
207
199
|
// Watch for class changes to update validation state
|
|
208
200
|
if (typeof MutationObserver !== 'undefined') {
|
|
209
201
|
this.validationObserver = new MutationObserver(() => {
|
|
210
|
-
const newIsInvalid = this.
|
|
202
|
+
const newIsInvalid = validity.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 = class {
|
|
|
220
212
|
});
|
|
221
213
|
}
|
|
222
214
|
// Always set initial state
|
|
223
|
-
this.isInvalid = this.
|
|
215
|
+
this.isInvalid = validity.checkInvalidState(this.el);
|
|
224
216
|
this.debounceChanged();
|
|
225
217
|
{
|
|
226
218
|
document.dispatchEvent(new CustomEvent('ionInputDidLoad', {
|
|
@@ -484,7 +476,7 @@ const Textarea = class {
|
|
|
484
476
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
485
477
|
*/
|
|
486
478
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
|
487
|
-
return (index.h(index.Host, { key: '
|
|
479
|
+
return (index.h(index.Host, { key: 'a70a62d7aae3831a50acd74f60b930925ada1326', class: theme.createColorClasses(this.color, {
|
|
488
480
|
[mode]: true,
|
|
489
481
|
'has-value': hasValue,
|
|
490
482
|
'has-focus': hasFocus,
|
|
@@ -493,7 +485,7 @@ const Textarea = class {
|
|
|
493
485
|
[`textarea-shape-${shape}`]: shape !== undefined,
|
|
494
486
|
[`textarea-label-placement-${labelPlacement}`]: true,
|
|
495
487
|
'textarea-disabled': disabled,
|
|
496
|
-
}) }, index.h("label", { key: '
|
|
488
|
+
}) }, index.h("label", { key: '8a2dd59a60f7469df84018eb0ede3a9ec3862703', class: "textarea-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), index.h("div", { key: '1bfc368236e3da7a225a45118c27fbfc1fe5fa46', class: "textarea-wrapper-inner" }, index.h("div", { key: '215cbb2635ff52e31a8973376989b85e7245d40f', class: "start-slot-wrapper" }, index.h("slot", { key: '9f6b461cdee9d629deb695d2bea054ece2f32305', name: "start" })), index.h("div", { key: 'c1af35a2d5bc452bebe0b22a26d15ff52b4e9fc8', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, index.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)), index.h("div", { key: 'c053ea8b865d0e29763aed2e4939cc9c9e374c15', class: "end-slot-wrapper" }, index.h("slot", { key: '930aa641833b0df54b9ea10368fc2f46d5f491f6', name: "end" }))), shouldRenderHighlight && index.h("div", { key: '8d12597d15f5f429d80e8272ea99e64ed924e482', class: "textarea-highlight" })), this.renderBottomContent()));
|
|
497
489
|
}
|
|
498
490
|
get el() { return index.getElement(this); }
|
|
499
491
|
static get watchers() { return {
|