voyager-ionic-core 8.7.6 → 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/button.js +3 -7
- package/components/checkbox.js +64 -13
- package/components/header.js +42 -4
- package/components/index2.js +74 -3
- package/components/ion-accordion.js +93 -14
- package/components/ion-datetime.js +35 -2
- package/components/ion-input.js +6 -13
- package/components/ion-select.js +59 -10
- package/components/ion-textarea.js +5 -12
- package/components/ion-toggle.js +63 -16
- package/components/radio-group.js +60 -7
- package/components/validity.js +17 -0
- package/dist/cjs/{index-CD5Rjp23.js → index-094mMFB-.js} +76 -5
- package/dist/cjs/index.cjs.js +3 -3
- package/dist/cjs/ion-accordion_2.cjs.entry.js +91 -13
- package/dist/cjs/ion-app_8.cjs.entry.js +43 -5
- package/dist/cjs/ion-button_2.cjs.entry.js +3 -7
- package/dist/cjs/ion-checkbox.cjs.entry.js +61 -12
- package/dist/cjs/ion-datetime_3.cjs.entry.js +35 -2
- package/dist/cjs/ion-input.cjs.entry.js +6 -13
- package/dist/cjs/ion-modal.cjs.entry.js +1 -1
- package/dist/cjs/ion-nav_2.cjs.entry.js +1 -1
- package/dist/cjs/ion-popover.cjs.entry.js +1 -1
- package/dist/cjs/ion-radio_2.cjs.entry.js +57 -6
- package/dist/cjs/ion-select_3.cjs.entry.js +56 -9
- package/dist/cjs/ion-textarea.cjs.entry.js +5 -12
- package/dist/cjs/ion-toggle.cjs.entry.js +59 -14
- package/dist/cjs/ionic.cjs.js +1 -1
- package/dist/cjs/{ios.transition-j9CclgEW.js → ios.transition-BOt_uW73.js} +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/{md.transition-CwFyRSfv.js → md.transition-Dt968VXB.js} +1 -1
- package/dist/cjs/validity-BpS37YFM.js +19 -0
- package/dist/collection/components/accordion/accordion.js +93 -14
- package/dist/collection/components/button/button.js +3 -7
- package/dist/collection/components/checkbox/checkbox.js +68 -13
- package/dist/collection/components/datetime/datetime.js +35 -2
- package/dist/collection/components/header/header.ios.css +27 -1
- package/dist/collection/components/header/header.js +5 -4
- package/dist/collection/components/header/header.utils.js +37 -0
- package/dist/collection/components/input/input.js +6 -14
- package/dist/collection/components/radio-group/radio-group.js +64 -7
- package/dist/collection/components/select/select.js +60 -12
- package/dist/collection/components/textarea/textarea.js +5 -13
- package/dist/collection/components/toggle/toggle.js +63 -16
- package/dist/collection/utils/forms/index.js +1 -0
- package/dist/collection/utils/forms/validity.js +15 -0
- package/dist/collection/utils/test/playwright/page/utils/set-content.js +7 -0
- package/dist/collection/utils/test/playwright/page/utils/spy-on-event.js +32 -0
- package/dist/collection/utils/transition/index.js +74 -3
- package/dist/docs.json +1 -1
- package/dist/esm/{index-D6G2seR8.js → index-r2D9DEro.js} +76 -5
- package/dist/esm/index.js +3 -3
- package/dist/esm/ion-accordion_2.entry.js +91 -13
- package/dist/esm/ion-app_8.entry.js +43 -5
- package/dist/esm/ion-button_2.entry.js +3 -7
- package/dist/esm/ion-checkbox.entry.js +61 -12
- package/dist/esm/ion-datetime_3.entry.js +35 -2
- package/dist/esm/ion-input.entry.js +6 -13
- package/dist/esm/ion-modal.entry.js +1 -1
- package/dist/esm/ion-nav_2.entry.js +1 -1
- package/dist/esm/ion-popover.entry.js +1 -1
- package/dist/esm/ion-radio_2.entry.js +57 -6
- package/dist/esm/ion-select_3.entry.js +56 -9
- package/dist/esm/ion-textarea.entry.js +5 -12
- package/dist/esm/ion-toggle.entry.js +59 -14
- package/dist/esm/ionic.js +1 -1
- package/dist/esm/{ios.transition-Bpq9ixwv.js → ios.transition-BDzw0_Hm.js} +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/{md.transition-zOA0oanq.js → md.transition-BzDYi3qq.js} +1 -1
- package/dist/esm/validity-DJztqcrH.js +17 -0
- package/dist/ionic/index.esm.js +1 -1
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-40c261a3.entry.js +4 -0
- package/dist/ionic/p-43ed1ef5.entry.js +4 -0
- package/dist/ionic/p-4e41ea20.entry.js +4 -0
- package/dist/ionic/{p-323421af.entry.js → p-5a39a99a.entry.js} +1 -1
- package/dist/ionic/p-5fb517e4.entry.js +4 -0
- package/dist/ionic/p-7380261c.entry.js +4 -0
- package/dist/ionic/{p-9a36e2e7.entry.js → p-95bddd49.entry.js} +1 -1
- package/dist/ionic/{p-DPhQmGJN.js → p-C7hRNDhM.js} +1 -1
- package/dist/ionic/p-DJztqcrH.js +4 -0
- package/dist/ionic/p-DUt5fQmA.js +4 -0
- package/dist/ionic/{p-9R1XyICs.js → p-DZRJwG4S.js} +1 -1
- package/dist/ionic/p-c19f63d0.entry.js +4 -0
- package/dist/ionic/p-cb93126d.entry.js +4 -0
- package/dist/ionic/p-d0a2a1ab.entry.js +4 -0
- package/dist/ionic/p-d1f54e28.entry.js +4 -0
- package/dist/ionic/p-d3014190.entry.js +4 -0
- package/dist/ionic/{p-de7b5fa3.entry.js → p-e16b69e1.entry.js} +1 -1
- package/dist/ionic/svg/checkbox-outline.svg +1 -0
- package/dist/ionic/svg/checkbox-sharp.svg +1 -0
- package/dist/ionic/svg/checkbox.svg +1 -0
- package/dist/ionic/svg/checkmark-circle-outline.svg +1 -0
- package/dist/ionic/svg/checkmark-circle-sharp.svg +1 -0
- package/dist/ionic/svg/checkmark-circle.svg +1 -0
- package/dist/ionic/svg/checkmark-done-circle-outline.svg +1 -0
- package/dist/ionic/svg/checkmark-done-circle-sharp.svg +1 -0
- package/dist/ionic/svg/checkmark-done-circle.svg +1 -0
- package/dist/ionic/svg/checkmark-done-outline.svg +1 -0
- package/dist/ionic/svg/checkmark-done-sharp.svg +1 -0
- package/dist/ionic/svg/checkmark-done.svg +1 -0
- package/dist/ionic/svg/checkmark-outline.svg +1 -0
- package/dist/ionic/svg/checkmark-sharp.svg +1 -0
- package/dist/ionic/svg/checkmark.svg +1 -0
- package/dist/ionic/svg/chevron-back-circle-outline.svg +1 -0
- package/dist/ionic/svg/chevron-back-circle-sharp.svg +1 -0
- package/dist/ionic/svg/chevron-back-circle.svg +1 -0
- package/dist/ionic/svg/chevron-back-outline.svg +1 -0
- package/dist/ionic/svg/chevron-back-sharp.svg +1 -0
- package/dist/ionic/svg/chevron-back.svg +1 -0
- package/dist/ionic/svg/chevron-collapse-outline.svg +1 -0
- package/dist/ionic/svg/chevron-collapse-sharp.svg +1 -0
- package/dist/ionic/svg/chevron-collapse.svg +1 -0
- package/dist/ionic/svg/chevron-down-circle-outline.svg +1 -0
- package/dist/ionic/svg/chevron-down-circle-sharp.svg +1 -0
- package/dist/ionic/svg/chevron-down-circle.svg +1 -0
- package/dist/ionic/svg/chevron-down-outline.svg +1 -0
- package/dist/ionic/svg/chevron-down-sharp.svg +1 -0
- package/dist/ionic/svg/chevron-down.svg +1 -0
- package/dist/ionic/svg/chevron-expand-outline.svg +1 -0
- package/dist/ionic/svg/chevron-expand-sharp.svg +1 -0
- package/dist/ionic/svg/chevron-expand.svg +1 -0
- package/dist/ionic/svg/chevron-forward-circle-outline.svg +1 -0
- package/dist/ionic/svg/chevron-forward-circle-sharp.svg +1 -0
- package/dist/ionic/svg/chevron-forward-circle.svg +1 -0
- package/dist/ionic/svg/chevron-forward-outline.svg +1 -0
- package/dist/ionic/svg/chevron-forward-sharp.svg +1 -0
- package/dist/ionic/svg/chevron-forward.svg +1 -0
- package/dist/ionic/svg/chevron-up-circle-outline.svg +1 -0
- package/dist/ionic/svg/chevron-up-circle-sharp.svg +1 -0
- package/dist/ionic/svg/chevron-up-circle.svg +1 -0
- package/dist/ionic/svg/chevron-up-outline.svg +1 -0
- package/dist/ionic/svg/chevron-up-sharp.svg +1 -0
- package/dist/ionic/svg/chevron-up.svg +1 -0
- package/dist/ionic/svg/clipboard-outline.svg +1 -0
- package/dist/ionic/svg/clipboard-sharp.svg +1 -0
- package/dist/ionic/svg/clipboard.svg +1 -0
- package/dist/ionic/svg/close-circle-outline.svg +1 -0
- package/dist/ionic/svg/close-circle-sharp.svg +1 -0
- package/dist/ionic/svg/close-circle.svg +1 -0
- package/dist/ionic/svg/close-outline.svg +1 -0
- package/dist/ionic/svg/close-sharp.svg +1 -0
- package/dist/ionic/svg/close.svg +1 -0
- package/dist/ionic/svg/cloud-circle-outline.svg +1 -0
- package/dist/ionic/svg/cloud-circle-sharp.svg +1 -0
- package/dist/ionic/svg/cloud-circle.svg +1 -0
- package/dist/ionic/svg/cloud-done-outline.svg +1 -0
- package/dist/ionic/svg/cloud-done-sharp.svg +1 -0
- package/dist/ionic/svg/cloud-done.svg +1 -0
- package/dist/ionic/svg/cloud-download-outline.svg +1 -0
- package/dist/ionic/svg/cloud-download-sharp.svg +1 -0
- package/dist/ionic/svg/cloud-download.svg +1 -0
- package/dist/ionic/svg/cloud-offline-outline.svg +1 -0
- package/dist/ionic/svg/cloud-offline-sharp.svg +1 -0
- package/dist/ionic/svg/cloud-offline.svg +1 -0
- package/dist/ionic/svg/cloud-outline.svg +1 -0
- package/dist/ionic/svg/cloud-sharp.svg +1 -0
- package/dist/ionic/svg/cloud-upload-outline.svg +1 -0
- package/dist/ionic/svg/cloud-upload-sharp.svg +1 -0
- package/dist/ionic/svg/cloud-upload.svg +1 -0
- package/dist/ionic/svg/cloud.svg +1 -0
- package/dist/ionic/svg/cloudy-night-outline.svg +1 -0
- package/dist/ionic/svg/cloudy-night-sharp.svg +1 -0
- package/dist/ionic/svg/cloudy-night.svg +1 -0
- package/dist/ionic/svg/cloudy-outline.svg +1 -0
- package/dist/ionic/svg/cloudy-sharp.svg +1 -0
- package/dist/ionic/svg/cloudy.svg +1 -0
- package/dist/ionic/svg/code-download-outline.svg +1 -0
- package/dist/ionic/svg/code-download-sharp.svg +1 -0
- package/dist/ionic/svg/code-download.svg +1 -0
- package/dist/ionic/svg/code-outline.svg +1 -0
- package/dist/ionic/svg/code-sharp.svg +1 -0
- package/dist/ionic/svg/code-slash-outline.svg +1 -0
- package/dist/ionic/svg/code-slash-sharp.svg +1 -0
- package/dist/ionic/svg/code-slash.svg +1 -0
- package/dist/ionic/svg/code-working-outline.svg +1 -0
- package/dist/ionic/svg/code-working-sharp.svg +1 -0
- package/dist/ionic/svg/code-working.svg +1 -0
- package/dist/ionic/svg/code.svg +1 -0
- package/dist/ionic/svg/cog-outline.svg +1 -0
- package/dist/ionic/svg/cog-sharp.svg +1 -0
- package/dist/ionic/svg/cog.svg +1 -0
- package/dist/ionic/svg/color-fill-outline.svg +1 -0
- package/dist/ionic/svg/color-fill-sharp.svg +1 -0
- package/dist/ionic/svg/color-fill.svg +1 -0
- package/dist/ionic/svg/color-filter-outline.svg +1 -0
- package/dist/ionic/svg/color-filter-sharp.svg +1 -0
- package/dist/ionic/svg/color-filter.svg +1 -0
- package/dist/ionic/svg/color-palette-outline.svg +1 -0
- package/dist/ionic/svg/color-palette-sharp.svg +1 -0
- package/dist/ionic/svg/color-palette.svg +1 -0
- package/dist/ionic/svg/color-wand-outline.svg +1 -0
- package/dist/ionic/svg/color-wand-sharp.svg +1 -0
- package/dist/ionic/svg/color-wand.svg +1 -0
- package/dist/ionic/svg/compass-outline.svg +1 -0
- package/dist/ionic/svg/compass-sharp.svg +1 -0
- package/dist/ionic/svg/compass.svg +1 -0
- package/dist/ionic/svg/construct-outline.svg +1 -0
- package/dist/ionic/svg/construct-sharp.svg +1 -0
- package/dist/ionic/svg/construct.svg +1 -0
- package/dist/ionic/svg/contract-outline.svg +1 -0
- package/dist/ionic/svg/contract-sharp.svg +1 -0
- package/dist/ionic/svg/contract.svg +1 -0
- package/dist/ionic/svg/contrast-outline.svg +1 -0
- package/dist/ionic/svg/contrast-sharp.svg +1 -0
- package/dist/ionic/svg/contrast.svg +1 -0
- package/dist/ionic/svg/copy-outline.svg +1 -0
- package/dist/ionic/svg/copy-sharp.svg +1 -0
- package/dist/ionic/svg/copy.svg +1 -0
- package/dist/ionic/svg/create-outline.svg +1 -0
- package/dist/ionic/svg/create-sharp.svg +1 -0
- package/dist/ionic/svg/create.svg +1 -0
- package/dist/ionic/svg/crop-outline.svg +1 -0
- package/dist/ionic/svg/crop-sharp.svg +1 -0
- package/dist/ionic/svg/crop.svg +1 -0
- package/dist/ionic/svg/cube-outline.svg +1 -0
- package/dist/ionic/svg/cube-sharp.svg +1 -0
- package/dist/ionic/svg/cube.svg +1 -0
- package/dist/ionic/svg/cut-outline.svg +1 -0
- package/dist/ionic/svg/cut-sharp.svg +1 -0
- package/dist/ionic/svg/cut.svg +1 -0
- package/dist/ionic/svg/desktop-outline.svg +1 -0
- package/dist/ionic/svg/desktop-sharp.svg +1 -0
- package/dist/ionic/svg/desktop.svg +1 -0
- package/dist/ionic/svg/diamond-outline.svg +1 -0
- package/dist/ionic/svg/diamond-sharp.svg +1 -0
- package/dist/ionic/svg/diamond.svg +1 -0
- package/dist/ionic/svg/dice-outline.svg +1 -0
- package/dist/ionic/svg/dice-sharp.svg +1 -0
- package/dist/ionic/svg/dice.svg +1 -0
- package/dist/ionic/svg/disc-outline.svg +1 -0
- package/dist/ionic/svg/disc-sharp.svg +1 -0
- package/dist/ionic/svg/disc.svg +1 -0
- package/dist/ionic/svg/document-attach-outline.svg +1 -0
- package/dist/ionic/svg/document-attach-sharp.svg +1 -0
- package/dist/ionic/svg/document-attach.svg +1 -0
- package/dist/ionic/svg/document-lock-outline.svg +1 -0
- package/dist/ionic/svg/document-lock-sharp.svg +1 -0
- package/dist/ionic/svg/document-lock.svg +1 -0
- package/dist/ionic/svg/document-outline.svg +1 -0
- package/dist/types/components/accordion/accordion.d.ts +18 -1
- package/dist/types/components/checkbox/checkbox.d.ts +9 -2
- package/dist/types/components/datetime/datetime.d.ts +10 -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/radio-group/radio-group.d.ts +9 -1
- package/dist/types/components/select/select.d.ts +7 -1
- package/dist/types/components/textarea/textarea.d.ts +0 -4
- package/dist/types/components/toggle/toggle.d.ts +7 -2
- package/dist/types/utils/forms/index.d.ts +1 -0
- package/dist/types/utils/forms/validity.d.ts +10 -0
- package/dist/types/utils/transition/index.d.ts +9 -0
- package/hydrate/index.js +687 -413
- package/hydrate/index.mjs +687 -413
- package/package.json +4 -4
- package/dist/ionic/p-1c8a476d.entry.js +0 -4
- package/dist/ionic/p-3355a2ff.entry.js +0 -4
- package/dist/ionic/p-4efea47a.entry.js +0 -4
- package/dist/ionic/p-62e50f80.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-7bcfc421.entry.js +0 -4
- package/dist/ionic/p-83fc84e7.entry.js +0 -4
- package/dist/ionic/p-913a7c1e.entry.js +0 -4
- package/dist/ionic/p-CMhMiYSX.js +0 -4
- package/dist/ionic/p-c17c0a01.entry.js +0 -4
|
@@ -21,10 +21,57 @@ import { getIonMode } from "../../global/ionic-global";
|
|
|
21
21
|
*/
|
|
22
22
|
export class Accordion {
|
|
23
23
|
constructor() {
|
|
24
|
-
this.
|
|
24
|
+
this.accordionGroupUpdateHandler = () => {
|
|
25
|
+
/**
|
|
26
|
+
* Determine if this update will cause an actual state change.
|
|
27
|
+
* We only want to mark as "interacted" if the state is changing.
|
|
28
|
+
*/
|
|
29
|
+
const accordionGroup = this.accordionGroupEl;
|
|
30
|
+
if (accordionGroup) {
|
|
31
|
+
const value = accordionGroup.value;
|
|
32
|
+
const accordionValue = this.value;
|
|
33
|
+
const shouldExpand = Array.isArray(value) ? value.includes(accordionValue) : value === accordionValue;
|
|
34
|
+
const isExpanded = this.state === 4 /* AccordionState.Expanded */ || this.state === 8 /* AccordionState.Expanding */;
|
|
35
|
+
const stateWillChange = shouldExpand !== isExpanded;
|
|
36
|
+
/**
|
|
37
|
+
* Only mark as interacted if:
|
|
38
|
+
* 1. This is not the first update we've received with a defined value
|
|
39
|
+
* 2. The state is actually changing (prevents redundant updates from enabling animations)
|
|
40
|
+
*/
|
|
41
|
+
if (this.hasReceivedFirstUpdate && stateWillChange) {
|
|
42
|
+
this.hasInteracted = true;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Only count this as the first update if the group value is defined.
|
|
46
|
+
* This prevents the initial undefined value from the group's componentDidLoad
|
|
47
|
+
* from being treated as the first real update.
|
|
48
|
+
*/
|
|
49
|
+
if (value !== undefined) {
|
|
50
|
+
this.hasReceivedFirstUpdate = true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
this.updateState();
|
|
54
|
+
};
|
|
25
55
|
this.state = 1 /* AccordionState.Collapsed */;
|
|
26
56
|
this.isNext = false;
|
|
27
57
|
this.isPrevious = false;
|
|
58
|
+
/**
|
|
59
|
+
* Tracks whether a user-initiated interaction has occurred.
|
|
60
|
+
* Animations are disabled until the first interaction happens.
|
|
61
|
+
* This prevents the accordion from animating when it's programmatically
|
|
62
|
+
* set to an expanded or collapsed state on initial load.
|
|
63
|
+
*/
|
|
64
|
+
this.hasInteracted = false;
|
|
65
|
+
/**
|
|
66
|
+
* Tracks if this accordion has ever been expanded.
|
|
67
|
+
* Used to prevent the first expansion from animating.
|
|
68
|
+
*/
|
|
69
|
+
this.hasEverBeenExpanded = false;
|
|
70
|
+
/**
|
|
71
|
+
* Tracks if this accordion has received its first update from the group.
|
|
72
|
+
* Used to distinguish initial programmatic sets from user interactions.
|
|
73
|
+
*/
|
|
74
|
+
this.hasReceivedFirstUpdate = false;
|
|
28
75
|
/**
|
|
29
76
|
* The value of the accordion. Defaults to an autogenerated
|
|
30
77
|
* value.
|
|
@@ -129,10 +176,15 @@ export class Accordion {
|
|
|
129
176
|
iconEl.setAttribute('aria-hidden', 'true');
|
|
130
177
|
ionItem.appendChild(iconEl);
|
|
131
178
|
};
|
|
132
|
-
this.expandAccordion = (
|
|
179
|
+
this.expandAccordion = () => {
|
|
133
180
|
const { contentEl, contentElWrapper } = this;
|
|
134
|
-
|
|
181
|
+
/**
|
|
182
|
+
* If the content elements aren't available yet, just set the state.
|
|
183
|
+
* This happens on initial render before the DOM is ready.
|
|
184
|
+
*/
|
|
185
|
+
if (contentEl === undefined || contentElWrapper === undefined) {
|
|
135
186
|
this.state = 4 /* AccordionState.Expanded */;
|
|
187
|
+
this.hasEverBeenExpanded = true;
|
|
136
188
|
return;
|
|
137
189
|
}
|
|
138
190
|
if (this.state === 4 /* AccordionState.Expanded */) {
|
|
@@ -141,6 +193,11 @@ export class Accordion {
|
|
|
141
193
|
if (this.currentRaf !== undefined) {
|
|
142
194
|
cancelAnimationFrame(this.currentRaf);
|
|
143
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Mark that this accordion has been expanded at least once.
|
|
198
|
+
* This allows subsequent expansions to animate.
|
|
199
|
+
*/
|
|
200
|
+
this.hasEverBeenExpanded = true;
|
|
144
201
|
if (this.shouldAnimate()) {
|
|
145
202
|
raf(() => {
|
|
146
203
|
this.state = 8 /* AccordionState.Expanding */;
|
|
@@ -158,9 +215,13 @@ export class Accordion {
|
|
|
158
215
|
this.state = 4 /* AccordionState.Expanded */;
|
|
159
216
|
}
|
|
160
217
|
};
|
|
161
|
-
this.collapseAccordion = (
|
|
218
|
+
this.collapseAccordion = () => {
|
|
162
219
|
const { contentEl } = this;
|
|
163
|
-
|
|
220
|
+
/**
|
|
221
|
+
* If the content element isn't available yet, just set the state.
|
|
222
|
+
* This happens on initial render before the DOM is ready.
|
|
223
|
+
*/
|
|
224
|
+
if (contentEl === undefined) {
|
|
164
225
|
this.state = 1 /* AccordionState.Collapsed */;
|
|
165
226
|
return;
|
|
166
227
|
}
|
|
@@ -195,6 +256,18 @@ export class Accordion {
|
|
|
195
256
|
* of what is set in the config.
|
|
196
257
|
*/
|
|
197
258
|
this.shouldAnimate = () => {
|
|
259
|
+
/**
|
|
260
|
+
* Don't animate until after the first user interaction.
|
|
261
|
+
* This prevents animations on initial load when accordions
|
|
262
|
+
* start in an expanded or collapsed state programmatically.
|
|
263
|
+
*
|
|
264
|
+
* Additionally, don't animate the very first expansion even if
|
|
265
|
+
* hasInteracted is true. This handles edge cases like React StrictMode
|
|
266
|
+
* where effects run twice and might incorrectly mark as interacted.
|
|
267
|
+
*/
|
|
268
|
+
if (!this.hasInteracted || !this.hasEverBeenExpanded) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
198
271
|
if (typeof window === 'undefined') {
|
|
199
272
|
return false;
|
|
200
273
|
}
|
|
@@ -211,7 +284,7 @@ export class Accordion {
|
|
|
211
284
|
}
|
|
212
285
|
return true;
|
|
213
286
|
};
|
|
214
|
-
this.updateState = async (
|
|
287
|
+
this.updateState = async () => {
|
|
215
288
|
const accordionGroup = this.accordionGroupEl;
|
|
216
289
|
const accordionValue = this.value;
|
|
217
290
|
if (!accordionGroup) {
|
|
@@ -220,11 +293,11 @@ export class Accordion {
|
|
|
220
293
|
const value = accordionGroup.value;
|
|
221
294
|
const shouldExpand = Array.isArray(value) ? value.includes(accordionValue) : value === accordionValue;
|
|
222
295
|
if (shouldExpand) {
|
|
223
|
-
this.expandAccordion(
|
|
296
|
+
this.expandAccordion();
|
|
224
297
|
this.isNext = this.isPrevious = false;
|
|
225
298
|
}
|
|
226
299
|
else {
|
|
227
|
-
this.collapseAccordion(
|
|
300
|
+
this.collapseAccordion();
|
|
228
301
|
/**
|
|
229
302
|
* When using popout or inset,
|
|
230
303
|
* the collapsed accordion items
|
|
@@ -272,14 +345,14 @@ export class Accordion {
|
|
|
272
345
|
var _a;
|
|
273
346
|
const accordionGroupEl = (this.accordionGroupEl = (_a = this.el) === null || _a === void 0 ? void 0 : _a.closest('ion-accordion-group'));
|
|
274
347
|
if (accordionGroupEl) {
|
|
275
|
-
this.updateState(
|
|
276
|
-
addEventListener(accordionGroupEl, 'ionValueChange', this.
|
|
348
|
+
this.updateState();
|
|
349
|
+
addEventListener(accordionGroupEl, 'ionValueChange', this.accordionGroupUpdateHandler);
|
|
277
350
|
}
|
|
278
351
|
}
|
|
279
352
|
disconnectedCallback() {
|
|
280
353
|
const accordionGroupEl = this.accordionGroupEl;
|
|
281
354
|
if (accordionGroupEl) {
|
|
282
|
-
removeEventListener(accordionGroupEl, 'ionValueChange', this.
|
|
355
|
+
removeEventListener(accordionGroupEl, 'ionValueChange', this.accordionGroupUpdateHandler);
|
|
283
356
|
}
|
|
284
357
|
}
|
|
285
358
|
componentDidLoad() {
|
|
@@ -303,6 +376,11 @@ export class Accordion {
|
|
|
303
376
|
const { accordionGroupEl, disabled, readonly, value, state } = this;
|
|
304
377
|
if (disabled || readonly)
|
|
305
378
|
return;
|
|
379
|
+
/**
|
|
380
|
+
* Mark that the user has interacted with the accordion.
|
|
381
|
+
* This enables animations for all future state changes.
|
|
382
|
+
*/
|
|
383
|
+
this.hasInteracted = true;
|
|
306
384
|
if (accordionGroupEl) {
|
|
307
385
|
/**
|
|
308
386
|
* Because the accordion group may or may
|
|
@@ -323,7 +401,7 @@ export class Accordion {
|
|
|
323
401
|
const headerPart = expanded ? 'header expanded' : 'header';
|
|
324
402
|
const contentPart = expanded ? 'content expanded' : 'content';
|
|
325
403
|
this.setAria(expanded);
|
|
326
|
-
return (h(Host, { key: '
|
|
404
|
+
return (h(Host, { key: '9c90bce01eff7e5774a19f69c872f3761d66cf3c', class: {
|
|
327
405
|
[mode]: true,
|
|
328
406
|
'accordion-expanding': this.state === 8 /* AccordionState.Expanding */,
|
|
329
407
|
'accordion-expanded': this.state === 4 /* AccordionState.Expanded */,
|
|
@@ -334,7 +412,7 @@ export class Accordion {
|
|
|
334
412
|
'accordion-disabled': disabled,
|
|
335
413
|
'accordion-readonly': readonly,
|
|
336
414
|
'accordion-animated': this.shouldAnimate(),
|
|
337
|
-
} }, h("div", { key: '
|
|
415
|
+
} }, h("div", { key: 'cab40d5bcf3c93fd78e70b6d3906a541e725837d', onClick: () => this.toggleExpanded(), id: "header", part: headerPart, "aria-controls": "content", ref: (headerEl) => (this.headerEl = headerEl) }, h("slot", { key: '672bc7fb3f9e18076b41e20fc9eaeab7cafcf3a2', name: "header" })), h("div", { key: 'fd777ca5b4ab04aa4f44c339d58c8cd987c52bcb', id: "content", part: contentPart, role: "region", "aria-labelledby": "header", ref: (contentEl) => (this.contentEl = contentEl) }, h("div", { key: '0aad70a71e2cd2c16b2e98fa0bdd40421d95fe16', id: "content-wrapper", ref: (contentElWrapper) => (this.contentElWrapper = contentElWrapper) }, h("slot", { key: 'd630e10ac7c56b4dbf943b523f26759b83aead55', name: "content" })))));
|
|
338
416
|
}
|
|
339
417
|
static get is() { return "ion-accordion"; }
|
|
340
418
|
static get encapsulation() { return "shadow"; }
|
|
@@ -459,7 +537,8 @@ export class Accordion {
|
|
|
459
537
|
return {
|
|
460
538
|
"state": {},
|
|
461
539
|
"isNext": {},
|
|
462
|
-
"isPrevious": {}
|
|
540
|
+
"isPrevious": {},
|
|
541
|
+
"hasInteracted": {}
|
|
463
542
|
};
|
|
464
543
|
}
|
|
465
544
|
static get elementRef() { return "el"; }
|
|
@@ -216,11 +216,7 @@ export class Button {
|
|
|
216
216
|
target,
|
|
217
217
|
};
|
|
218
218
|
let fill = this.fill;
|
|
219
|
-
|
|
220
|
-
* We check both undefined and null to
|
|
221
|
-
* work around https://github.com/ionic-team/stencil/issues/3586.
|
|
222
|
-
*/
|
|
223
|
-
if (fill == null) {
|
|
219
|
+
if (fill === undefined) {
|
|
224
220
|
fill = this.inToolbar || this.inListHeader ? 'clear' : 'solid';
|
|
225
221
|
}
|
|
226
222
|
/**
|
|
@@ -233,7 +229,7 @@ export class Button {
|
|
|
233
229
|
{
|
|
234
230
|
type !== 'button' && this.renderHiddenButton();
|
|
235
231
|
}
|
|
236
|
-
return (h(Host, { key: '
|
|
232
|
+
return (h(Host, { key: 'ed82ea53705523f9afc5f1a9addff44cc6424f27', onClick: this.handleClick, "aria-disabled": disabled ? 'true' : null, class: createColorClasses(color, {
|
|
237
233
|
[mode]: true,
|
|
238
234
|
[buttonType]: true,
|
|
239
235
|
[`${buttonType}-${expand}`]: expand !== undefined,
|
|
@@ -248,7 +244,7 @@ export class Button {
|
|
|
248
244
|
'button-disabled': disabled,
|
|
249
245
|
'ion-activatable': true,
|
|
250
246
|
'ion-focusable': true,
|
|
251
|
-
}) }, h(TagType, Object.assign({ key: '
|
|
247
|
+
}) }, h(TagType, Object.assign({ key: 'fadec13053469dd0405bbbc61b70ced568aa4826' }, attrs, { class: "button-native", part: "native", disabled: disabled, onFocus: this.onFocus, onBlur: this.onBlur }, inheritedAttributes), h("span", { key: '6bf0e5144fb1148002e88038522402b789689d2c', class: "button-inner" }, h("slot", { key: '25da0ca155cfa9e2754842c34f4fd09f576ac2d2', name: "icon-only", onSlotchange: this.slotChanged }), h("slot", { key: '51414065bb11953ec9d818f8d9353589bc9072c5', name: "start" }), h("slot", { key: 'c9b5f8842aeabd20628df2f4600f1257ea913d8d' }), h("slot", { key: '478dd3671c7be1909fc84e672f0fa8dfe6082263', name: "end" })), mode === 'md' && h("ion-ripple-effect", { key: 'e1d55f85a55144d743f58a5914cd116cb065fa8c', type: this.rippleType }))));
|
|
252
248
|
}
|
|
253
249
|
static get is() { return "ion-button"; }
|
|
254
250
|
static get encapsulation() { return "shadow"; }
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
|
-
import { Host, h } from "@stencil/core";
|
|
4
|
+
import { Build, Host, h } from "@stencil/core";
|
|
5
|
+
import { checkInvalidState } from "../../utils/forms/index";
|
|
5
6
|
import { inheritAriaAttributes, renderHiddenInput } from "../../utils/helpers";
|
|
6
7
|
import { createColorClasses, hostContext } from "../../utils/theme";
|
|
7
8
|
import { getIonMode } from "../../global/ionic-global";
|
|
@@ -62,6 +63,10 @@ export class Checkbox {
|
|
|
62
63
|
* submitting if the value is invalid.
|
|
63
64
|
*/
|
|
64
65
|
this.required = false;
|
|
66
|
+
/**
|
|
67
|
+
* Track validation state for proper aria-live announcements.
|
|
68
|
+
*/
|
|
69
|
+
this.isInvalid = false;
|
|
65
70
|
/**
|
|
66
71
|
* Sets the checked property and emits
|
|
67
72
|
* the ionChange event. Use this to update the
|
|
@@ -77,7 +82,6 @@ export class Checkbox {
|
|
|
77
82
|
};
|
|
78
83
|
this.toggleChecked = (ev) => {
|
|
79
84
|
ev.preventDefault();
|
|
80
|
-
this.setFocus();
|
|
81
85
|
this.setChecked(!this.checked);
|
|
82
86
|
this.indeterminate = false;
|
|
83
87
|
};
|
|
@@ -109,18 +113,63 @@ export class Checkbox {
|
|
|
109
113
|
ev.stopPropagation();
|
|
110
114
|
};
|
|
111
115
|
}
|
|
116
|
+
connectedCallback() {
|
|
117
|
+
const { el } = this;
|
|
118
|
+
// Watch for class changes to update validation state.
|
|
119
|
+
if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
|
|
120
|
+
this.validationObserver = new MutationObserver(() => {
|
|
121
|
+
const newIsInvalid = checkInvalidState(el);
|
|
122
|
+
if (this.isInvalid !== newIsInvalid) {
|
|
123
|
+
this.isInvalid = newIsInvalid;
|
|
124
|
+
/**
|
|
125
|
+
* Screen readers tend to announce changes
|
|
126
|
+
* to `aria-describedby` when the attribute
|
|
127
|
+
* is changed during a blur event for a
|
|
128
|
+
* native form control.
|
|
129
|
+
* However, the announcement can be spotty
|
|
130
|
+
* when using a non-native form control
|
|
131
|
+
* and `forceUpdate()`.
|
|
132
|
+
* This is due to `forceUpdate()` internally
|
|
133
|
+
* rescheduling the DOM update to a lower
|
|
134
|
+
* priority queue regardless if it's called
|
|
135
|
+
* inside a Promise or not, thus causing
|
|
136
|
+
* the screen reader to potentially miss the
|
|
137
|
+
* change.
|
|
138
|
+
* By using a State variable inside a Promise,
|
|
139
|
+
* it guarantees a re-render immediately at
|
|
140
|
+
* a higher priority.
|
|
141
|
+
*/
|
|
142
|
+
Promise.resolve().then(() => {
|
|
143
|
+
this.hintTextId = this.getHintTextId();
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
this.validationObserver.observe(el, {
|
|
148
|
+
attributes: true,
|
|
149
|
+
attributeFilter: ['class'],
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// Always set initial state
|
|
153
|
+
this.isInvalid = checkInvalidState(el);
|
|
154
|
+
}
|
|
112
155
|
componentWillLoad() {
|
|
113
156
|
this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
|
|
157
|
+
this.hintTextId = this.getHintTextId();
|
|
158
|
+
}
|
|
159
|
+
disconnectedCallback() {
|
|
160
|
+
// Clean up validation observer to prevent memory leaks.
|
|
161
|
+
if (this.validationObserver) {
|
|
162
|
+
this.validationObserver.disconnect();
|
|
163
|
+
this.validationObserver = undefined;
|
|
164
|
+
}
|
|
114
165
|
}
|
|
115
166
|
/** @internal */
|
|
116
167
|
async setFocus() {
|
|
117
|
-
|
|
118
|
-
this.focusEl.focus();
|
|
119
|
-
}
|
|
168
|
+
this.el.focus();
|
|
120
169
|
}
|
|
121
|
-
|
|
122
|
-
const {
|
|
123
|
-
if (
|
|
170
|
+
getHintTextId() {
|
|
171
|
+
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
172
|
+
if (isInvalid && errorText) {
|
|
124
173
|
return errorTextId;
|
|
125
174
|
}
|
|
126
175
|
if (helperText) {
|
|
@@ -133,7 +182,7 @@ export class Checkbox {
|
|
|
133
182
|
* This element should only be rendered if hint text is set.
|
|
134
183
|
*/
|
|
135
184
|
renderHintText() {
|
|
136
|
-
const { helperText, errorText, helperTextId, errorTextId } = this;
|
|
185
|
+
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
|
|
137
186
|
/**
|
|
138
187
|
* undefined and empty string values should
|
|
139
188
|
* be treated as not having helper/error text.
|
|
@@ -142,7 +191,7 @@ export class Checkbox {
|
|
|
142
191
|
if (!hasHintText) {
|
|
143
192
|
return;
|
|
144
193
|
}
|
|
145
|
-
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)));
|
|
194
|
+
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)));
|
|
146
195
|
}
|
|
147
196
|
render() {
|
|
148
197
|
const { color, checked, disabled, el, getSVGPath, indeterminate, inheritedAttributes, inputId, justify, labelPlacement, name, value, alignment, required, } = this;
|
|
@@ -152,7 +201,7 @@ export class Checkbox {
|
|
|
152
201
|
renderHiddenInput(true, el, name, checked ? value : '', disabled);
|
|
153
202
|
// The host element must have a checkbox role to ensure proper VoiceOver
|
|
154
203
|
// support in Safari for accessibility.
|
|
155
|
-
return (h(Host, { key: '
|
|
204
|
+
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, {
|
|
156
205
|
[mode]: true,
|
|
157
206
|
'in-item': hostContext('ion-item', el),
|
|
158
207
|
'checkbox-checked': checked,
|
|
@@ -162,10 +211,10 @@ export class Checkbox {
|
|
|
162
211
|
[`checkbox-justify-${justify}`]: justify !== undefined,
|
|
163
212
|
[`checkbox-alignment-${alignment}`]: alignment !== undefined,
|
|
164
213
|
[`checkbox-label-placement-${labelPlacement}`]: true,
|
|
165
|
-
})
|
|
214
|
+
}) }, 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: {
|
|
166
215
|
'label-text-wrapper': true,
|
|
167
216
|
'label-text-wrapper-hidden': !hasLabelContent,
|
|
168
|
-
}, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '
|
|
217
|
+
}, 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)))));
|
|
169
218
|
}
|
|
170
219
|
getSVGPath(mode, indeterminate) {
|
|
171
220
|
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" }));
|
|
@@ -433,6 +482,12 @@ export class Checkbox {
|
|
|
433
482
|
}
|
|
434
483
|
};
|
|
435
484
|
}
|
|
485
|
+
static get states() {
|
|
486
|
+
return {
|
|
487
|
+
"isInvalid": {},
|
|
488
|
+
"hintTextId": {}
|
|
489
|
+
};
|
|
490
|
+
}
|
|
436
491
|
static get events() {
|
|
437
492
|
return [{
|
|
438
493
|
"method": "ionChange",
|
|
@@ -585,6 +585,28 @@ export class Datetime {
|
|
|
585
585
|
destroyKeyboardMO();
|
|
586
586
|
}
|
|
587
587
|
};
|
|
588
|
+
/**
|
|
589
|
+
* TODO(FW-6931): Remove this fallback upon solving the root cause
|
|
590
|
+
* Fallback to ensure the datetime becomes ready even if
|
|
591
|
+
* IntersectionObserver never reports it as intersecting.
|
|
592
|
+
*
|
|
593
|
+
* This is primarily used in environments where the observer
|
|
594
|
+
* might not fire as expected, such as when running under
|
|
595
|
+
* synthetic tests that stub IntersectionObserver.
|
|
596
|
+
*/
|
|
597
|
+
this.ensureReadyIfVisible = () => {
|
|
598
|
+
if (this.el.classList.contains('datetime-ready')) {
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
const rect = this.el.getBoundingClientRect();
|
|
602
|
+
if (rect.width === 0 || rect.height === 0) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
this.initializeListeners();
|
|
606
|
+
writeTask(() => {
|
|
607
|
+
this.el.classList.add('datetime-ready');
|
|
608
|
+
});
|
|
609
|
+
};
|
|
588
610
|
this.processValue = (value) => {
|
|
589
611
|
const hasValue = value !== null && value !== undefined && value !== '' && (!Array.isArray(value) || value.length > 0);
|
|
590
612
|
const valueToProcess = hasValue ? parseDate(value) : this.defaultParts;
|
|
@@ -902,6 +924,17 @@ export class Datetime {
|
|
|
902
924
|
* triggering the `hiddenIO` observer below.
|
|
903
925
|
*/
|
|
904
926
|
raf(() => visibleIO === null || visibleIO === void 0 ? void 0 : visibleIO.observe(intersectionTrackerRef));
|
|
927
|
+
/**
|
|
928
|
+
* TODO(FW-6931): Remove this fallback upon solving the root cause
|
|
929
|
+
* Fallback: If IntersectionObserver never reports that the
|
|
930
|
+
* datetime is visible but the host clearly has layout, ensure
|
|
931
|
+
* we still initialize listeners and mark the component as ready.
|
|
932
|
+
*
|
|
933
|
+
* We schedule this after everything has had a chance to run.
|
|
934
|
+
*/
|
|
935
|
+
setTimeout(() => {
|
|
936
|
+
this.ensureReadyIfVisible();
|
|
937
|
+
}, 100);
|
|
905
938
|
/**
|
|
906
939
|
* We need to clean up listeners when the datetime is hidden
|
|
907
940
|
* in a popover/modal so that we can properly scroll containers
|
|
@@ -1657,7 +1690,7 @@ export class Datetime {
|
|
|
1657
1690
|
const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
|
|
1658
1691
|
const hasWheelVariant = hasDatePresentation && preferWheel;
|
|
1659
1692
|
renderHiddenInput(true, el, name, formatValue(value), disabled);
|
|
1660
|
-
return (h(Host, { key: '
|
|
1693
|
+
return (h(Host, { key: 'efdbc0922670a841bc667ceac392cdc1dedffd01', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
|
|
1661
1694
|
[mode]: true,
|
|
1662
1695
|
['datetime-readonly']: readonly,
|
|
1663
1696
|
['datetime-disabled']: disabled,
|
|
@@ -1667,7 +1700,7 @@ export class Datetime {
|
|
|
1667
1700
|
[`datetime-size-${size}`]: true,
|
|
1668
1701
|
[`datetime-prefer-wheel`]: hasWheelVariant,
|
|
1669
1702
|
[`datetime-grid`]: isGridStyle,
|
|
1670
|
-
})) }, h("div", { key: '
|
|
1703
|
+
})) }, h("div", { key: '3f8bb75fcb0baff55182ef3aa1b535eacc58d81f', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
|
|
1671
1704
|
}
|
|
1672
1705
|
static get is() { return "ion-datetime"; }
|
|
1673
1706
|
static get encapsulation() { return "shadow"; }
|
|
@@ -152,6 +152,15 @@ ion-header ion-toolbar:first-of-type {
|
|
|
152
152
|
--opacity-scale: inherit;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Override styles applied during the page transition to prevent
|
|
157
|
+
* header flickering.
|
|
158
|
+
*/
|
|
159
|
+
.header-collapse-fade.header-transitioning ion-toolbar {
|
|
160
|
+
--background: transparent;
|
|
161
|
+
--border-style: none;
|
|
162
|
+
}
|
|
163
|
+
|
|
155
164
|
.header-collapse-condense {
|
|
156
165
|
z-index: 9;
|
|
157
166
|
}
|
|
@@ -175,7 +184,6 @@ ion-header ion-toolbar:first-of-type {
|
|
|
175
184
|
* since it needs to blend in with the header above it.
|
|
176
185
|
*/
|
|
177
186
|
.header-collapse-condense ion-toolbar {
|
|
178
|
-
--background: var(--ion-background-color, #fff);
|
|
179
187
|
z-index: 0;
|
|
180
188
|
}
|
|
181
189
|
|
|
@@ -201,6 +209,24 @@ ion-header ion-toolbar:first-of-type {
|
|
|
201
209
|
transition: all 0.2s ease-in-out;
|
|
202
210
|
}
|
|
203
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Large title toolbar should just use the content background
|
|
214
|
+
* since it needs to blend in with the header above it.
|
|
215
|
+
*/
|
|
216
|
+
.header-collapse-condense ion-toolbar,
|
|
217
|
+
.header-collapse-condense-inactive.header-transitioning:not(.header-collapse-condense) ion-toolbar {
|
|
218
|
+
--background: var(--ion-background-color, #fff);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Override styles applied during the page transition to prevent
|
|
223
|
+
* header flickering.
|
|
224
|
+
*/
|
|
225
|
+
.header-collapse-condense-inactive.header-transitioning:not(.header-collapse-condense) ion-toolbar {
|
|
226
|
+
--border-style: none;
|
|
227
|
+
--opacity-scale: 1;
|
|
228
|
+
}
|
|
229
|
+
|
|
204
230
|
.header-collapse-condense-inactive:not(.header-collapse-condense) ion-toolbar.in-toolbar ion-title,
|
|
205
231
|
.header-collapse-condense-inactive:not(.header-collapse-condense) ion-toolbar.in-toolbar ion-buttons.buttons-collapse {
|
|
206
232
|
opacity: 0;
|
|
@@ -6,7 +6,7 @@ import { findIonContent, getScrollElement, printIonContentErrorMsg } from "../..
|
|
|
6
6
|
import { inheritAriaAttributes } from "../../utils/helpers";
|
|
7
7
|
import { hostContext } from "../../utils/theme";
|
|
8
8
|
import { getIonMode } from "../../global/ionic-global";
|
|
9
|
-
import { cloneElement, createHeaderIndex, handleContentScroll, handleHeaderFade, handleToolbarIntersection, setHeaderActive, setToolbarBackgroundOpacity, } from "./header.utils";
|
|
9
|
+
import { cloneElement, createHeaderIndex, handleContentScroll, handleHeaderFade, handleToolbarIntersection, setHeaderActive, setToolbarBackgroundOpacity, getRoleType, } from "./header.utils";
|
|
10
10
|
/**
|
|
11
11
|
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
|
|
12
12
|
*/
|
|
@@ -145,16 +145,17 @@ export class Header {
|
|
|
145
145
|
const { translucent, inheritedAttributes } = this;
|
|
146
146
|
const mode = getIonMode(this);
|
|
147
147
|
const collapse = this.collapse || 'none';
|
|
148
|
+
const isCondensed = collapse === 'condense';
|
|
148
149
|
// banner role must be at top level, so remove role if inside a menu
|
|
149
|
-
const roleType = hostContext('ion-menu', this.el)
|
|
150
|
-
return (h(Host, Object.assign({ key: '
|
|
150
|
+
const roleType = getRoleType(hostContext('ion-menu', this.el), isCondensed, mode);
|
|
151
|
+
return (h(Host, Object.assign({ key: '863c4568cd7b8c0ec55109f193bbbaed68a1346e', role: roleType, class: {
|
|
151
152
|
[mode]: true,
|
|
152
153
|
// Used internally for styling
|
|
153
154
|
[`header-${mode}`]: true,
|
|
154
155
|
[`header-translucent`]: this.translucent,
|
|
155
156
|
[`header-collapse-${collapse}`]: true,
|
|
156
157
|
[`header-translucent-${mode}`]: this.translucent,
|
|
157
|
-
} }, inheritedAttributes), mode === 'ios' && translucent && h("div", { key: '
|
|
158
|
+
} }, inheritedAttributes), mode === 'ios' && translucent && h("div", { key: '25c3bdce328b0b35607d154c8b8374679313d881', class: "header-background" }), h("slot", { key: 'b44fab0a9be7920b9650da26117c783e751e1702' })));
|
|
158
159
|
}
|
|
159
160
|
static get is() { return "ion-header"; }
|
|
160
161
|
static get originalStyleUrls() {
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import { readTask, writeTask } from "@stencil/core";
|
|
5
5
|
import { clamp } from "../../utils/helpers";
|
|
6
6
|
const TRANSITION = 'all 0.2s ease-in-out';
|
|
7
|
+
const ROLE_NONE = 'none';
|
|
8
|
+
const ROLE_BANNER = 'banner';
|
|
7
9
|
export const cloneElement = (tagName) => {
|
|
8
10
|
const getCachedEl = document.querySelector(`${tagName}.ion-cloned-element`);
|
|
9
11
|
if (getCachedEl !== null) {
|
|
@@ -130,6 +132,7 @@ export const setHeaderActive = (headerIndex, active = true) => {
|
|
|
130
132
|
const toolbars = headerIndex.toolbars;
|
|
131
133
|
const ionTitles = toolbars.map((toolbar) => toolbar.ionTitleEl);
|
|
132
134
|
if (active) {
|
|
135
|
+
headerEl.setAttribute('role', ROLE_BANNER);
|
|
133
136
|
headerEl.classList.remove('header-collapse-condense-inactive');
|
|
134
137
|
ionTitles.forEach((ionTitle) => {
|
|
135
138
|
if (ionTitle) {
|
|
@@ -138,6 +141,16 @@ export const setHeaderActive = (headerIndex, active = true) => {
|
|
|
138
141
|
});
|
|
139
142
|
}
|
|
140
143
|
else {
|
|
144
|
+
/**
|
|
145
|
+
* There can only be one banner landmark per page.
|
|
146
|
+
* By default, all ion-headers have the banner role.
|
|
147
|
+
* This causes an accessibility issue when using a
|
|
148
|
+
* condensed header since there are two ion-headers
|
|
149
|
+
* on the page at once (active and inactive).
|
|
150
|
+
* To solve this, the role needs to be toggled
|
|
151
|
+
* based on which header is active.
|
|
152
|
+
*/
|
|
153
|
+
headerEl.setAttribute('role', ROLE_NONE);
|
|
141
154
|
headerEl.classList.add('header-collapse-condense-inactive');
|
|
142
155
|
/**
|
|
143
156
|
* The small title should only be accessed by screen readers
|
|
@@ -197,3 +210,27 @@ export const handleHeaderFade = (scrollEl, baseEl, condenseHeader) => {
|
|
|
197
210
|
});
|
|
198
211
|
});
|
|
199
212
|
};
|
|
213
|
+
/**
|
|
214
|
+
* Get the role type for the ion-header.
|
|
215
|
+
*
|
|
216
|
+
* @param isInsideMenu If ion-header is inside ion-menu.
|
|
217
|
+
* @param isCondensed If ion-header has collapse="condense".
|
|
218
|
+
* @param mode The current mode.
|
|
219
|
+
* @returns 'none' if inside ion-menu or if condensed in md
|
|
220
|
+
* mode, otherwise 'banner'.
|
|
221
|
+
*/
|
|
222
|
+
export const getRoleType = (isInsideMenu, isCondensed, mode) => {
|
|
223
|
+
// If the header is inside a menu, it should not have the banner role.
|
|
224
|
+
if (isInsideMenu) {
|
|
225
|
+
return ROLE_NONE;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Only apply role="none" to `md` mode condensed headers
|
|
229
|
+
* since the large header is never shown.
|
|
230
|
+
*/
|
|
231
|
+
if (isCondensed && mode === 'md') {
|
|
232
|
+
return ROLE_NONE;
|
|
233
|
+
}
|
|
234
|
+
// Default to banner role.
|
|
235
|
+
return ROLE_BANNER;
|
|
236
|
+
};
|