voyager-ionic-core 8.7.9 → 8.7.11
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/checkbox.js +63 -9
- package/components/ion-datetime.js +35 -2
- package/components/ion-input.js +2 -1
- package/components/ion-select.js +7 -6
- package/components/ion-textarea.js +2 -1
- package/components/ion-toggle.js +62 -12
- package/components/notch-controller.js +153 -0
- package/components/radio-group.js +60 -7
- package/components/validity.js +1 -150
- package/dist/cjs/ion-checkbox.cjs.entry.js +60 -8
- package/dist/cjs/ion-datetime_3.cjs.entry.js +35 -2
- package/dist/cjs/ion-input.cjs.entry.js +3 -2
- package/dist/cjs/ion-radio_2.cjs.entry.js +57 -6
- package/dist/cjs/ion-select_3.cjs.entry.js +7 -6
- package/dist/cjs/ion-textarea.cjs.entry.js +3 -2
- package/dist/cjs/ion-toggle.cjs.entry.js +58 -10
- package/dist/cjs/ionic.cjs.js +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/{validity-C8QoAYT2.js → notch-controller-Bzqhjm4f.js} +0 -14
- package/dist/cjs/validity-BpS37YFM.js +19 -0
- package/dist/collection/components/checkbox/checkbox.js +67 -9
- package/dist/collection/components/datetime/datetime.js +35 -2
- package/dist/collection/components/radio-group/radio-group.js +64 -7
- package/dist/collection/components/select/select.js +5 -5
- package/dist/collection/components/toggle/toggle.js +62 -12
- package/dist/collection/utils/test/playwright/page/utils/set-content.js +7 -0
- package/dist/docs.json +1 -1
- package/dist/esm/ion-checkbox.entry.js +60 -8
- package/dist/esm/ion-datetime_3.entry.js +35 -2
- package/dist/esm/ion-input.entry.js +2 -1
- package/dist/esm/ion-radio_2.entry.js +57 -6
- package/dist/esm/ion-select_3.entry.js +6 -5
- package/dist/esm/ion-textarea.entry.js +2 -1
- package/dist/esm/ion-toggle.entry.js +58 -10
- package/dist/esm/ionic.js +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/{validity-B8oWougr.js → notch-controller-BwelN_JM.js} +1 -14
- package/dist/esm/validity-DJztqcrH.js +17 -0
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-40c261a3.entry.js +4 -0
- package/dist/ionic/p-4e41ea20.entry.js +4 -0
- package/dist/ionic/p-7380261c.entry.js +4 -0
- package/dist/ionic/{p-DieJyvMP.js → p-DCv9sLH2.js} +1 -1
- package/dist/ionic/p-DJztqcrH.js +4 -0
- package/dist/ionic/p-c19f63d0.entry.js +4 -0
- package/dist/ionic/p-cb93126d.entry.js +4 -0
- package/dist/ionic/p-d1f54e28.entry.js +4 -0
- package/dist/ionic/p-d3014190.entry.js +4 -0
- package/dist/types/components/checkbox/checkbox.d.ts +9 -1
- package/dist/types/components/datetime/datetime.d.ts +10 -0
- package/dist/types/components/radio-group/radio-group.d.ts +9 -1
- package/dist/types/components/select/select.d.ts +2 -2
- package/dist/types/components/toggle/toggle.d.ts +7 -1
- package/dist/types/utils/forms/validity.d.ts +1 -1
- package/hydrate/index.js +312 -227
- package/hydrate/index.mjs +312 -227
- package/package.json +2 -2
- package/dist/ionic/p-4cc26913.entry.js +0 -4
- package/dist/ionic/p-4efea47a.entry.js +0 -4
- package/dist/ionic/p-7bcfc421.entry.js +0 -4
- package/dist/ionic/p-8bdfc8f6.entry.js +0 -4
- package/dist/ionic/p-dc2e126d.entry.js +0 -4
- package/dist/ionic/p-f65f9308.entry.js +0 -4
- package/dist/ionic/p-fc278823.entry.js +0 -4
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { r as registerInstance, c as createEvent, h, d as Host, g as getElement } from './index-C8IsBmNU.js';
|
|
5
5
|
import { i as inheritAriaAttributes, a as renderHiddenInput } from './helpers-DEn3pfjm.js';
|
|
6
|
+
import { c as checkInvalidState } from './validity-DJztqcrH.js';
|
|
6
7
|
import { c as createColorClasses, h as hostContext } from './theme-DiVJyqlX.js';
|
|
7
8
|
import { b as getIonMode } from './ionic-global-CDrldh-5.js';
|
|
8
9
|
|
|
@@ -59,6 +60,10 @@ const Checkbox = class {
|
|
|
59
60
|
* submitting if the value is invalid.
|
|
60
61
|
*/
|
|
61
62
|
this.required = false;
|
|
63
|
+
/**
|
|
64
|
+
* Track validation state for proper aria-live announcements.
|
|
65
|
+
*/
|
|
66
|
+
this.isInvalid = false;
|
|
62
67
|
/**
|
|
63
68
|
* Sets the checked property and emits
|
|
64
69
|
* the ionChange event. Use this to update the
|
|
@@ -105,16 +110,63 @@ const Checkbox = class {
|
|
|
105
110
|
ev.stopPropagation();
|
|
106
111
|
};
|
|
107
112
|
}
|
|
113
|
+
connectedCallback() {
|
|
114
|
+
const { el } = this;
|
|
115
|
+
// Watch for class changes to update validation state.
|
|
116
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
117
|
+
this.validationObserver = new MutationObserver(() => {
|
|
118
|
+
const newIsInvalid = checkInvalidState(el);
|
|
119
|
+
if (this.isInvalid !== newIsInvalid) {
|
|
120
|
+
this.isInvalid = newIsInvalid;
|
|
121
|
+
/**
|
|
122
|
+
* Screen readers tend to announce changes
|
|
123
|
+
* to `aria-describedby` when the attribute
|
|
124
|
+
* is changed during a blur event for a
|
|
125
|
+
* native form control.
|
|
126
|
+
* However, the announcement can be spotty
|
|
127
|
+
* when using a non-native form control
|
|
128
|
+
* and `forceUpdate()`.
|
|
129
|
+
* This is due to `forceUpdate()` internally
|
|
130
|
+
* rescheduling the DOM update to a lower
|
|
131
|
+
* priority queue regardless if it's called
|
|
132
|
+
* inside a Promise or not, thus causing
|
|
133
|
+
* the screen reader to potentially miss the
|
|
134
|
+
* change.
|
|
135
|
+
* By using a State variable inside a Promise,
|
|
136
|
+
* it guarantees a re-render immediately at
|
|
137
|
+
* a higher priority.
|
|
138
|
+
*/
|
|
139
|
+
Promise.resolve().then(() => {
|
|
140
|
+
this.hintTextId = this.getHintTextId();
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
this.validationObserver.observe(el, {
|
|
145
|
+
attributes: true,
|
|
146
|
+
attributeFilter: ['class'],
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
// Always set initial state
|
|
150
|
+
this.isInvalid = checkInvalidState(el);
|
|
151
|
+
}
|
|
108
152
|
componentWillLoad() {
|
|
109
153
|
this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
|
|
154
|
+
this.hintTextId = this.getHintTextId();
|
|
155
|
+
}
|
|
156
|
+
disconnectedCallback() {
|
|
157
|
+
// Clean up validation observer to prevent memory leaks.
|
|
158
|
+
if (this.validationObserver) {
|
|
159
|
+
this.validationObserver.disconnect();
|
|
160
|
+
this.validationObserver = undefined;
|
|
161
|
+
}
|
|
110
162
|
}
|
|
111
163
|
/** @internal */
|
|
112
164
|
async setFocus() {
|
|
113
165
|
this.el.focus();
|
|
114
166
|
}
|
|
115
|
-
|
|
116
|
-
const {
|
|
117
|
-
if (
|
|
167
|
+
getHintTextId() {
|
|
168
|
+
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
169
|
+
if (isInvalid && errorText) {
|
|
118
170
|
return errorTextId;
|
|
119
171
|
}
|
|
120
172
|
if (helperText) {
|
|
@@ -127,7 +179,7 @@ const Checkbox = class {
|
|
|
127
179
|
* This element should only be rendered if hint text is set.
|
|
128
180
|
*/
|
|
129
181
|
renderHintText() {
|
|
130
|
-
const { helperText, errorText, helperTextId, errorTextId } = this;
|
|
182
|
+
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
131
183
|
/**
|
|
132
184
|
* undefined and empty string values should
|
|
133
185
|
* be treated as not having helper/error text.
|
|
@@ -136,7 +188,7 @@ const Checkbox = class {
|
|
|
136
188
|
if (!hasHintText) {
|
|
137
189
|
return;
|
|
138
190
|
}
|
|
139
|
-
return (h("div", { class: "checkbox-bottom" }, h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText), h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText)));
|
|
191
|
+
return (h("div", { class: "checkbox-bottom" }, h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null), h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null)));
|
|
140
192
|
}
|
|
141
193
|
render() {
|
|
142
194
|
const { color, checked, disabled, el, getSVGPath, indeterminate, inheritedAttributes, inputId, justify, labelPlacement, name, value, alignment, required, } = this;
|
|
@@ -146,7 +198,7 @@ const Checkbox = class {
|
|
|
146
198
|
renderHiddenInput(true, el, name, checked ? value : '', disabled);
|
|
147
199
|
// The host element must have a checkbox role to ensure proper VoiceOver
|
|
148
200
|
// support in Safari for accessibility.
|
|
149
|
-
return (h(Host, { key: '
|
|
201
|
+
return (h(Host, { key: 'ae0fbd4b21accbac132e6b85c513512ad9179394', role: "checkbox", "aria-checked": indeterminate ? 'mixed' : `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-labelledby": hasLabelContent ? this.inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, "aria-required": required ? 'true' : undefined, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, onClick: this.onClick, class: createColorClasses(color, {
|
|
150
202
|
[mode]: true,
|
|
151
203
|
'in-item': hostContext('ion-item', el),
|
|
152
204
|
'checkbox-checked': checked,
|
|
@@ -156,10 +208,10 @@ const Checkbox = class {
|
|
|
156
208
|
[`checkbox-justify-${justify}`]: justify !== undefined,
|
|
157
209
|
[`checkbox-alignment-${alignment}`]: alignment !== undefined,
|
|
158
210
|
[`checkbox-label-placement-${labelPlacement}`]: true,
|
|
159
|
-
}) }, h("label", { key: '
|
|
211
|
+
}) }, h("label", { key: '7a3d7f3c27dde514f2dbf2e34f4629fad33ec3bf', class: "checkbox-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: '4130d77ddf034271fecccda14e101a5a809921b6', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, required: required }, inheritedAttributes)), h("div", { key: '5daa74f4e62b0947e37764762524001ee42609d9', class: {
|
|
160
212
|
'label-text-wrapper': true,
|
|
161
213
|
'label-text-wrapper-hidden': !hasLabelContent,
|
|
162
|
-
}, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '
|
|
214
|
+
}, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '23ff66138f8c3a2f56f39113fc842d54b2f7952a' }), this.renderHintText()), h("div", { key: 'ab914d9623c19fc46821d5e62db92f1192ebbe7e', class: "native-wrapper" }, h("svg", { key: '66e3f4f5dcaa9756fb0e9452299954f9ed3dcb7b', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
|
|
163
215
|
}
|
|
164
216
|
getSVGPath(mode, indeterminate) {
|
|
165
217
|
let path = indeterminate ? (h("path", { d: "M6 12L18 12", part: "mark" })) : (h("path", { d: "M5.9,12.5l3.8,3.8l8.8-8.8", part: "mark" }));
|
|
@@ -784,6 +784,28 @@ const Datetime = class {
|
|
|
784
784
|
destroyKeyboardMO();
|
|
785
785
|
}
|
|
786
786
|
};
|
|
787
|
+
/**
|
|
788
|
+
* TODO(FW-6931): Remove this fallback upon solving the root cause
|
|
789
|
+
* Fallback to ensure the datetime becomes ready even if
|
|
790
|
+
* IntersectionObserver never reports it as intersecting.
|
|
791
|
+
*
|
|
792
|
+
* This is primarily used in environments where the observer
|
|
793
|
+
* might not fire as expected, such as when running under
|
|
794
|
+
* synthetic tests that stub IntersectionObserver.
|
|
795
|
+
*/
|
|
796
|
+
this.ensureReadyIfVisible = () => {
|
|
797
|
+
if (this.el.classList.contains('datetime-ready')) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
const rect = this.el.getBoundingClientRect();
|
|
801
|
+
if (rect.width === 0 || rect.height === 0) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
this.initializeListeners();
|
|
805
|
+
writeTask(() => {
|
|
806
|
+
this.el.classList.add('datetime-ready');
|
|
807
|
+
});
|
|
808
|
+
};
|
|
787
809
|
this.processValue = (value) => {
|
|
788
810
|
const hasValue = value !== null && value !== undefined && value !== '' && (!Array.isArray(value) || value.length > 0);
|
|
789
811
|
const valueToProcess = hasValue ? parseDate(value) : this.defaultParts;
|
|
@@ -1101,6 +1123,17 @@ const Datetime = class {
|
|
|
1101
1123
|
* triggering the `hiddenIO` observer below.
|
|
1102
1124
|
*/
|
|
1103
1125
|
raf(() => visibleIO === null || visibleIO === void 0 ? void 0 : visibleIO.observe(intersectionTrackerRef));
|
|
1126
|
+
/**
|
|
1127
|
+
* TODO(FW-6931): Remove this fallback upon solving the root cause
|
|
1128
|
+
* Fallback: If IntersectionObserver never reports that the
|
|
1129
|
+
* datetime is visible but the host clearly has layout, ensure
|
|
1130
|
+
* we still initialize listeners and mark the component as ready.
|
|
1131
|
+
*
|
|
1132
|
+
* We schedule this after everything has had a chance to run.
|
|
1133
|
+
*/
|
|
1134
|
+
setTimeout(() => {
|
|
1135
|
+
this.ensureReadyIfVisible();
|
|
1136
|
+
}, 100);
|
|
1104
1137
|
/**
|
|
1105
1138
|
* We need to clean up listeners when the datetime is hidden
|
|
1106
1139
|
* in a popover/modal so that we can properly scroll containers
|
|
@@ -1856,7 +1889,7 @@ const Datetime = class {
|
|
|
1856
1889
|
const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
|
|
1857
1890
|
const hasWheelVariant = hasDatePresentation && preferWheel;
|
|
1858
1891
|
renderHiddenInput(true, el, name, formatValue(value), disabled);
|
|
1859
|
-
return (h(Host, { key: '
|
|
1892
|
+
return (h(Host, { key: 'efdbc0922670a841bc667ceac392cdc1dedffd01', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
|
|
1860
1893
|
[mode]: true,
|
|
1861
1894
|
['datetime-readonly']: readonly,
|
|
1862
1895
|
['datetime-disabled']: disabled,
|
|
@@ -1866,7 +1899,7 @@ const Datetime = class {
|
|
|
1866
1899
|
[`datetime-size-${size}`]: true,
|
|
1867
1900
|
[`datetime-prefer-wheel`]: hasWheelVariant,
|
|
1868
1901
|
[`datetime-grid`]: isGridStyle,
|
|
1869
|
-
})) }, h("div", { key: '
|
|
1902
|
+
})) }, h("div", { key: '3f8bb75fcb0baff55182ef3aa1b535eacc58d81f', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
|
|
1870
1903
|
}
|
|
1871
1904
|
get el() { return getElement(this); }
|
|
1872
1905
|
static get watchers() { return {
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
4
|
import { r as registerInstance, c as createEvent, i as forceUpdate, h, d as Host, g as getElement } from './index-C8IsBmNU.js';
|
|
5
|
-
import { c as createNotchController
|
|
5
|
+
import { c as createNotchController } from './notch-controller-BwelN_JM.js';
|
|
6
|
+
import { c as checkInvalidState } from './validity-DJztqcrH.js';
|
|
6
7
|
import { d as debounceEvent, i as inheritAriaAttributes, b as inheritAttributes, c as componentOnReady } from './helpers-DEn3pfjm.js';
|
|
7
8
|
import { c as createSlotMutationController, g as getCounterText } from './input.utils-DrvTa8gz.js';
|
|
8
9
|
import { h as hostContext, c as createColorClasses } from './theme-DiVJyqlX.js';
|
|
@@ -6,6 +6,7 @@ import { f as addEventListener, m as removeEventListener, a as renderHiddenInput
|
|
|
6
6
|
import { i as isOptionSelected } from './compare-with-utils-sObYyvOy.js';
|
|
7
7
|
import { h as hostContext, c as createColorClasses } from './theme-DiVJyqlX.js';
|
|
8
8
|
import { b as getIonMode } from './ionic-global-CDrldh-5.js';
|
|
9
|
+
import { c as checkInvalidState } from './validity-DJztqcrH.js';
|
|
9
10
|
|
|
10
11
|
const radioIosCss = ":host{--inner-border-radius:50%;display:inline-block;position:relative;max-width:100%;min-height:inherit;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:2;-webkit-box-sizing:border-box;box-sizing:border-box}:host(.radio-disabled){pointer-events:none}.radio-icon{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:100%;height:100%;contain:layout size style}.radio-icon,.radio-inner{-webkit-box-sizing:border-box;box-sizing:border-box}input{position:absolute;top:0;left:0;right:0;bottom:0;width:100%;height:100%;margin:0;padding:0;border:0;outline:0;clip:rect(0 0 0 0);opacity:0;overflow:hidden;-webkit-appearance:none;-moz-appearance:none}:host(:focus){outline:none}:host(.in-item){-ms-flex:1 1 0px;flex:1 1 0;width:100%;height:100%}:host([slot=start]),:host([slot=end]){-ms-flex:initial;flex:initial;width:auto}.radio-wrapper{display:-ms-flexbox;display:flex;position:relative;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;height:inherit;min-height:inherit;cursor:inherit}.label-text-wrapper{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}:host(.in-item) .label-text-wrapper{margin-top:10px;margin-bottom:10px}:host(.in-item.radio-label-placement-stacked) .label-text-wrapper{margin-top:10px;margin-bottom:16px}:host(.in-item.radio-label-placement-stacked) .native-wrapper{margin-bottom:10px}.label-text-wrapper-hidden{display:none}.native-wrapper{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}:host(.radio-justify-space-between) .radio-wrapper{-ms-flex-pack:justify;justify-content:space-between}:host(.radio-justify-start) .radio-wrapper{-ms-flex-pack:start;justify-content:start}:host(.radio-justify-end) .radio-wrapper{-ms-flex-pack:end;justify-content:end}:host(.radio-alignment-start) .radio-wrapper{-ms-flex-align:start;align-items:start}:host(.radio-alignment-center) .radio-wrapper{-ms-flex-align:center;align-items:center}:host(.radio-justify-space-between),:host(.radio-justify-start),:host(.radio-justify-end),:host(.radio-alignment-start),:host(.radio-alignment-center){display:block}:host(.radio-label-placement-start) .radio-wrapper{-ms-flex-direction:row;flex-direction:row}:host(.radio-label-placement-start) .label-text-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:16px;margin-inline-end:16px}:host(.radio-label-placement-end) .radio-wrapper{-ms-flex-direction:row-reverse;flex-direction:row-reverse}:host(.radio-label-placement-end) .label-text-wrapper{-webkit-margin-start:16px;margin-inline-start:16px;-webkit-margin-end:0;margin-inline-end:0}:host(.radio-label-placement-fixed) .label-text-wrapper{-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:16px;margin-inline-end:16px}:host(.radio-label-placement-fixed) .label-text-wrapper{-ms-flex:0 0 100px;flex:0 0 100px;width:100px;min-width:100px}:host(.radio-label-placement-stacked) .radio-wrapper{-ms-flex-direction:column;flex-direction:column}:host(.radio-label-placement-stacked) .label-text-wrapper{-webkit-transform:scale(0.75);transform:scale(0.75);margin-left:0;margin-right:0;margin-bottom:16px;max-width:calc(100% / 0.75)}:host(.radio-label-placement-stacked.radio-alignment-start) .label-text-wrapper{-webkit-transform-origin:left top;transform-origin:left top}:host-context([dir=rtl]):host(.radio-label-placement-stacked.radio-alignment-start) .label-text-wrapper,:host-context([dir=rtl]).radio-label-placement-stacked.radio-alignment-start .label-text-wrapper{-webkit-transform-origin:right top;transform-origin:right top}@supports selector(:dir(rtl)){:host(.radio-label-placement-stacked.radio-alignment-start:dir(rtl)) .label-text-wrapper{-webkit-transform-origin:right top;transform-origin:right top}}:host(.radio-label-placement-stacked.radio-alignment-center) .label-text-wrapper{-webkit-transform-origin:center top;transform-origin:center top}:host-context([dir=rtl]):host(.radio-label-placement-stacked.radio-alignment-center) .label-text-wrapper,:host-context([dir=rtl]).radio-label-placement-stacked.radio-alignment-center .label-text-wrapper{-webkit-transform-origin:calc(100% - center) top;transform-origin:calc(100% - center) top}@supports selector(:dir(rtl)){:host(.radio-label-placement-stacked.radio-alignment-center:dir(rtl)) .label-text-wrapper{-webkit-transform-origin:calc(100% - center) top;transform-origin:calc(100% - center) top}}:host{--color-checked:var(--ion-color-primary, #0054e9)}:host(.ion-color.radio-checked) .radio-inner{border-color:var(--ion-color-base)}.item-radio.item-ios ion-label{-webkit-margin-start:0;margin-inline-start:0}.radio-inner{width:33%;height:50%}:host(.radio-checked) .radio-inner{-webkit-transform:rotate(45deg);transform:rotate(45deg);border-width:0.125rem;border-top-width:0;border-left-width:0;border-style:solid;border-color:var(--color-checked)}:host(.radio-disabled){opacity:0.3}:host(.ion-focused) .radio-icon::after{border-radius:var(--inner-border-radius);top:-8px;display:block;position:absolute;width:36px;height:36px;background:var(--ion-color-primary-tint, #1a65eb);content:\"\";opacity:0.2}:host(.ion-focused) .radio-icon::after{inset-inline-start:-9px}.native-wrapper .radio-icon{width:0.9375rem;height:1.5rem}";
|
|
11
12
|
|
|
@@ -175,6 +176,10 @@ const RadioGroup = class {
|
|
|
175
176
|
this.helperTextId = `${this.inputId}-helper-text`;
|
|
176
177
|
this.errorTextId = `${this.inputId}-error-text`;
|
|
177
178
|
this.labelId = `${this.inputId}-lbl`;
|
|
179
|
+
/**
|
|
180
|
+
* Track validation state for proper aria-live announcements.
|
|
181
|
+
*/
|
|
182
|
+
this.isInvalid = false;
|
|
178
183
|
/**
|
|
179
184
|
* If `true`, the radios can be deselected.
|
|
180
185
|
*/
|
|
@@ -256,6 +261,52 @@ const RadioGroup = class {
|
|
|
256
261
|
this.labelId = label.id = this.name + '-lbl';
|
|
257
262
|
}
|
|
258
263
|
}
|
|
264
|
+
// Watch for class changes to update validation state.
|
|
265
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
266
|
+
this.validationObserver = new MutationObserver(() => {
|
|
267
|
+
const newIsInvalid = checkInvalidState(this.el);
|
|
268
|
+
if (this.isInvalid !== newIsInvalid) {
|
|
269
|
+
this.isInvalid = newIsInvalid;
|
|
270
|
+
/**
|
|
271
|
+
* Screen readers tend to announce changes
|
|
272
|
+
* to `aria-describedby` when the attribute
|
|
273
|
+
* is changed during a blur event for a
|
|
274
|
+
* native form control.
|
|
275
|
+
* However, the announcement can be spotty
|
|
276
|
+
* when using a non-native form control
|
|
277
|
+
* and `forceUpdate()`.
|
|
278
|
+
* This is due to `forceUpdate()` internally
|
|
279
|
+
* rescheduling the DOM update to a lower
|
|
280
|
+
* priority queue regardless if it's called
|
|
281
|
+
* inside a Promise or not, thus causing
|
|
282
|
+
* the screen reader to potentially miss the
|
|
283
|
+
* change.
|
|
284
|
+
* By using a State variable inside a Promise,
|
|
285
|
+
* it guarantees a re-render immediately at
|
|
286
|
+
* a higher priority.
|
|
287
|
+
*/
|
|
288
|
+
Promise.resolve().then(() => {
|
|
289
|
+
this.hintTextId = this.getHintTextId();
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
this.validationObserver.observe(this.el, {
|
|
294
|
+
attributes: true,
|
|
295
|
+
attributeFilter: ['class'],
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
// Always set initial state
|
|
299
|
+
this.isInvalid = checkInvalidState(this.el);
|
|
300
|
+
}
|
|
301
|
+
componentWillLoad() {
|
|
302
|
+
this.hintTextId = this.getHintTextId();
|
|
303
|
+
}
|
|
304
|
+
disconnectedCallback() {
|
|
305
|
+
// Clean up validation observer to prevent memory leaks.
|
|
306
|
+
if (this.validationObserver) {
|
|
307
|
+
this.validationObserver.disconnect();
|
|
308
|
+
this.validationObserver = undefined;
|
|
309
|
+
}
|
|
259
310
|
}
|
|
260
311
|
getRadios() {
|
|
261
312
|
return Array.from(this.el.querySelectorAll('ion-radio'));
|
|
@@ -331,16 +382,16 @@ const RadioGroup = class {
|
|
|
331
382
|
* Renders the helper text or error text values
|
|
332
383
|
*/
|
|
333
384
|
renderHintText() {
|
|
334
|
-
const { helperText, errorText, helperTextId, errorTextId } = this;
|
|
385
|
+
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
335
386
|
const hasHintText = !!helperText || !!errorText;
|
|
336
387
|
if (!hasHintText) {
|
|
337
388
|
return;
|
|
338
389
|
}
|
|
339
|
-
return (h("div", { class: "radio-group-top" }, h("div", { id: helperTextId, class: "helper-text" }, helperText), h("div", { id: errorTextId, class: "error-text" }, errorText)));
|
|
390
|
+
return (h("div", { class: "radio-group-top" }, h("div", { id: helperTextId, class: "helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null), h("div", { id: errorTextId, class: "error-text", role: "alert" }, isInvalid ? errorText : null)));
|
|
340
391
|
}
|
|
341
|
-
|
|
342
|
-
const {
|
|
343
|
-
if (
|
|
392
|
+
getHintTextId() {
|
|
393
|
+
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
394
|
+
if (isInvalid && errorText) {
|
|
344
395
|
return errorTextId;
|
|
345
396
|
}
|
|
346
397
|
if (helperText) {
|
|
@@ -352,7 +403,7 @@ const RadioGroup = class {
|
|
|
352
403
|
const { label, labelId, el, name, value } = this;
|
|
353
404
|
const mode = getIonMode(this);
|
|
354
405
|
renderHiddenInput(true, el, name, value, false);
|
|
355
|
-
return (h(Host, { key: '
|
|
406
|
+
return (h(Host, { key: 'db593b3ed511e9395e3c7bfd91b787328692cd6d', role: "radiogroup", "aria-labelledby": label ? labelId : null, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, class: mode }, this.renderHintText(), h("div", { key: '85045b45a0100a45f3b9a35d1c5a25ec63d525c4', class: "radio-group-wrapper" }, h("slot", { key: '53dacb87ce62398e78771fb2efaf839ab922d946' }))));
|
|
356
407
|
}
|
|
357
408
|
get el() { return getElement(this); }
|
|
358
409
|
static get watchers() { return {
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
4
|
import { r as registerInstance, c as createEvent, f as printIonWarning, h, d as Host, g as getElement, i as forceUpdate } from './index-C8IsBmNU.js';
|
|
5
|
-
import { c as createNotchController
|
|
5
|
+
import { c as createNotchController } from './notch-controller-BwelN_JM.js';
|
|
6
6
|
import { i as isOptionSelected, c as compareOptions } from './compare-with-utils-sObYyvOy.js';
|
|
7
|
+
import { c as checkInvalidState } from './validity-DJztqcrH.js';
|
|
7
8
|
import { b as inheritAttributes, a as renderHiddenInput, n as focusVisibleElement } from './helpers-DEn3pfjm.js';
|
|
8
9
|
import { c as popoverController, b as actionSheetController, a as alertController, m as modalController, s as safeCall } from './overlays-BymNv-BL.js';
|
|
9
10
|
import { i as isRTL } from './dir-C53feagD.js';
|
|
@@ -201,7 +202,7 @@ const Select = class {
|
|
|
201
202
|
* a higher priority.
|
|
202
203
|
*/
|
|
203
204
|
Promise.resolve().then(() => {
|
|
204
|
-
this.
|
|
205
|
+
this.hintTextId = this.getHintTextId();
|
|
205
206
|
});
|
|
206
207
|
}
|
|
207
208
|
});
|
|
@@ -215,7 +216,7 @@ const Select = class {
|
|
|
215
216
|
}
|
|
216
217
|
componentWillLoad() {
|
|
217
218
|
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
|
|
218
|
-
this.
|
|
219
|
+
this.hintTextId = this.getHintTextId();
|
|
219
220
|
}
|
|
220
221
|
componentDidLoad() {
|
|
221
222
|
/**
|
|
@@ -714,9 +715,9 @@ const Select = class {
|
|
|
714
715
|
}
|
|
715
716
|
renderListbox() {
|
|
716
717
|
const { disabled, inputId, isExpanded, required } = this;
|
|
717
|
-
return (h("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "dialog", "aria-expanded": `${isExpanded}`, "aria-describedby": this.
|
|
718
|
+
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) }));
|
|
718
719
|
}
|
|
719
|
-
|
|
720
|
+
getHintTextId() {
|
|
720
721
|
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
721
722
|
if (isInvalid && errorText) {
|
|
722
723
|
return errorTextId;
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
4
|
import { r as registerInstance, c as createEvent, i as forceUpdate, w as writeTask, h, d as Host, g as getElement } from './index-C8IsBmNU.js';
|
|
5
|
-
import { c as createNotchController
|
|
5
|
+
import { c as createNotchController } from './notch-controller-BwelN_JM.js';
|
|
6
|
+
import { c as checkInvalidState } from './validity-DJztqcrH.js';
|
|
6
7
|
import { d as debounceEvent, i as inheritAriaAttributes, b as inheritAttributes, c as componentOnReady } from './helpers-DEn3pfjm.js';
|
|
7
8
|
import { c as createSlotMutationController, g as getCounterText } from './input.utils-DrvTa8gz.js';
|
|
8
9
|
import { h as hostContext, c as createColorClasses } from './theme-DiVJyqlX.js';
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { r as registerInstance, c as createEvent, e as config, h, d as Host, g as getElement } from './index-C8IsBmNU.js';
|
|
5
5
|
import { i as inheritAriaAttributes, a as renderHiddenInput } from './helpers-DEn3pfjm.js';
|
|
6
|
+
import { c as checkInvalidState } from './validity-DJztqcrH.js';
|
|
6
7
|
import { c as hapticSelection } from './haptic-DzAMWJuk.js';
|
|
7
8
|
import { a as isPlatform, b as getIonMode } from './ionic-global-CDrldh-5.js';
|
|
8
9
|
import { i as isRTL } from './dir-C53feagD.js';
|
|
@@ -29,6 +30,10 @@ const Toggle = class {
|
|
|
29
30
|
this.inheritedAttributes = {};
|
|
30
31
|
this.didLoad = false;
|
|
31
32
|
this.activated = false;
|
|
33
|
+
/**
|
|
34
|
+
* Track validation state for proper aria-live announcements.
|
|
35
|
+
*/
|
|
36
|
+
this.isInvalid = false;
|
|
32
37
|
/**
|
|
33
38
|
* The name of the control, which is submitted with the form data.
|
|
34
39
|
*/
|
|
@@ -142,15 +147,52 @@ const Toggle = class {
|
|
|
142
147
|
});
|
|
143
148
|
}
|
|
144
149
|
async connectedCallback() {
|
|
150
|
+
const { didLoad, el } = this;
|
|
145
151
|
/**
|
|
146
152
|
* If we have not yet rendered
|
|
147
153
|
* ion-toggle, then toggleTrack is not defined.
|
|
148
154
|
* But if we are moving ion-toggle via appendChild,
|
|
149
155
|
* then toggleTrack will be defined.
|
|
150
156
|
*/
|
|
151
|
-
if (
|
|
157
|
+
if (didLoad) {
|
|
152
158
|
this.setupGesture();
|
|
153
159
|
}
|
|
160
|
+
// Watch for class changes to update validation state.
|
|
161
|
+
if (typeof MutationObserver !== 'undefined') {
|
|
162
|
+
this.validationObserver = new MutationObserver(() => {
|
|
163
|
+
const newIsInvalid = checkInvalidState(el);
|
|
164
|
+
if (this.isInvalid !== newIsInvalid) {
|
|
165
|
+
this.isInvalid = newIsInvalid;
|
|
166
|
+
/**
|
|
167
|
+
* Screen readers tend to announce changes
|
|
168
|
+
* to `aria-describedby` when the attribute
|
|
169
|
+
* is changed during a blur event for a
|
|
170
|
+
* native form control.
|
|
171
|
+
* However, the announcement can be spotty
|
|
172
|
+
* when using a non-native form control
|
|
173
|
+
* and `forceUpdate()`.
|
|
174
|
+
* This is due to `forceUpdate()` internally
|
|
175
|
+
* rescheduling the DOM update to a lower
|
|
176
|
+
* priority queue regardless if it's called
|
|
177
|
+
* inside a Promise or not, thus causing
|
|
178
|
+
* the screen reader to potentially miss the
|
|
179
|
+
* change.
|
|
180
|
+
* By using a State variable inside a Promise,
|
|
181
|
+
* it guarantees a re-render immediately at
|
|
182
|
+
* a higher priority.
|
|
183
|
+
*/
|
|
184
|
+
Promise.resolve().then(() => {
|
|
185
|
+
this.hintTextId = this.getHintTextId();
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
this.validationObserver.observe(el, {
|
|
190
|
+
attributes: true,
|
|
191
|
+
attributeFilter: ['class'],
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
// Always set initial state
|
|
195
|
+
this.isInvalid = checkInvalidState(el);
|
|
154
196
|
}
|
|
155
197
|
componentDidLoad() {
|
|
156
198
|
this.setupGesture();
|
|
@@ -161,9 +203,15 @@ const Toggle = class {
|
|
|
161
203
|
this.gesture.destroy();
|
|
162
204
|
this.gesture = undefined;
|
|
163
205
|
}
|
|
206
|
+
// Clean up validation observer to prevent memory leaks.
|
|
207
|
+
if (this.validationObserver) {
|
|
208
|
+
this.validationObserver.disconnect();
|
|
209
|
+
this.validationObserver = undefined;
|
|
210
|
+
}
|
|
164
211
|
}
|
|
165
212
|
componentWillLoad() {
|
|
166
213
|
this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
|
|
214
|
+
this.hintTextId = this.getHintTextId();
|
|
167
215
|
}
|
|
168
216
|
onStart() {
|
|
169
217
|
this.activated = true;
|
|
@@ -204,9 +252,9 @@ const Toggle = class {
|
|
|
204
252
|
get hasLabel() {
|
|
205
253
|
return this.el.textContent !== '';
|
|
206
254
|
}
|
|
207
|
-
|
|
208
|
-
const {
|
|
209
|
-
if (
|
|
255
|
+
getHintTextId() {
|
|
256
|
+
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
257
|
+
if (isInvalid && errorText) {
|
|
210
258
|
return errorTextId;
|
|
211
259
|
}
|
|
212
260
|
if (helperText) {
|
|
@@ -219,7 +267,7 @@ const Toggle = class {
|
|
|
219
267
|
* This element should only be rendered if hint text is set.
|
|
220
268
|
*/
|
|
221
269
|
renderHintText() {
|
|
222
|
-
const { helperText, errorText, helperTextId, errorTextId } = this;
|
|
270
|
+
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
223
271
|
/**
|
|
224
272
|
* undefined and empty string values should
|
|
225
273
|
* be treated as not having helper/error text.
|
|
@@ -228,15 +276,15 @@ const Toggle = class {
|
|
|
228
276
|
if (!hasHintText) {
|
|
229
277
|
return;
|
|
230
278
|
}
|
|
231
|
-
return (h("div", { class: "toggle-bottom" }, h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text" }, helperText), h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text" }, errorText)));
|
|
279
|
+
return (h("div", { class: "toggle-bottom" }, h("div", { id: helperTextId, class: "helper-text", part: "supporting-text helper-text", "aria-live": "polite" }, !isInvalid ? helperText : null), h("div", { id: errorTextId, class: "error-text", part: "supporting-text error-text", role: "alert" }, isInvalid ? errorText : null)));
|
|
232
280
|
}
|
|
233
281
|
render() {
|
|
234
|
-
const { activated, alignment, checked, color, disabled, el,
|
|
282
|
+
const { activated, alignment, checked, color, disabled, el, hasLabel, inheritedAttributes, inputId, inputLabelId, justify, labelPlacement, name, required, } = this;
|
|
235
283
|
const mode = getIonMode(this);
|
|
236
284
|
const value = this.getValue();
|
|
237
285
|
const rtl = isRTL(el) ? 'rtl' : 'ltr';
|
|
238
286
|
renderHiddenInput(true, el, name, checked ? value : '', disabled);
|
|
239
|
-
return (h(Host, { key: '
|
|
287
|
+
return (h(Host, { key: 'f569148edd89ee041a4719ffc4733c16b05229bd', role: "switch", "aria-checked": `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, onClick: this.onClick, "aria-labelledby": hasLabel ? inputLabelId : null, "aria-label": inheritedAttributes['aria-label'] || null, "aria-disabled": disabled ? 'true' : null, "aria-required": required ? 'true' : undefined, tabindex: disabled ? undefined : 0, onKeyDown: this.onKeyDown, onFocus: this.onFocus, onBlur: this.onBlur, class: createColorClasses(color, {
|
|
240
288
|
[mode]: true,
|
|
241
289
|
'in-item': hostContext('ion-item', el),
|
|
242
290
|
'toggle-activated': activated,
|
|
@@ -246,10 +294,10 @@ const Toggle = class {
|
|
|
246
294
|
[`toggle-alignment-${alignment}`]: alignment !== undefined,
|
|
247
295
|
[`toggle-label-placement-${labelPlacement}`]: true,
|
|
248
296
|
[`toggle-${rtl}`]: true,
|
|
249
|
-
}) }, h("label", { key: '
|
|
297
|
+
}) }, h("label", { key: '3027f2ac4be6de422a14486d847fbee77f615db1', class: "toggle-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: '4b0304c9e879e432b80184b4e5de37d55c11b436', type: "checkbox", role: "switch", "aria-checked": `${checked}`, checked: checked, disabled: disabled, id: inputId, required: required }, inheritedAttributes)), h("div", { key: '8ef265ec942e7f01ff31cbb202ed146c6bf94e02', class: {
|
|
250
298
|
'label-text-wrapper': true,
|
|
251
299
|
'label-text-wrapper-hidden': !hasLabel,
|
|
252
|
-
}, part: "label", id: inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '
|
|
300
|
+
}, part: "label", id: inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '7b162b7dd27199cca2a4c995276a18b9f8e44aaf' }), this.renderHintText()), h("div", { key: 'd13c34bd42fca01cc73ddb4ea7e471b33a282a3e', class: "native-wrapper" }, this.renderToggleControl()))));
|
|
253
301
|
}
|
|
254
302
|
get el() { return getElement(this); }
|
|
255
303
|
static get watchers() { return {
|