@ionic/core 8.7.17-nightly.20260114 → 8.7.18-nightly.20260115
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/ion-footer.js +23 -3
- package/components/ion-input.js +35 -6
- package/components/ion-tab-bar.js +23 -3
- package/dist/cjs/ion-app_8.cjs.entry.js +23 -3
- package/dist/cjs/ion-input.cjs.entry.js +35 -6
- package/dist/cjs/ion-tab-bar_2.cjs.entry.js +23 -3
- package/dist/collection/components/footer/footer.js +23 -3
- package/dist/collection/components/input/input.js +35 -6
- package/dist/collection/components/tab-bar/tab-bar.js +23 -3
- package/dist/docs.json +1 -1
- package/dist/esm/ion-app_8.entry.js +23 -3
- package/dist/esm/ion-input.entry.js +35 -6
- package/dist/esm/ion-tab-bar_2.entry.js +23 -3
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-172a579f.entry.js +4 -0
- package/dist/ionic/p-1ccd6829.entry.js +4 -0
- package/dist/ionic/p-66fbe052.entry.js +4 -0
- package/dist/types/components/footer/footer.d.ts +1 -0
- package/dist/types/components/input/input.d.ts +11 -0
- package/dist/types/components/tab-bar/tab-bar.d.ts +1 -0
- package/hydrate/index.js +80 -12
- package/hydrate/index.mjs +80 -12
- package/package.json +1 -1
- package/dist/ionic/p-7268efa5.entry.js +0 -4
- package/dist/ionic/p-9ab2d871.entry.js +0 -4
- package/dist/ionic/p-db8027bd.entry.js +0 -4
package/components/ion-footer.js
CHANGED
|
@@ -47,6 +47,7 @@ const Footer = /*@__PURE__*/ proxyCustomElement(class Footer extends HTMLElement
|
|
|
47
47
|
this.__registerHost();
|
|
48
48
|
}
|
|
49
49
|
this.keyboardCtrl = null;
|
|
50
|
+
this.keyboardCtrlPromise = null;
|
|
50
51
|
this.keyboardVisible = false;
|
|
51
52
|
/**
|
|
52
53
|
* If `true`, the footer will be translucent.
|
|
@@ -94,7 +95,7 @@ const Footer = /*@__PURE__*/ proxyCustomElement(class Footer extends HTMLElement
|
|
|
94
95
|
this.checkCollapsibleFooter();
|
|
95
96
|
}
|
|
96
97
|
async connectedCallback() {
|
|
97
|
-
|
|
98
|
+
const promise = createKeyboardController(async (keyboardOpen, waitForResize) => {
|
|
98
99
|
/**
|
|
99
100
|
* If the keyboard is hiding, then we need to wait
|
|
100
101
|
* for the webview to resize. Otherwise, the footer
|
|
@@ -105,10 +106,29 @@ const Footer = /*@__PURE__*/ proxyCustomElement(class Footer extends HTMLElement
|
|
|
105
106
|
}
|
|
106
107
|
this.keyboardVisible = keyboardOpen; // trigger re-render by updating state
|
|
107
108
|
});
|
|
109
|
+
this.keyboardCtrlPromise = promise;
|
|
110
|
+
const keyboardCtrl = await promise;
|
|
111
|
+
/**
|
|
112
|
+
* Only assign if this is still the current promise.
|
|
113
|
+
* Otherwise, a new connectedCallback has started or
|
|
114
|
+
* disconnectedCallback was called, so destroy this instance.
|
|
115
|
+
*/
|
|
116
|
+
if (this.keyboardCtrlPromise === promise) {
|
|
117
|
+
this.keyboardCtrl = keyboardCtrl;
|
|
118
|
+
this.keyboardCtrlPromise = null;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
keyboardCtrl.destroy();
|
|
122
|
+
}
|
|
108
123
|
}
|
|
109
124
|
disconnectedCallback() {
|
|
125
|
+
if (this.keyboardCtrlPromise) {
|
|
126
|
+
this.keyboardCtrlPromise.then((ctrl) => ctrl.destroy());
|
|
127
|
+
this.keyboardCtrlPromise = null;
|
|
128
|
+
}
|
|
110
129
|
if (this.keyboardCtrl) {
|
|
111
130
|
this.keyboardCtrl.destroy();
|
|
131
|
+
this.keyboardCtrl = null;
|
|
112
132
|
}
|
|
113
133
|
}
|
|
114
134
|
destroyCollapsibleFooter() {
|
|
@@ -122,7 +142,7 @@ const Footer = /*@__PURE__*/ proxyCustomElement(class Footer extends HTMLElement
|
|
|
122
142
|
const mode = getIonMode(this);
|
|
123
143
|
const tabs = this.el.closest('ion-tabs');
|
|
124
144
|
const tabBar = tabs === null || tabs === void 0 ? void 0 : tabs.querySelector(':scope > ion-tab-bar');
|
|
125
|
-
return (h(Host, { key: '
|
|
145
|
+
return (h(Host, { key: '71939c4bbaef5062532a99ee2e33574102a9abad', role: "contentinfo", class: {
|
|
126
146
|
[mode]: true,
|
|
127
147
|
// Used internally for styling
|
|
128
148
|
[`footer-${mode}`]: true,
|
|
@@ -130,7 +150,7 @@ const Footer = /*@__PURE__*/ proxyCustomElement(class Footer extends HTMLElement
|
|
|
130
150
|
[`footer-translucent-${mode}`]: translucent,
|
|
131
151
|
['footer-toolbar-padding']: !this.keyboardVisible && (!tabBar || tabBar.slot !== 'bottom'),
|
|
132
152
|
[`footer-collapse-${collapse}`]: collapse !== undefined,
|
|
133
|
-
} }, mode === 'ios' && translucent && h("div", { key: '
|
|
153
|
+
} }, mode === 'ios' && translucent && h("div", { key: '2fa14f61661c47c661cecd696176728d6eafa74f', class: "footer-background" }), h("slot", { key: '8e63696e7c528d5c38201e546bf08135290d0945' })));
|
|
134
154
|
}
|
|
135
155
|
get el() { return this; }
|
|
136
156
|
static get style() { return {
|
package/components/ion-input.js
CHANGED
|
@@ -28,6 +28,7 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
28
28
|
this.inputId = `ion-input-${inputIds++}`;
|
|
29
29
|
this.helperTextId = `${this.inputId}-helper-text`;
|
|
30
30
|
this.errorTextId = `${this.inputId}-error-text`;
|
|
31
|
+
this.labelTextId = `${this.inputId}-label`;
|
|
31
32
|
this.inheritedAttributes = {};
|
|
32
33
|
this.isComposing = false;
|
|
33
34
|
/**
|
|
@@ -236,7 +237,11 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
236
237
|
}
|
|
237
238
|
connectedCallback() {
|
|
238
239
|
const { el } = this;
|
|
239
|
-
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () =>
|
|
240
|
+
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => {
|
|
241
|
+
this.setSlottedLabelId();
|
|
242
|
+
forceUpdate(this);
|
|
243
|
+
});
|
|
244
|
+
this.setSlottedLabelId();
|
|
240
245
|
this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
|
|
241
246
|
// Watch for class changes to update validation state
|
|
242
247
|
if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
|
|
@@ -439,11 +444,11 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
439
444
|
return (h("div", { class: "input-bottom" }, this.renderHintText(), this.renderCounter()));
|
|
440
445
|
}
|
|
441
446
|
renderLabel() {
|
|
442
|
-
const { label } = this;
|
|
447
|
+
const { label, labelTextId } = this;
|
|
443
448
|
return (h("div", { class: {
|
|
444
449
|
'label-text-wrapper': true,
|
|
445
450
|
'label-text-wrapper-hidden': !this.hasLabel,
|
|
446
|
-
} }, label === undefined ? h("slot", { name: "label" }) : h("div", { class: "label-text" }, label)));
|
|
451
|
+
}, "aria-hidden": this.hasLabel ? 'true' : null }, label === undefined ? (h("slot", { name: "label" })) : (h("div", { class: "label-text", id: labelTextId }, label))));
|
|
447
452
|
}
|
|
448
453
|
/**
|
|
449
454
|
* Gets any content passed into the `label` slot,
|
|
@@ -452,6 +457,30 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
452
457
|
get labelSlot() {
|
|
453
458
|
return this.el.querySelector('[slot="label"]');
|
|
454
459
|
}
|
|
460
|
+
/**
|
|
461
|
+
* Ensures the slotted label element has an ID for aria-labelledby.
|
|
462
|
+
* If no ID exists, we assign one using our generated labelTextId.
|
|
463
|
+
*/
|
|
464
|
+
setSlottedLabelId() {
|
|
465
|
+
const slottedLabel = this.labelSlot;
|
|
466
|
+
if (slottedLabel && !slottedLabel.id) {
|
|
467
|
+
slottedLabel.id = this.labelTextId;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Returns the ID to use for aria-labelledby on the native input,
|
|
472
|
+
* or undefined if aria-label is explicitly set (to avoid conflicts).
|
|
473
|
+
*/
|
|
474
|
+
getLabelledById() {
|
|
475
|
+
var _a;
|
|
476
|
+
if (this.inheritedAttributes['aria-label']) {
|
|
477
|
+
return undefined;
|
|
478
|
+
}
|
|
479
|
+
if (this.label !== undefined) {
|
|
480
|
+
return this.labelTextId;
|
|
481
|
+
}
|
|
482
|
+
return ((_a = this.labelSlot) === null || _a === void 0 ? void 0 : _a.id) || undefined;
|
|
483
|
+
}
|
|
455
484
|
/**
|
|
456
485
|
* Returns `true` if label content is provided
|
|
457
486
|
* either by a prop or a content. If you want
|
|
@@ -518,7 +547,7 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
518
547
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
519
548
|
*/
|
|
520
549
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
|
521
|
-
return (h(Host, { key: '
|
|
550
|
+
return (h(Host, { key: '9ba9cf425b573d2ca9ac34455a0e6b8474c4de6d', class: createColorClasses(this.color, {
|
|
522
551
|
[mode]: true,
|
|
523
552
|
'has-value': hasValue,
|
|
524
553
|
'has-focus': hasFocus,
|
|
@@ -529,14 +558,14 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
|
|
|
529
558
|
'in-item': inItem,
|
|
530
559
|
'in-item-color': hostContext('ion-item.ion-color', this.el),
|
|
531
560
|
'input-disabled': disabled,
|
|
532
|
-
}) }, h("label", { key: '
|
|
561
|
+
}) }, h("label", { key: '74b989d0aa5ab38f29f952519868f05119df6005', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '47f2b42e2f74ea866b4f871026e08ab375d7a726', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: 'eaabe5a4a329a356cac3294d15c087d0d131fff2', name: "start" }), h("input", Object.assign({ key: 'c821a984a8a9b7f96f30892c06d8deda093ff24b', 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, "aria-labelledby": this.getLabelledById() }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: '62069c11016ee190dc46ab941372e1c4ad8a36ed', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
|
|
533
562
|
/**
|
|
534
563
|
* This prevents mobile browsers from
|
|
535
564
|
* blurring the input when the clear
|
|
536
565
|
* button is activated.
|
|
537
566
|
*/
|
|
538
567
|
ev.preventDefault();
|
|
539
|
-
}, onClick: this.clearTextInput }, h("ion-icon", { key: '
|
|
568
|
+
}, onClick: this.clearTextInput }, h("ion-icon", { key: 'dd75a516d32110d85382b664c663bd41f177ce12', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '330d4b9389f2c62223a5ee24003e96ef3e6b2473', name: "end" })), shouldRenderHighlight && h("div", { key: '8e442bed130ddc84976ab70fd3f8578d6bcc6316', class: "input-highlight" })), this.renderBottomContent()));
|
|
540
569
|
}
|
|
541
570
|
get el() { return this; }
|
|
542
571
|
static get watchers() { return {
|
|
@@ -20,6 +20,7 @@ const TabBar = /*@__PURE__*/ proxyCustomElement(class TabBar extends HTMLElement
|
|
|
20
20
|
this.ionTabBarChanged = createEvent(this, "ionTabBarChanged", 7);
|
|
21
21
|
this.ionTabBarLoaded = createEvent(this, "ionTabBarLoaded", 7);
|
|
22
22
|
this.keyboardCtrl = null;
|
|
23
|
+
this.keyboardCtrlPromise = null;
|
|
23
24
|
this.didLoad = false;
|
|
24
25
|
this.keyboardVisible = false;
|
|
25
26
|
/**
|
|
@@ -55,7 +56,7 @@ const TabBar = /*@__PURE__*/ proxyCustomElement(class TabBar extends HTMLElement
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
async connectedCallback() {
|
|
58
|
-
|
|
59
|
+
const promise = createKeyboardController(async (keyboardOpen, waitForResize) => {
|
|
59
60
|
/**
|
|
60
61
|
* If the keyboard is hiding, then we need to wait
|
|
61
62
|
* for the webview to resize. Otherwise, the tab bar
|
|
@@ -66,21 +67,40 @@ const TabBar = /*@__PURE__*/ proxyCustomElement(class TabBar extends HTMLElement
|
|
|
66
67
|
}
|
|
67
68
|
this.keyboardVisible = keyboardOpen; // trigger re-render by updating state
|
|
68
69
|
});
|
|
70
|
+
this.keyboardCtrlPromise = promise;
|
|
71
|
+
const keyboardCtrl = await promise;
|
|
72
|
+
/**
|
|
73
|
+
* Only assign if this is still the current promise.
|
|
74
|
+
* Otherwise, a new connectedCallback has started or
|
|
75
|
+
* disconnectedCallback was called, so destroy this instance.
|
|
76
|
+
*/
|
|
77
|
+
if (this.keyboardCtrlPromise === promise) {
|
|
78
|
+
this.keyboardCtrl = keyboardCtrl;
|
|
79
|
+
this.keyboardCtrlPromise = null;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
keyboardCtrl.destroy();
|
|
83
|
+
}
|
|
69
84
|
}
|
|
70
85
|
disconnectedCallback() {
|
|
86
|
+
if (this.keyboardCtrlPromise) {
|
|
87
|
+
this.keyboardCtrlPromise.then((ctrl) => ctrl.destroy());
|
|
88
|
+
this.keyboardCtrlPromise = null;
|
|
89
|
+
}
|
|
71
90
|
if (this.keyboardCtrl) {
|
|
72
91
|
this.keyboardCtrl.destroy();
|
|
92
|
+
this.keyboardCtrl = null;
|
|
73
93
|
}
|
|
74
94
|
}
|
|
75
95
|
render() {
|
|
76
96
|
const { color, translucent, keyboardVisible } = this;
|
|
77
97
|
const mode = getIonMode(this);
|
|
78
98
|
const shouldHide = keyboardVisible && this.el.getAttribute('slot') !== 'top';
|
|
79
|
-
return (h(Host, { key: '
|
|
99
|
+
return (h(Host, { key: '9daf4e2acaff6e3ce3878cf9dd5109fb1afbbebe', role: "tablist", "aria-hidden": shouldHide ? 'true' : null, class: createColorClasses(color, {
|
|
80
100
|
[mode]: true,
|
|
81
101
|
'tab-bar-translucent': translucent,
|
|
82
102
|
'tab-bar-hidden': shouldHide,
|
|
83
|
-
}) }, h("slot", { key: '
|
|
103
|
+
}) }, h("slot", { key: '1d15aa2da8501e8e7eff11ad4a491478be845c43' })));
|
|
84
104
|
}
|
|
85
105
|
get el() { return this; }
|
|
86
106
|
static get watchers() { return {
|
|
@@ -626,6 +626,7 @@ const Footer = class {
|
|
|
626
626
|
constructor(hostRef) {
|
|
627
627
|
index.registerInstance(this, hostRef);
|
|
628
628
|
this.keyboardCtrl = null;
|
|
629
|
+
this.keyboardCtrlPromise = null;
|
|
629
630
|
this.keyboardVisible = false;
|
|
630
631
|
/**
|
|
631
632
|
* If `true`, the footer will be translucent.
|
|
@@ -673,7 +674,7 @@ const Footer = class {
|
|
|
673
674
|
this.checkCollapsibleFooter();
|
|
674
675
|
}
|
|
675
676
|
async connectedCallback() {
|
|
676
|
-
|
|
677
|
+
const promise = keyboardController.createKeyboardController(async (keyboardOpen, waitForResize) => {
|
|
677
678
|
/**
|
|
678
679
|
* If the keyboard is hiding, then we need to wait
|
|
679
680
|
* for the webview to resize. Otherwise, the footer
|
|
@@ -684,10 +685,29 @@ const Footer = class {
|
|
|
684
685
|
}
|
|
685
686
|
this.keyboardVisible = keyboardOpen; // trigger re-render by updating state
|
|
686
687
|
});
|
|
688
|
+
this.keyboardCtrlPromise = promise;
|
|
689
|
+
const keyboardCtrl = await promise;
|
|
690
|
+
/**
|
|
691
|
+
* Only assign if this is still the current promise.
|
|
692
|
+
* Otherwise, a new connectedCallback has started or
|
|
693
|
+
* disconnectedCallback was called, so destroy this instance.
|
|
694
|
+
*/
|
|
695
|
+
if (this.keyboardCtrlPromise === promise) {
|
|
696
|
+
this.keyboardCtrl = keyboardCtrl;
|
|
697
|
+
this.keyboardCtrlPromise = null;
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
keyboardCtrl.destroy();
|
|
701
|
+
}
|
|
687
702
|
}
|
|
688
703
|
disconnectedCallback() {
|
|
704
|
+
if (this.keyboardCtrlPromise) {
|
|
705
|
+
this.keyboardCtrlPromise.then((ctrl) => ctrl.destroy());
|
|
706
|
+
this.keyboardCtrlPromise = null;
|
|
707
|
+
}
|
|
689
708
|
if (this.keyboardCtrl) {
|
|
690
709
|
this.keyboardCtrl.destroy();
|
|
710
|
+
this.keyboardCtrl = null;
|
|
691
711
|
}
|
|
692
712
|
}
|
|
693
713
|
destroyCollapsibleFooter() {
|
|
@@ -701,7 +721,7 @@ const Footer = class {
|
|
|
701
721
|
const mode = ionicGlobal.getIonMode(this);
|
|
702
722
|
const tabs = this.el.closest('ion-tabs');
|
|
703
723
|
const tabBar = tabs === null || tabs === void 0 ? void 0 : tabs.querySelector(':scope > ion-tab-bar');
|
|
704
|
-
return (index.h(index.Host, { key: '
|
|
724
|
+
return (index.h(index.Host, { key: '71939c4bbaef5062532a99ee2e33574102a9abad', role: "contentinfo", class: {
|
|
705
725
|
[mode]: true,
|
|
706
726
|
// Used internally for styling
|
|
707
727
|
[`footer-${mode}`]: true,
|
|
@@ -709,7 +729,7 @@ const Footer = class {
|
|
|
709
729
|
[`footer-translucent-${mode}`]: translucent,
|
|
710
730
|
['footer-toolbar-padding']: !this.keyboardVisible && (!tabBar || tabBar.slot !== 'bottom'),
|
|
711
731
|
[`footer-collapse-${collapse}`]: collapse !== undefined,
|
|
712
|
-
} }, mode === 'ios' && translucent && index.h("div", { key: '
|
|
732
|
+
} }, mode === 'ios' && translucent && index.h("div", { key: '2fa14f61661c47c661cecd696176728d6eafa74f', class: "footer-background" }), index.h("slot", { key: '8e63696e7c528d5c38201e546bf08135290d0945' })));
|
|
713
733
|
}
|
|
714
734
|
get el() { return index.getElement(this); }
|
|
715
735
|
};
|
|
@@ -27,6 +27,7 @@ const Input = class {
|
|
|
27
27
|
this.inputId = `ion-input-${inputIds++}`;
|
|
28
28
|
this.helperTextId = `${this.inputId}-helper-text`;
|
|
29
29
|
this.errorTextId = `${this.inputId}-error-text`;
|
|
30
|
+
this.labelTextId = `${this.inputId}-label`;
|
|
30
31
|
this.inheritedAttributes = {};
|
|
31
32
|
this.isComposing = false;
|
|
32
33
|
/**
|
|
@@ -235,7 +236,11 @@ const Input = class {
|
|
|
235
236
|
}
|
|
236
237
|
connectedCallback() {
|
|
237
238
|
const { el } = this;
|
|
238
|
-
this.slotMutationController = input_utils.createSlotMutationController(el, ['label', 'start', 'end'], () =>
|
|
239
|
+
this.slotMutationController = input_utils.createSlotMutationController(el, ['label', 'start', 'end'], () => {
|
|
240
|
+
this.setSlottedLabelId();
|
|
241
|
+
index.forceUpdate(this);
|
|
242
|
+
});
|
|
243
|
+
this.setSlottedLabelId();
|
|
239
244
|
this.notchController = notchController.createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
|
|
240
245
|
// Watch for class changes to update validation state
|
|
241
246
|
if (typeof MutationObserver !== 'undefined') {
|
|
@@ -438,11 +443,11 @@ const Input = class {
|
|
|
438
443
|
return (index.h("div", { class: "input-bottom" }, this.renderHintText(), this.renderCounter()));
|
|
439
444
|
}
|
|
440
445
|
renderLabel() {
|
|
441
|
-
const { label } = this;
|
|
446
|
+
const { label, labelTextId } = this;
|
|
442
447
|
return (index.h("div", { class: {
|
|
443
448
|
'label-text-wrapper': true,
|
|
444
449
|
'label-text-wrapper-hidden': !this.hasLabel,
|
|
445
|
-
} }, label === undefined ? index.h("slot", { name: "label" }) : index.h("div", { class: "label-text" }, label)));
|
|
450
|
+
}, "aria-hidden": this.hasLabel ? 'true' : null }, label === undefined ? (index.h("slot", { name: "label" })) : (index.h("div", { class: "label-text", id: labelTextId }, label))));
|
|
446
451
|
}
|
|
447
452
|
/**
|
|
448
453
|
* Gets any content passed into the `label` slot,
|
|
@@ -451,6 +456,30 @@ const Input = class {
|
|
|
451
456
|
get labelSlot() {
|
|
452
457
|
return this.el.querySelector('[slot="label"]');
|
|
453
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* Ensures the slotted label element has an ID for aria-labelledby.
|
|
461
|
+
* If no ID exists, we assign one using our generated labelTextId.
|
|
462
|
+
*/
|
|
463
|
+
setSlottedLabelId() {
|
|
464
|
+
const slottedLabel = this.labelSlot;
|
|
465
|
+
if (slottedLabel && !slottedLabel.id) {
|
|
466
|
+
slottedLabel.id = this.labelTextId;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Returns the ID to use for aria-labelledby on the native input,
|
|
471
|
+
* or undefined if aria-label is explicitly set (to avoid conflicts).
|
|
472
|
+
*/
|
|
473
|
+
getLabelledById() {
|
|
474
|
+
var _a;
|
|
475
|
+
if (this.inheritedAttributes['aria-label']) {
|
|
476
|
+
return undefined;
|
|
477
|
+
}
|
|
478
|
+
if (this.label !== undefined) {
|
|
479
|
+
return this.labelTextId;
|
|
480
|
+
}
|
|
481
|
+
return ((_a = this.labelSlot) === null || _a === void 0 ? void 0 : _a.id) || undefined;
|
|
482
|
+
}
|
|
454
483
|
/**
|
|
455
484
|
* Returns `true` if label content is provided
|
|
456
485
|
* either by a prop or a content. If you want
|
|
@@ -517,7 +546,7 @@ const Input = class {
|
|
|
517
546
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
518
547
|
*/
|
|
519
548
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
|
520
|
-
return (index.h(index.Host, { key: '
|
|
549
|
+
return (index.h(index.Host, { key: '9ba9cf425b573d2ca9ac34455a0e6b8474c4de6d', class: theme.createColorClasses(this.color, {
|
|
521
550
|
[mode]: true,
|
|
522
551
|
'has-value': hasValue,
|
|
523
552
|
'has-focus': hasFocus,
|
|
@@ -528,14 +557,14 @@ const Input = class {
|
|
|
528
557
|
'in-item': inItem,
|
|
529
558
|
'in-item-color': theme.hostContext('ion-item.ion-color', this.el),
|
|
530
559
|
'input-disabled': disabled,
|
|
531
|
-
}) }, index.h("label", { key: '
|
|
560
|
+
}) }, index.h("label", { key: '74b989d0aa5ab38f29f952519868f05119df6005', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), index.h("div", { key: '47f2b42e2f74ea866b4f871026e08ab375d7a726', class: "native-wrapper", onClick: this.onLabelClick }, index.h("slot", { key: 'eaabe5a4a329a356cac3294d15c087d0d131fff2', name: "start" }), index.h("input", Object.assign({ key: 'c821a984a8a9b7f96f30892c06d8deda093ff24b', 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, "aria-labelledby": this.getLabelledById() }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (index.h("button", { key: '62069c11016ee190dc46ab941372e1c4ad8a36ed', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
|
|
532
561
|
/**
|
|
533
562
|
* This prevents mobile browsers from
|
|
534
563
|
* blurring the input when the clear
|
|
535
564
|
* button is activated.
|
|
536
565
|
*/
|
|
537
566
|
ev.preventDefault();
|
|
538
|
-
}, onClick: this.clearTextInput }, index.h("ion-icon", { key: '
|
|
567
|
+
}, onClick: this.clearTextInput }, index.h("ion-icon", { key: 'dd75a516d32110d85382b664c663bd41f177ce12', "aria-hidden": "true", icon: clearIconData }))), index.h("slot", { key: '330d4b9389f2c62223a5ee24003e96ef3e6b2473', name: "end" })), shouldRenderHighlight && index.h("div", { key: '8e442bed130ddc84976ab70fd3f8578d6bcc6316', class: "input-highlight" })), this.renderBottomContent()));
|
|
539
568
|
}
|
|
540
569
|
get el() { return index.getElement(this); }
|
|
541
570
|
static get watchers() { return {
|
|
@@ -22,6 +22,7 @@ const TabBar = class {
|
|
|
22
22
|
this.ionTabBarChanged = index.createEvent(this, "ionTabBarChanged", 7);
|
|
23
23
|
this.ionTabBarLoaded = index.createEvent(this, "ionTabBarLoaded", 7);
|
|
24
24
|
this.keyboardCtrl = null;
|
|
25
|
+
this.keyboardCtrlPromise = null;
|
|
25
26
|
this.didLoad = false;
|
|
26
27
|
this.keyboardVisible = false;
|
|
27
28
|
/**
|
|
@@ -57,7 +58,7 @@ const TabBar = class {
|
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
async connectedCallback() {
|
|
60
|
-
|
|
61
|
+
const promise = keyboardController.createKeyboardController(async (keyboardOpen, waitForResize) => {
|
|
61
62
|
/**
|
|
62
63
|
* If the keyboard is hiding, then we need to wait
|
|
63
64
|
* for the webview to resize. Otherwise, the tab bar
|
|
@@ -68,21 +69,40 @@ const TabBar = class {
|
|
|
68
69
|
}
|
|
69
70
|
this.keyboardVisible = keyboardOpen; // trigger re-render by updating state
|
|
70
71
|
});
|
|
72
|
+
this.keyboardCtrlPromise = promise;
|
|
73
|
+
const keyboardCtrl = await promise;
|
|
74
|
+
/**
|
|
75
|
+
* Only assign if this is still the current promise.
|
|
76
|
+
* Otherwise, a new connectedCallback has started or
|
|
77
|
+
* disconnectedCallback was called, so destroy this instance.
|
|
78
|
+
*/
|
|
79
|
+
if (this.keyboardCtrlPromise === promise) {
|
|
80
|
+
this.keyboardCtrl = keyboardCtrl;
|
|
81
|
+
this.keyboardCtrlPromise = null;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
keyboardCtrl.destroy();
|
|
85
|
+
}
|
|
71
86
|
}
|
|
72
87
|
disconnectedCallback() {
|
|
88
|
+
if (this.keyboardCtrlPromise) {
|
|
89
|
+
this.keyboardCtrlPromise.then((ctrl) => ctrl.destroy());
|
|
90
|
+
this.keyboardCtrlPromise = null;
|
|
91
|
+
}
|
|
73
92
|
if (this.keyboardCtrl) {
|
|
74
93
|
this.keyboardCtrl.destroy();
|
|
94
|
+
this.keyboardCtrl = null;
|
|
75
95
|
}
|
|
76
96
|
}
|
|
77
97
|
render() {
|
|
78
98
|
const { color, translucent, keyboardVisible } = this;
|
|
79
99
|
const mode = ionicGlobal.getIonMode(this);
|
|
80
100
|
const shouldHide = keyboardVisible && this.el.getAttribute('slot') !== 'top';
|
|
81
|
-
return (index.h(index.Host, { key: '
|
|
101
|
+
return (index.h(index.Host, { key: '9daf4e2acaff6e3ce3878cf9dd5109fb1afbbebe', role: "tablist", "aria-hidden": shouldHide ? 'true' : null, class: theme.createColorClasses(color, {
|
|
82
102
|
[mode]: true,
|
|
83
103
|
'tab-bar-translucent': translucent,
|
|
84
104
|
'tab-bar-hidden': shouldHide,
|
|
85
|
-
}) }, index.h("slot", { key: '
|
|
105
|
+
}) }, index.h("slot", { key: '1d15aa2da8501e8e7eff11ad4a491478be845c43' })));
|
|
86
106
|
}
|
|
87
107
|
get el() { return index.getElement(this); }
|
|
88
108
|
static get watchers() { return {
|
|
@@ -12,6 +12,7 @@ import { handleFooterFade } from "./footer.utils";
|
|
|
12
12
|
export class Footer {
|
|
13
13
|
constructor() {
|
|
14
14
|
this.keyboardCtrl = null;
|
|
15
|
+
this.keyboardCtrlPromise = null;
|
|
15
16
|
this.keyboardVisible = false;
|
|
16
17
|
/**
|
|
17
18
|
* If `true`, the footer will be translucent.
|
|
@@ -59,7 +60,7 @@ export class Footer {
|
|
|
59
60
|
this.checkCollapsibleFooter();
|
|
60
61
|
}
|
|
61
62
|
async connectedCallback() {
|
|
62
|
-
|
|
63
|
+
const promise = createKeyboardController(async (keyboardOpen, waitForResize) => {
|
|
63
64
|
/**
|
|
64
65
|
* If the keyboard is hiding, then we need to wait
|
|
65
66
|
* for the webview to resize. Otherwise, the footer
|
|
@@ -70,10 +71,29 @@ export class Footer {
|
|
|
70
71
|
}
|
|
71
72
|
this.keyboardVisible = keyboardOpen; // trigger re-render by updating state
|
|
72
73
|
});
|
|
74
|
+
this.keyboardCtrlPromise = promise;
|
|
75
|
+
const keyboardCtrl = await promise;
|
|
76
|
+
/**
|
|
77
|
+
* Only assign if this is still the current promise.
|
|
78
|
+
* Otherwise, a new connectedCallback has started or
|
|
79
|
+
* disconnectedCallback was called, so destroy this instance.
|
|
80
|
+
*/
|
|
81
|
+
if (this.keyboardCtrlPromise === promise) {
|
|
82
|
+
this.keyboardCtrl = keyboardCtrl;
|
|
83
|
+
this.keyboardCtrlPromise = null;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
keyboardCtrl.destroy();
|
|
87
|
+
}
|
|
73
88
|
}
|
|
74
89
|
disconnectedCallback() {
|
|
90
|
+
if (this.keyboardCtrlPromise) {
|
|
91
|
+
this.keyboardCtrlPromise.then((ctrl) => ctrl.destroy());
|
|
92
|
+
this.keyboardCtrlPromise = null;
|
|
93
|
+
}
|
|
75
94
|
if (this.keyboardCtrl) {
|
|
76
95
|
this.keyboardCtrl.destroy();
|
|
96
|
+
this.keyboardCtrl = null;
|
|
77
97
|
}
|
|
78
98
|
}
|
|
79
99
|
destroyCollapsibleFooter() {
|
|
@@ -87,7 +107,7 @@ export class Footer {
|
|
|
87
107
|
const mode = getIonMode(this);
|
|
88
108
|
const tabs = this.el.closest('ion-tabs');
|
|
89
109
|
const tabBar = tabs === null || tabs === void 0 ? void 0 : tabs.querySelector(':scope > ion-tab-bar');
|
|
90
|
-
return (h(Host, { key: '
|
|
110
|
+
return (h(Host, { key: '71939c4bbaef5062532a99ee2e33574102a9abad', role: "contentinfo", class: {
|
|
91
111
|
[mode]: true,
|
|
92
112
|
// Used internally for styling
|
|
93
113
|
[`footer-${mode}`]: true,
|
|
@@ -95,7 +115,7 @@ export class Footer {
|
|
|
95
115
|
[`footer-translucent-${mode}`]: translucent,
|
|
96
116
|
['footer-toolbar-padding']: !this.keyboardVisible && (!tabBar || tabBar.slot !== 'bottom'),
|
|
97
117
|
[`footer-collapse-${collapse}`]: collapse !== undefined,
|
|
98
|
-
} }, mode === 'ios' && translucent && h("div", { key: '
|
|
118
|
+
} }, mode === 'ios' && translucent && h("div", { key: '2fa14f61661c47c661cecd696176728d6eafa74f', class: "footer-background" }), h("slot", { key: '8e63696e7c528d5c38201e546bf08135290d0945' })));
|
|
99
119
|
}
|
|
100
120
|
static get is() { return "ion-footer"; }
|
|
101
121
|
static get originalStyleUrls() {
|
|
@@ -21,6 +21,7 @@ export class Input {
|
|
|
21
21
|
this.inputId = `ion-input-${inputIds++}`;
|
|
22
22
|
this.helperTextId = `${this.inputId}-helper-text`;
|
|
23
23
|
this.errorTextId = `${this.inputId}-error-text`;
|
|
24
|
+
this.labelTextId = `${this.inputId}-label`;
|
|
24
25
|
this.inheritedAttributes = {};
|
|
25
26
|
this.isComposing = false;
|
|
26
27
|
/**
|
|
@@ -229,7 +230,11 @@ export class Input {
|
|
|
229
230
|
}
|
|
230
231
|
connectedCallback() {
|
|
231
232
|
const { el } = this;
|
|
232
|
-
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () =>
|
|
233
|
+
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => {
|
|
234
|
+
this.setSlottedLabelId();
|
|
235
|
+
forceUpdate(this);
|
|
236
|
+
});
|
|
237
|
+
this.setSlottedLabelId();
|
|
233
238
|
this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
|
|
234
239
|
// Watch for class changes to update validation state
|
|
235
240
|
if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
|
|
@@ -432,11 +437,11 @@ export class Input {
|
|
|
432
437
|
return (h("div", { class: "input-bottom" }, this.renderHintText(), this.renderCounter()));
|
|
433
438
|
}
|
|
434
439
|
renderLabel() {
|
|
435
|
-
const { label } = this;
|
|
440
|
+
const { label, labelTextId } = this;
|
|
436
441
|
return (h("div", { class: {
|
|
437
442
|
'label-text-wrapper': true,
|
|
438
443
|
'label-text-wrapper-hidden': !this.hasLabel,
|
|
439
|
-
} }, label === undefined ? h("slot", { name: "label" }) : h("div", { class: "label-text" }, label)));
|
|
444
|
+
}, "aria-hidden": this.hasLabel ? 'true' : null }, label === undefined ? (h("slot", { name: "label" })) : (h("div", { class: "label-text", id: labelTextId }, label))));
|
|
440
445
|
}
|
|
441
446
|
/**
|
|
442
447
|
* Gets any content passed into the `label` slot,
|
|
@@ -445,6 +450,30 @@ export class Input {
|
|
|
445
450
|
get labelSlot() {
|
|
446
451
|
return this.el.querySelector('[slot="label"]');
|
|
447
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Ensures the slotted label element has an ID for aria-labelledby.
|
|
455
|
+
* If no ID exists, we assign one using our generated labelTextId.
|
|
456
|
+
*/
|
|
457
|
+
setSlottedLabelId() {
|
|
458
|
+
const slottedLabel = this.labelSlot;
|
|
459
|
+
if (slottedLabel && !slottedLabel.id) {
|
|
460
|
+
slottedLabel.id = this.labelTextId;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Returns the ID to use for aria-labelledby on the native input,
|
|
465
|
+
* or undefined if aria-label is explicitly set (to avoid conflicts).
|
|
466
|
+
*/
|
|
467
|
+
getLabelledById() {
|
|
468
|
+
var _a;
|
|
469
|
+
if (this.inheritedAttributes['aria-label']) {
|
|
470
|
+
return undefined;
|
|
471
|
+
}
|
|
472
|
+
if (this.label !== undefined) {
|
|
473
|
+
return this.labelTextId;
|
|
474
|
+
}
|
|
475
|
+
return ((_a = this.labelSlot) === null || _a === void 0 ? void 0 : _a.id) || undefined;
|
|
476
|
+
}
|
|
448
477
|
/**
|
|
449
478
|
* Returns `true` if label content is provided
|
|
450
479
|
* either by a prop or a content. If you want
|
|
@@ -511,7 +540,7 @@ export class Input {
|
|
|
511
540
|
* TODO(FW-5592): Remove hasStartEndSlots condition
|
|
512
541
|
*/
|
|
513
542
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
|
514
|
-
return (h(Host, { key: '
|
|
543
|
+
return (h(Host, { key: '9ba9cf425b573d2ca9ac34455a0e6b8474c4de6d', class: createColorClasses(this.color, {
|
|
515
544
|
[mode]: true,
|
|
516
545
|
'has-value': hasValue,
|
|
517
546
|
'has-focus': hasFocus,
|
|
@@ -522,14 +551,14 @@ export class Input {
|
|
|
522
551
|
'in-item': inItem,
|
|
523
552
|
'in-item-color': hostContext('ion-item.ion-color', this.el),
|
|
524
553
|
'input-disabled': disabled,
|
|
525
|
-
}) }, h("label", { key: '
|
|
554
|
+
}) }, h("label", { key: '74b989d0aa5ab38f29f952519868f05119df6005', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, this.renderLabelContainer(), h("div", { key: '47f2b42e2f74ea866b4f871026e08ab375d7a726', class: "native-wrapper", onClick: this.onLabelClick }, h("slot", { key: 'eaabe5a4a329a356cac3294d15c087d0d131fff2', name: "start" }), h("input", Object.assign({ key: 'c821a984a8a9b7f96f30892c06d8deda093ff24b', 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, "aria-labelledby": this.getLabelledById() }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: '62069c11016ee190dc46ab941372e1c4ad8a36ed', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
|
|
526
555
|
/**
|
|
527
556
|
* This prevents mobile browsers from
|
|
528
557
|
* blurring the input when the clear
|
|
529
558
|
* button is activated.
|
|
530
559
|
*/
|
|
531
560
|
ev.preventDefault();
|
|
532
|
-
}, onClick: this.clearTextInput }, h("ion-icon", { key: '
|
|
561
|
+
}, onClick: this.clearTextInput }, h("ion-icon", { key: 'dd75a516d32110d85382b664c663bd41f177ce12', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '330d4b9389f2c62223a5ee24003e96ef3e6b2473', name: "end" })), shouldRenderHighlight && h("div", { key: '8e442bed130ddc84976ab70fd3f8578d6bcc6316', class: "input-highlight" })), this.renderBottomContent()));
|
|
533
562
|
}
|
|
534
563
|
static get is() { return "ion-input"; }
|
|
535
564
|
static get encapsulation() { return "scoped"; }
|