voyager-ionic-core 8.8.12 → 8.8.17
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/index.js +1 -1
- package/components/ion-accordion.js +1 -1
- package/components/ion-action-sheet.js +1 -1
- package/components/ion-alert.js +1 -1
- package/components/ion-app.js +1 -1
- package/components/ion-back-button.js +1 -1
- package/components/ion-breadcrumb.js +1 -1
- package/components/ion-button.js +1 -1
- package/components/ion-card.js +1 -1
- package/components/ion-checkbox.js +1 -1
- package/components/ion-content.js +1 -1
- package/components/ion-datetime-button.js +1 -1
- package/components/ion-datetime.js +1 -1
- package/components/ion-fab-button.js +1 -1
- package/components/ion-footer.js +1 -1
- package/components/ion-header.js +1 -1
- package/components/ion-img.js +1 -1
- package/components/ion-infinite-scroll.js +1 -1
- package/components/ion-input-otp.js +1 -1
- package/components/ion-input-password-toggle.js +1 -1
- package/components/ion-input.js +1 -1
- package/components/ion-item-options.js +1 -1
- package/components/ion-item-sliding.js +1 -1
- package/components/ion-item.js +1 -1
- package/components/ion-loading.js +1 -1
- package/components/ion-menu-button.js +1 -1
- package/components/ion-menu-toggle.js +1 -1
- package/components/ion-menu.js +1 -1
- package/components/ion-modal.js +1 -1
- package/components/ion-nav.js +1 -1
- package/components/ion-picker-column-option.js +1 -1
- package/components/ion-picker-column.js +1 -1
- package/components/ion-picker-legacy-column.js +1 -1
- package/components/ion-picker-legacy.js +1 -1
- package/components/ion-picker.js +1 -1
- package/components/ion-popover.js +1 -1
- package/components/ion-progress-bar.js +1 -1
- package/components/ion-radio-group.js +1 -1
- package/components/ion-radio.js +1 -1
- package/components/ion-range.js +1 -1
- package/components/ion-refresher-content.js +1 -1
- package/components/ion-refresher.js +1 -1
- package/components/ion-reorder-group.js +1 -1
- package/components/ion-router-outlet.js +1 -1
- package/components/ion-router.js +1 -1
- package/components/ion-searchbar.js +1 -1
- package/components/ion-segment-button.js +1 -1
- package/components/ion-segment.js +1 -1
- package/components/ion-select-modal.js +1 -1
- package/components/ion-select-popover.js +1 -1
- package/components/ion-select.js +1 -1
- package/components/ion-tab-button.js +1 -1
- package/components/ion-tab.js +1 -1
- package/components/ion-textarea.js +1 -1
- package/components/ion-toast.js +1 -1
- package/components/ion-toggle.js +1 -1
- package/components/{p-CtukzcyX.js → p-A3O24YmY.js} +1 -1
- package/components/{p-CU5rbWAW.js → p-ABkNVeU9.js} +1 -1
- package/components/{p-BT_5jSqG.js → p-B-JJgYSG.js} +1 -1
- package/components/p-BB42Mlnr.js +4 -0
- package/components/{p-lE_JGpHD.js → p-BMI53wYL.js} +1 -1
- package/components/p-BkKAANMJ.js +4 -0
- package/components/p-BlIAuGv2.js +4 -0
- package/components/{p-Bth-EzJ0.js → p-Bnsn6KaP.js} +1 -1
- package/components/p-BsBCoGa-.js +4 -0
- package/components/{p-BnqQlm7c.js → p-CQGnMY3n.js} +1 -1
- package/components/{p-DNQI6d-L.js → p-C_hpsif8.js} +1 -1
- package/components/p-Cf2jSDS8.js +4 -0
- package/components/{p-C4t5ymfq.js → p-ChIZKXUu.js} +1 -1
- package/components/p-CkMSOsFF.js +4 -0
- package/components/p-CsNJ1ULL.js +4 -0
- package/components/{p-BsPelv04.js → p-CzCbeKys.js} +1 -1
- package/components/{p-BjfgFSSR.js → p-D-aHdMxW.js} +1 -1
- package/components/p-D0d8qTwj.js +4 -0
- package/components/{p-Baq1XyAy.js → p-D3xo7hqU.js} +1 -1
- package/components/{p-nQi4oKcG.js → p-DCnGU1fF.js} +1 -1
- package/components/p-DGZpx6KJ.js +4 -0
- package/components/p-DGvrK8dD.js +4 -0
- package/components/p-DHTSTb52.js +4 -0
- package/components/{p-BirPFsRD.js → p-DJZp0KTQ.js} +1 -1
- package/components/{p-ijF0iCrA.js → p-DRugphUN.js} +1 -1
- package/components/{p-0NRupjzW.js → p-DV5mPYQS.js} +1 -1
- package/components/{p-C1aScSTo.js → p-DWkAgceV.js} +1 -1
- package/components/{p-DDWp95gH.js → p-KEFO0w3i.js} +1 -1
- package/components/{p-Ciz1-FuE.js → p-Osve4qkc.js} +1 -1
- package/components/{p-dhLYXa7A.js → p-SLS1rDZM.js} +1 -1
- package/components/{p-D5Ubpm7D.js → p-VWbRkLT7.js} +1 -1
- package/components/{p-BTPfOxsU.js → p-aJ6PBUvj.js} +1 -1
- package/components/{p-DnL1c9_X.js → p-oD4kcBRX.js} +1 -1
- package/components/{p-CI3ncNZm.js → p-scxfI1Dd.js} +1 -1
- package/components/p-yzCsOZE2.js +4 -0
- package/dist/cjs/{data-DLTUw-KF.js → format-PxvGiOik.js} +8 -532
- package/dist/cjs/index.cjs.js +1 -1
- package/dist/cjs/ion-action-sheet.cjs.entry.js +1 -1
- package/dist/cjs/ion-alert.cjs.entry.js +1 -1
- package/dist/cjs/ion-button_2.cjs.entry.js +7 -3
- package/dist/cjs/ion-checkbox.cjs.entry.js +17 -7
- package/dist/cjs/ion-datetime-button.cjs.entry.js +17 -15
- package/dist/cjs/ion-datetime_3.cjs.entry.js +616 -75
- package/dist/cjs/ion-fab_3.cjs.entry.js +87 -9
- package/dist/cjs/ion-item_8.cjs.entry.js +84 -4
- package/dist/cjs/ion-loading.cjs.entry.js +1 -1
- package/dist/cjs/ion-menu_3.cjs.entry.js +1 -1
- package/dist/cjs/ion-modal.cjs.entry.js +35 -9
- package/dist/cjs/ion-popover.cjs.entry.js +1 -1
- package/dist/cjs/ion-radio_2.cjs.entry.js +16 -7
- package/dist/cjs/ion-refresher_2.cjs.entry.js +31 -4
- package/dist/cjs/ion-select-modal.cjs.entry.js +1 -1
- package/dist/cjs/ion-select_3.cjs.entry.js +5 -4
- package/dist/cjs/ion-tab_2.cjs.entry.js +20 -13
- package/dist/cjs/ion-toast.cjs.entry.js +1 -1
- package/dist/cjs/ion-toggle.cjs.entry.js +25 -6
- package/dist/cjs/ionic.cjs.js +1 -1
- package/dist/cjs/item-multiple-inputs-D-gyg57c.js +38 -0
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/{overlays-TbKsuC-K.js → overlays-DdtIYQUH.js} +24 -1
- package/dist/collection/components/button/button.js +7 -3
- package/dist/collection/components/checkbox/checkbox.ios.css +18 -0
- package/dist/collection/components/checkbox/checkbox.js +16 -7
- package/dist/collection/components/checkbox/checkbox.md.css +18 -0
- package/dist/collection/components/datetime/datetime.js +51 -6
- package/dist/collection/components/datetime-button/datetime-button.js +8 -7
- package/dist/collection/components/fab-button/fab-button.js +114 -10
- package/dist/collection/components/item/item.ios.css +11 -0
- package/dist/collection/components/item/item.js +85 -4
- package/dist/collection/components/item/item.md.css +11 -0
- package/dist/collection/components/modal/gestures/sheet.js +7 -0
- package/dist/collection/components/modal/gestures/swipe-to-close.js +7 -0
- package/dist/collection/components/modal/modal.ios.css +8 -0
- package/dist/collection/components/modal/modal.js +18 -6
- package/dist/collection/components/modal/modal.md.css +8 -0
- package/dist/collection/components/radio/radio.ios.css +11 -15
- package/dist/collection/components/radio/radio.js +15 -7
- package/dist/collection/components/radio/radio.md.css +11 -11
- package/dist/collection/components/refresher/refresher.js +31 -4
- package/dist/collection/components/select/select.js +4 -3
- package/dist/collection/components/tab/tab.js +20 -13
- package/dist/collection/components/toggle/toggle.ios.css +12 -4
- package/dist/collection/components/toggle/toggle.js +24 -6
- package/dist/collection/components/toggle/toggle.md.css +42 -5
- package/dist/collection/utils/forms/index.js +1 -0
- package/dist/collection/utils/forms/item-multiple-inputs.js +33 -0
- package/dist/collection/utils/overlays.js +24 -1
- package/dist/collection/utils/test/playwright/apply-keyboard-focus.js +25 -0
- package/dist/collection/utils/test/playwright/drag-element.js +15 -0
- package/dist/collection/utils/test/playwright/index.js +1 -0
- package/dist/docs.json +40 -1
- package/dist/esm/{data-DZI70dKr.js → format-CAmvpAez.js} +1 -524
- package/dist/esm/index.js +1 -1
- package/dist/esm/ion-action-sheet.entry.js +1 -1
- package/dist/esm/ion-alert.entry.js +1 -1
- package/dist/esm/ion-button_2.entry.js +7 -3
- package/dist/esm/ion-checkbox.entry.js +19 -9
- package/dist/esm/ion-datetime-button.entry.js +9 -7
- package/dist/esm/ion-datetime_3.entry.js +550 -9
- package/dist/esm/ion-fab_3.entry.js +89 -11
- package/dist/esm/ion-item_8.entry.js +84 -4
- package/dist/esm/ion-loading.entry.js +1 -1
- package/dist/esm/ion-menu_3.entry.js +1 -1
- package/dist/esm/ion-modal.entry.js +35 -9
- package/dist/esm/ion-popover.entry.js +1 -1
- package/dist/esm/ion-radio_2.entry.js +17 -8
- package/dist/esm/ion-refresher_2.entry.js +31 -4
- package/dist/esm/ion-select-modal.entry.js +1 -1
- package/dist/esm/ion-select_3.entry.js +5 -4
- package/dist/esm/ion-tab_2.entry.js +20 -13
- package/dist/esm/ion-toast.entry.js +1 -1
- package/dist/esm/ion-toggle.entry.js +27 -8
- package/dist/esm/ionic.js +1 -1
- package/dist/esm/item-multiple-inputs-DlNuqNJm.js +36 -0
- package/dist/esm/loader.js +1 -1
- package/dist/esm/{overlays-TbiM4mdr.js → overlays-Dg0q2_tJ.js} +25 -2
- package/dist/html.html-data.json +4 -0
- package/dist/ionic/index.esm.js +1 -1
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/{p-30bf9737.entry.js → p-0dc33652.entry.js} +1 -1
- package/dist/ionic/p-10e833ef.entry.js +4 -0
- package/dist/ionic/{p-4dc1c33c.entry.js → p-2a05aa64.entry.js} +1 -1
- package/dist/ionic/p-2b204c59.entry.js +4 -0
- package/dist/ionic/p-2fa58e80.entry.js +4 -0
- package/dist/ionic/{p-94f2a736.entry.js → p-34586ead.entry.js} +1 -1
- package/dist/ionic/p-4654ae1c.entry.js +4 -0
- package/dist/ionic/p-4ac4f3d6.entry.js +4 -0
- package/dist/ionic/p-5018dfc8.entry.js +4 -0
- package/dist/ionic/{p-00dbb17e.entry.js → p-5eb8e86d.entry.js} +1 -1
- package/dist/ionic/{p-e847a888.entry.js → p-65ebdf1b.entry.js} +1 -1
- package/dist/ionic/p-7c319341.entry.js +4 -0
- package/dist/ionic/p-829367ed.entry.js +4 -0
- package/dist/ionic/{p-aa5d6b8f.entry.js → p-9f9db3a2.entry.js} +1 -1
- package/dist/ionic/p-C0eMdTCW.js +4 -0
- package/dist/ionic/p-D-3ycHmd.js +4 -0
- package/dist/ionic/p-DqN5EA2O.js +4 -0
- package/dist/ionic/p-aaeedb6b.entry.js +4 -0
- package/dist/ionic/p-bfc9bb0f.entry.js +4 -0
- package/dist/ionic/p-c38b648e.entry.js +4 -0
- package/dist/ionic/{p-3a541154.entry.js → p-ca2ab74d.entry.js} +1 -1
- package/dist/ionic/p-d889ff6d.entry.js +4 -0
- package/dist/types/components/checkbox/checkbox.d.ts +1 -0
- package/dist/types/components/datetime/datetime.d.ts +8 -0
- package/dist/types/components/fab-button/fab-button.d.ts +15 -0
- package/dist/types/components/item/item.d.ts +19 -0
- package/dist/types/components/radio/radio.d.ts +1 -0
- package/dist/types/components/tab/tab.d.ts +1 -1
- package/dist/types/components/toggle/toggle.d.ts +1 -0
- package/dist/types/components.d.ts +15 -2
- package/dist/types/utils/forms/index.d.ts +1 -0
- package/dist/types/utils/forms/item-multiple-inputs.d.ts +13 -0
- package/dist/types/utils/test/playwright/apply-keyboard-focus.d.ts +16 -0
- package/dist/types/utils/test/playwright/index.d.ts +1 -0
- package/hydrate/index.js +382 -79
- package/hydrate/index.mjs +382 -79
- package/package.json +5 -5
- package/components/p-5bE_Wz4Y.js +0 -4
- package/components/p-B60hm8Qv.js +0 -4
- package/components/p-BAYYcg3o.js +0 -4
- package/components/p-BjoEPDW5.js +0 -4
- package/components/p-C2lKgrXx.js +0 -4
- package/components/p-D7bSXQD3.js +0 -4
- package/components/p-DIE4pXMl.js +0 -4
- package/components/p-W5nO8mX_.js +0 -4
- package/components/p-W9CZ2mWX.js +0 -4
- package/components/p-XlSIJDJ-.js +0 -4
- package/components/p-ryqmO0B-.js +0 -4
- package/dist/ionic/p-0d005398.entry.js +0 -4
- package/dist/ionic/p-1ae21e4c.entry.js +0 -4
- package/dist/ionic/p-2f55dc1a.entry.js +0 -4
- package/dist/ionic/p-3a126bca.entry.js +0 -4
- package/dist/ionic/p-3a7d1e91.entry.js +0 -4
- package/dist/ionic/p-81ea5652.entry.js +0 -4
- package/dist/ionic/p-8c8eee7d.entry.js +0 -4
- package/dist/ionic/p-9a65ac7f.entry.js +0 -4
- package/dist/ionic/p-DAAFkPFT.js +0 -4
- package/dist/ionic/p-a21add2f.entry.js +0 -4
- package/dist/ionic/p-ec090e0a.entry.js +0 -4
- package/dist/ionic/p-fabcce6c.entry.js +0 -4
- package/dist/ionic/p-facfde02.entry.js +0 -4
- package/dist/ionic/p-xOfi7-Zn.js +0 -4
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
|
-
import { Build, Host, h } from "@stencil/core";
|
|
5
|
-
import { checkInvalidState } from "../../utils/forms/index";
|
|
4
|
+
import { Build, Host, forceUpdate, h } from "@stencil/core";
|
|
5
|
+
import { checkInvalidState, createItemMultipleInputsObserver } from "../../utils/forms/index";
|
|
6
6
|
import { inheritAriaAttributes, renderHiddenInput } from "../../utils/helpers";
|
|
7
7
|
import { createColorClasses, hostContext } from "../../utils/theme";
|
|
8
8
|
import { getIonMode } from "../../global/ionic-global";
|
|
@@ -162,17 +162,21 @@ export class Checkbox {
|
|
|
162
162
|
// Always set initial state
|
|
163
163
|
this.isInvalid = checkInvalidState(el);
|
|
164
164
|
this.hasLabelContent = this.el.textContent !== '';
|
|
165
|
+
this.itemFocusObserver = createItemMultipleInputsObserver(el, () => forceUpdate(this));
|
|
165
166
|
}
|
|
166
167
|
componentWillLoad() {
|
|
167
168
|
this.inheritedAttributes = Object.assign({}, inheritAriaAttributes(this.el));
|
|
168
169
|
this.hintTextId = this.getHintTextId();
|
|
169
170
|
}
|
|
170
171
|
disconnectedCallback() {
|
|
171
|
-
// Clean up validation observer to prevent memory leaks.
|
|
172
172
|
if (this.validationObserver) {
|
|
173
173
|
this.validationObserver.disconnect();
|
|
174
174
|
this.validationObserver = undefined;
|
|
175
175
|
}
|
|
176
|
+
if (this.itemFocusObserver) {
|
|
177
|
+
this.itemFocusObserver.disconnect();
|
|
178
|
+
this.itemFocusObserver = undefined;
|
|
179
|
+
}
|
|
176
180
|
}
|
|
177
181
|
/** @internal */
|
|
178
182
|
async setFocus() {
|
|
@@ -208,23 +212,28 @@ export class Checkbox {
|
|
|
208
212
|
const { color, checked, disabled, el, getSVGPath, indeterminate, inheritedAttributes, inputId, justify, labelPlacement, name, value, alignment, required, } = this;
|
|
209
213
|
const mode = getIonMode(this);
|
|
210
214
|
const path = getSVGPath(mode, indeterminate);
|
|
215
|
+
const inItem = hostContext('ion-item', el);
|
|
216
|
+
const inMultipleInputsItem = hostContext('ion-item.item-multiple-inputs', el);
|
|
211
217
|
renderHiddenInput(true, el, name, checked ? value : '', disabled);
|
|
212
218
|
// The host element must have a checkbox role to ensure proper VoiceOver
|
|
213
219
|
// support in Safari for accessibility.
|
|
214
|
-
return (h(Host, { key: '
|
|
220
|
+
return (h(Host, { key: '6de0a71ffc45e65a4cf5ab0a1216f3ccd159d8de', role: "checkbox", "aria-checked": indeterminate ? 'mixed' : `${checked}`, "aria-describedby": this.hintTextId, "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-labelledby": this.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, {
|
|
215
221
|
[mode]: true,
|
|
216
|
-
'in-item':
|
|
222
|
+
'in-item': inItem,
|
|
217
223
|
'checkbox-checked': checked,
|
|
218
224
|
'checkbox-disabled': disabled,
|
|
219
225
|
'checkbox-indeterminate': indeterminate,
|
|
220
226
|
interactive: true,
|
|
227
|
+
// A single-input item has the input cover and draws the indicator itself.
|
|
228
|
+
// A multi-input item has no cover, so each control draws its own.
|
|
229
|
+
'ion-focusable': !inItem || inMultipleInputsItem,
|
|
221
230
|
[`checkbox-justify-${justify}`]: justify !== undefined,
|
|
222
231
|
[`checkbox-alignment-${alignment}`]: alignment !== undefined,
|
|
223
232
|
[`checkbox-label-placement-${labelPlacement}`]: true,
|
|
224
|
-
}) }, h("label", { key: '
|
|
233
|
+
}) }, h("label", { key: 'acca2f5b0a37aa70930b2f74ff4857939b2be2b0', class: "checkbox-wrapper", htmlFor: inputId }, h("input", Object.assign({ key: 'f46e409383e9d3ffc3daae8e7cd4159427d4c8fe', type: "checkbox", checked: checked ? true : undefined, disabled: disabled, id: inputId, onChange: this.toggleChecked, required: required }, inheritedAttributes)), h("div", { key: '69d8bf21c781cd9b1f56ceae40ff7911e6f5adfe', class: {
|
|
225
234
|
'label-text-wrapper': true,
|
|
226
235
|
'label-text-wrapper-hidden': !this.hasLabelContent,
|
|
227
|
-
}, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '
|
|
236
|
+
}, part: "label", id: this.inputLabelId, onClick: this.onDivLabelClick }, h("slot", { key: '3c444ab897456c522588fc6cee616030f16b0586' }), this.renderHintText()), h("div", { key: '0a18dfe7a68a8eec08ce8764bd2c8a59c4a38802', class: "native-wrapper" }, h("svg", { key: '56b298b54ebc88a429a9dd67b8dabd7063759b3a', class: "checkbox-icon", viewBox: "0 0 24 24", part: "container", "aria-hidden": "true" }, path)))));
|
|
228
237
|
}
|
|
229
238
|
getSVGPath(mode, indeterminate) {
|
|
230
239
|
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" }));
|
|
@@ -153,8 +153,13 @@ input {
|
|
|
153
153
|
display: none;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
:host(:focus) {
|
|
157
|
+
outline: none;
|
|
158
|
+
}
|
|
159
|
+
|
|
156
160
|
.native-wrapper {
|
|
157
161
|
display: flex;
|
|
162
|
+
position: relative;
|
|
158
163
|
align-items: center;
|
|
159
164
|
}
|
|
160
165
|
|
|
@@ -178,6 +183,19 @@ input {
|
|
|
178
183
|
opacity: 0;
|
|
179
184
|
}
|
|
180
185
|
|
|
186
|
+
:host(.ion-focused) .native-wrapper::after {
|
|
187
|
+
border-radius: 50%;
|
|
188
|
+
display: block;
|
|
189
|
+
position: absolute;
|
|
190
|
+
width: 36px;
|
|
191
|
+
height: 36px;
|
|
192
|
+
background: var(--ion-color-primary-tint, #1a65eb);
|
|
193
|
+
content: "";
|
|
194
|
+
opacity: 0.2;
|
|
195
|
+
inset-block-start: calc(50% - 36px / 2);
|
|
196
|
+
inset-inline-start: calc(50% - 36px / 2);
|
|
197
|
+
}
|
|
198
|
+
|
|
181
199
|
.checkbox-bottom {
|
|
182
200
|
padding-top: 4px;
|
|
183
201
|
display: flex;
|
|
@@ -9,6 +9,7 @@ import { FOCUS_TRAP_DISABLE_CLASS } from "../../utils/overlays";
|
|
|
9
9
|
import { isRTL } from "../../utils/rtl/index";
|
|
10
10
|
import { createColorClasses } from "../../utils/theme";
|
|
11
11
|
import { caretDownSharp, caretUpSharp, chevronBack, chevronDown, chevronForward } from "ionicons/icons";
|
|
12
|
+
import { config } from "../../global/config";
|
|
12
13
|
import { getIonMode } from "../../global/ionic-global";
|
|
13
14
|
import { isSameDay, warnIfValueOutOfBounds, isBefore, isAfter } from "./utils/comparison";
|
|
14
15
|
import { generateMonths, getDaysOfMonth, getDaysOfWeek, getToday, getMonthColumnData, getDayColumnData, getYearColumnData, getTimeColumnsData, getCombinedDateColumnData, } from "./utils/data";
|
|
@@ -780,10 +781,11 @@ export class Datetime {
|
|
|
780
781
|
return;
|
|
781
782
|
}
|
|
782
783
|
const left = nextMonth.offsetWidth * 2;
|
|
784
|
+
const scrollMode = config.getBoolean('animated', true) ? 'smooth' : 'instant';
|
|
783
785
|
calendarBodyRef.scrollTo({
|
|
784
786
|
top: 0,
|
|
785
787
|
left: left * (isRTL(this.el) ? -1 : 1),
|
|
786
|
-
behavior:
|
|
788
|
+
behavior: scrollMode,
|
|
787
789
|
});
|
|
788
790
|
};
|
|
789
791
|
this.prevMonth = () => {
|
|
@@ -796,10 +798,11 @@ export class Datetime {
|
|
|
796
798
|
return;
|
|
797
799
|
}
|
|
798
800
|
const left = prevMonth.offsetWidth * 2;
|
|
801
|
+
const scrollMode = config.getBoolean('animated', true) ? 'smooth' : 'instant';
|
|
799
802
|
calendarBodyRef.scrollTo({
|
|
800
803
|
top: 0,
|
|
801
804
|
left: left * (isRTL(this.el) ? 1 : -1),
|
|
802
|
-
behavior:
|
|
805
|
+
behavior: scrollMode,
|
|
803
806
|
});
|
|
804
807
|
};
|
|
805
808
|
this.toggleMonthAndYearView = () => {
|
|
@@ -914,6 +917,16 @@ export class Datetime {
|
|
|
914
917
|
this.closeParentOverlay(CANCEL_ROLE);
|
|
915
918
|
}
|
|
916
919
|
}
|
|
920
|
+
/**
|
|
921
|
+
* Returns the default parts the datetime falls back to when no value is set:
|
|
922
|
+
* today's date and time snapped to the closest value allowed by the
|
|
923
|
+
* component's constraints (`min`, `max`, and the `*Values` props).
|
|
924
|
+
*
|
|
925
|
+
* @internal
|
|
926
|
+
*/
|
|
927
|
+
async getDefaultPart() {
|
|
928
|
+
return this.defaultParts;
|
|
929
|
+
}
|
|
917
930
|
get isCalendarPicker() {
|
|
918
931
|
const { presentation } = this;
|
|
919
932
|
return presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
|
|
@@ -1326,7 +1339,10 @@ export class Datetime {
|
|
|
1326
1339
|
const activePart = this.getActivePartsWithFallback();
|
|
1327
1340
|
return (h("ion-picker-column", { part: WHEEL_PART, "aria-label": "Select a month", class: "month-column", color: this.color, disabled: disabled, value: workingParts.month, onIonChange: (ev) => {
|
|
1328
1341
|
this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { month: ev.detail.value }));
|
|
1329
|
-
|
|
1342
|
+
// Month wheel is navigation-only in multi-select mode as a fix for https://github.com/ionic-team/ionic-framework/issues/29673
|
|
1343
|
+
if (!this.multiple) {
|
|
1344
|
+
this.setActiveParts(Object.assign(Object.assign({}, activePart), { month: ev.detail.value }));
|
|
1345
|
+
}
|
|
1330
1346
|
ev.stopPropagation();
|
|
1331
1347
|
} }, months.map((month) => (h("ion-picker-column-option", { part: month.value === workingParts.month ? `${WHEEL_ITEM_PART} ${WHEEL_ITEM_ACTIVE_PART}` : WHEEL_ITEM_PART, key: month.value, disabled: month.disabled, value: month.value }, month.text)))));
|
|
1332
1348
|
}
|
|
@@ -1338,7 +1354,10 @@ export class Datetime {
|
|
|
1338
1354
|
const activePart = this.getActivePartsWithFallback();
|
|
1339
1355
|
return (h("ion-picker-column", { part: WHEEL_PART, "aria-label": "Select a year", class: "year-column", color: this.color, disabled: disabled, value: workingParts.year, onIonChange: (ev) => {
|
|
1340
1356
|
this.setWorkingParts(Object.assign(Object.assign({}, workingParts), { year: ev.detail.value }));
|
|
1341
|
-
|
|
1357
|
+
// Year wheel is navigation-only in multi-select mode as a fix for https://github.com/ionic-team/ionic-framework/issues/29673
|
|
1358
|
+
if (!this.multiple) {
|
|
1359
|
+
this.setActiveParts(Object.assign(Object.assign({}, activePart), { year: ev.detail.value }));
|
|
1360
|
+
}
|
|
1342
1361
|
ev.stopPropagation();
|
|
1343
1362
|
} }, years.map((year) => (h("ion-picker-column-option", { part: year.value === workingParts.year ? `${WHEEL_ITEM_PART} ${WHEEL_ITEM_ACTIVE_PART}` : WHEEL_ITEM_PART, key: year.value, disabled: year.disabled, value: year.value }, year.text)))));
|
|
1344
1363
|
}
|
|
@@ -1735,7 +1754,7 @@ export class Datetime {
|
|
|
1735
1754
|
const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
|
|
1736
1755
|
const hasWheelVariant = hasDatePresentation && preferWheel;
|
|
1737
1756
|
renderHiddenInput(true, el, name, formatValue(value), disabled);
|
|
1738
|
-
return (h(Host, { key: '
|
|
1757
|
+
return (h(Host, { key: '5e0b83cb88a87945eaae2f9402751819b75452ee', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: Object.assign({}, createColorClasses(color, {
|
|
1739
1758
|
[mode]: true,
|
|
1740
1759
|
['datetime-readonly']: readonly,
|
|
1741
1760
|
['datetime-disabled']: disabled,
|
|
@@ -1745,7 +1764,7 @@ export class Datetime {
|
|
|
1745
1764
|
[`datetime-size-${size}`]: true,
|
|
1746
1765
|
[`datetime-prefer-wheel`]: hasWheelVariant,
|
|
1747
1766
|
[`datetime-grid`]: isGridStyle,
|
|
1748
|
-
})) }, h("div", { key: '
|
|
1767
|
+
})) }, h("div", { key: '9f9ba72e9879525961b00abe342a7891492ca0ff', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
|
|
1749
1768
|
}
|
|
1750
1769
|
static get is() { return "ion-datetime"; }
|
|
1751
1770
|
static get encapsulation() { return "shadow"; }
|
|
@@ -2632,6 +2651,32 @@ export class Datetime {
|
|
|
2632
2651
|
"text": "closeOverlay If `true`, closes the parent overlay. Defaults to `false`."
|
|
2633
2652
|
}]
|
|
2634
2653
|
}
|
|
2654
|
+
},
|
|
2655
|
+
"getDefaultPart": {
|
|
2656
|
+
"complexType": {
|
|
2657
|
+
"signature": "() => Promise<DatetimeParts>",
|
|
2658
|
+
"parameters": [],
|
|
2659
|
+
"references": {
|
|
2660
|
+
"Promise": {
|
|
2661
|
+
"location": "global",
|
|
2662
|
+
"id": "global::Promise"
|
|
2663
|
+
},
|
|
2664
|
+
"DatetimeParts": {
|
|
2665
|
+
"location": "import",
|
|
2666
|
+
"path": "./datetime-interface",
|
|
2667
|
+
"id": "src/components/datetime/datetime-interface.ts::DatetimeParts",
|
|
2668
|
+
"referenceLocation": "DatetimeParts"
|
|
2669
|
+
}
|
|
2670
|
+
},
|
|
2671
|
+
"return": "Promise<DatetimeParts>"
|
|
2672
|
+
},
|
|
2673
|
+
"docs": {
|
|
2674
|
+
"text": "Returns the default parts the datetime falls back to when no value is set:\ntoday's date and time snapped to the closest value allowed by the\ncomponent's constraints (`min`, `max`, and the `*Values` props).",
|
|
2675
|
+
"tags": [{
|
|
2676
|
+
"name": "internal",
|
|
2677
|
+
"text": undefined
|
|
2678
|
+
}]
|
|
2679
|
+
}
|
|
2635
2680
|
}
|
|
2636
2681
|
};
|
|
2637
2682
|
}
|
|
@@ -6,7 +6,6 @@ import { componentOnReady, addEventListener } from "../../utils/helpers";
|
|
|
6
6
|
import { printIonError } from "../../utils/logging/index";
|
|
7
7
|
import { createColorClasses } from "../../utils/theme";
|
|
8
8
|
import { getIonMode } from "../../global/ionic-global";
|
|
9
|
-
import { getToday } from "../datetime/utils/data";
|
|
10
9
|
import { getLocalizedDateTime, getLocalizedTime } from "../datetime/utils/format";
|
|
11
10
|
import { getHourCycle } from "../datetime/utils/helpers";
|
|
12
11
|
import { parseDate } from "../datetime/utils/parse";
|
|
@@ -54,7 +53,7 @@ export class DatetimeButton {
|
|
|
54
53
|
* ion-datetime and then format it according
|
|
55
54
|
* to the locale specified on ion-datetime.
|
|
56
55
|
*/
|
|
57
|
-
this.setDateTimeText = () => {
|
|
56
|
+
this.setDateTimeText = async () => {
|
|
58
57
|
var _a, _b, _c, _d, _e;
|
|
59
58
|
const { datetimeEl, datetimePresentation } = this;
|
|
60
59
|
if (!datetimeEl) {
|
|
@@ -63,10 +62,12 @@ export class DatetimeButton {
|
|
|
63
62
|
const { value, locale, formatOptions, hourCycle, preferWheel, multiple, titleSelectedDatesFormatter } = datetimeEl;
|
|
64
63
|
const parsedValues = this.getParsedDateValues(value);
|
|
65
64
|
/**
|
|
66
|
-
* Both ion-datetime and ion-datetime-button default
|
|
67
|
-
*
|
|
65
|
+
* Both ion-datetime and ion-datetime-button default to today's date and
|
|
66
|
+
* time if no value is set. We read the datetime's computed default so the
|
|
67
|
+
* button respects the same constraints (min, max, minuteValues, etc.) that
|
|
68
|
+
* the datetime applies to its own fallback, instead of using a raw "now".
|
|
68
69
|
*/
|
|
69
|
-
const parsedDatetimes =
|
|
70
|
+
const parsedDatetimes = parsedValues.length > 0 ? parseDate(parsedValues) : [await datetimeEl.getDefaultPart()];
|
|
70
71
|
if (!parsedDatetimes) {
|
|
71
72
|
return;
|
|
72
73
|
}
|
|
@@ -341,11 +342,11 @@ export class DatetimeButton {
|
|
|
341
342
|
render() {
|
|
342
343
|
const { color, dateText, timeText, selectedButton, datetimeActive, disabled } = this;
|
|
343
344
|
const mode = getIonMode(this);
|
|
344
|
-
return (h(Host, { key: '
|
|
345
|
+
return (h(Host, { key: 'c28e5e428111f18eaceb030dca8aba5579867eaa', class: createColorClasses(color, {
|
|
345
346
|
[mode]: true,
|
|
346
347
|
[`${selectedButton}-active`]: datetimeActive,
|
|
347
348
|
['datetime-button-disabled']: disabled,
|
|
348
|
-
}) }, dateText && (h("button", { key: '
|
|
349
|
+
}) }, dateText && (h("button", { key: 'a08f41b3150ef7171d0f1d9b3d69b51be67b7b84', class: "ion-activatable", id: "date-button", "aria-expanded": datetimeActive ? 'true' : 'false', onClick: this.handleDateClick, disabled: disabled, part: "native", ref: (el) => (this.dateTargetEl = el) }, h("slot", { key: '35ee68ca1cea59f03e1b59b72606e618141c8e1f', name: "date-target" }, dateText), mode === 'md' && h("ion-ripple-effect", { key: '4be53227aaf0acd20e8ebbff9f645140e8a96f33' }))), timeText && (h("button", { key: 'ab500f9b78f8c194180073eaf7325041adbd02f7', class: "ion-activatable", id: "time-button", "aria-expanded": datetimeActive ? 'true' : 'false', onClick: this.handleTimeClick, disabled: disabled, part: "native", ref: (el) => (this.timeTargetEl = el) }, h("slot", { key: '5822486dfdedba6c071a0f5233cc06f8321a1f5d', name: "time-target" }, timeText), mode === 'md' && h("ion-ripple-effect", { key: '054a4acb811a98cc6d9bb6a3cf51232a1f09b0ea' })))));
|
|
349
350
|
}
|
|
350
351
|
static get is() { return "ion-datetime-button"; }
|
|
351
352
|
static get encapsulation() { return "shadow"; }
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
4
|
import { Host, h } from "@stencil/core";
|
|
5
|
-
import { inheritAriaAttributes } from "../../utils/helpers";
|
|
5
|
+
import { inheritAriaAttributes, hasShadowDom } from "../../utils/helpers";
|
|
6
|
+
import { printIonWarning } from "../../utils/logging/index";
|
|
6
7
|
import { createColorClasses, hostContext, openURL } from "../../utils/theme";
|
|
7
8
|
import { close } from "ionicons/icons";
|
|
8
9
|
import { getIonMode } from "../../global/ionic-global";
|
|
@@ -15,6 +16,8 @@ import { getIonMode } from "../../global/ionic-global";
|
|
|
15
16
|
export class FabButton {
|
|
16
17
|
constructor() {
|
|
17
18
|
this.fab = null;
|
|
19
|
+
this.formButtonEl = null;
|
|
20
|
+
this.formEl = null;
|
|
18
21
|
this.inheritedAttributes = {};
|
|
19
22
|
/**
|
|
20
23
|
* If `true`, the fab button will be show a close icon.
|
|
@@ -55,34 +58,105 @@ export class FabButton {
|
|
|
55
58
|
this.onBlur = () => {
|
|
56
59
|
this.ionBlur.emit();
|
|
57
60
|
};
|
|
58
|
-
this.onClick = () => {
|
|
59
|
-
const { fab } = this;
|
|
60
|
-
if (
|
|
61
|
-
|
|
61
|
+
this.onClick = (ev) => {
|
|
62
|
+
const { el, fab } = this;
|
|
63
|
+
if (this.type !== 'button' && hasShadowDom(el)) {
|
|
64
|
+
this.submitForm(ev);
|
|
65
|
+
}
|
|
66
|
+
if (fab) {
|
|
67
|
+
fab.toggle();
|
|
62
68
|
}
|
|
63
|
-
fab.toggle();
|
|
64
69
|
};
|
|
65
70
|
}
|
|
71
|
+
disabledChanged() {
|
|
72
|
+
const { disabled } = this;
|
|
73
|
+
if (this.formButtonEl) {
|
|
74
|
+
this.formButtonEl.disabled = disabled;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
66
77
|
connectedCallback() {
|
|
67
78
|
this.fab = this.el.closest('ion-fab');
|
|
68
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* The native button rendered inside `ion-fab-button` is in the Shadow DOM and
|
|
82
|
+
* does not participate in form submission, so a hidden native button is added
|
|
83
|
+
* to the associated form and clicked instead.
|
|
84
|
+
*/
|
|
85
|
+
renderHiddenButton() {
|
|
86
|
+
const formEl = (this.formEl = this.findForm());
|
|
87
|
+
if (formEl) {
|
|
88
|
+
const { formButtonEl } = this;
|
|
89
|
+
// Reuse the existing hidden button, syncing values that changed since it was created.
|
|
90
|
+
if (formButtonEl !== null && formEl.contains(formButtonEl)) {
|
|
91
|
+
formButtonEl.disabled = this.disabled;
|
|
92
|
+
formButtonEl.type = this.type;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const newFormButtonEl = (this.formButtonEl = document.createElement('button'));
|
|
96
|
+
newFormButtonEl.type = this.type;
|
|
97
|
+
newFormButtonEl.style.display = 'none';
|
|
98
|
+
newFormButtonEl.disabled = this.disabled;
|
|
99
|
+
formEl.appendChild(newFormButtonEl);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// TODO(FW-7663): share the hidden button and form lookup with ion-button
|
|
103
|
+
findForm() {
|
|
104
|
+
const { form } = this;
|
|
105
|
+
if (form instanceof HTMLFormElement) {
|
|
106
|
+
return form;
|
|
107
|
+
}
|
|
108
|
+
if (typeof form === 'string') {
|
|
109
|
+
const el = document.getElementById(form);
|
|
110
|
+
if (el) {
|
|
111
|
+
if (el instanceof HTMLFormElement) {
|
|
112
|
+
return el;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
printIonWarning(`[ion-fab-button] - Form with selector: "#${form}" could not be found. Verify that the id is attached to a <form> element.`, this.el);
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
printIonWarning(`[ion-fab-button] - Form with selector: "#${form}" could not be found. Verify that the id is correct and the form is rendered in the DOM.`, this.el);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (form !== undefined) {
|
|
125
|
+
printIonWarning(`[ion-fab-button] - The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.`, this.el);
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
return this.el.closest('form');
|
|
129
|
+
}
|
|
130
|
+
submitForm(ev) {
|
|
131
|
+
if (this.formEl && this.formButtonEl) {
|
|
132
|
+
ev.preventDefault();
|
|
133
|
+
this.formButtonEl.click();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
69
136
|
componentWillLoad() {
|
|
70
137
|
this.inheritedAttributes = inheritAriaAttributes(this.el);
|
|
71
138
|
}
|
|
72
139
|
render() {
|
|
73
|
-
const { el, disabled, color, href, activated, show, translucent, size, inheritedAttributes } = this;
|
|
140
|
+
const { el, disabled, color, href, activated, show, translucent, size, inheritedAttributes, type } = this;
|
|
74
141
|
const inList = hostContext('ion-fab-list', el);
|
|
75
142
|
const mode = getIonMode(this);
|
|
76
143
|
const TagType = href === undefined ? 'button' : 'a';
|
|
77
144
|
const attrs = TagType === 'button'
|
|
78
|
-
? { type
|
|
145
|
+
? { type }
|
|
79
146
|
: {
|
|
80
147
|
download: this.download,
|
|
81
148
|
href,
|
|
82
149
|
rel: this.rel,
|
|
83
150
|
target: this.target,
|
|
84
151
|
};
|
|
85
|
-
|
|
152
|
+
/**
|
|
153
|
+
* The hidden button is created during render so that setting `type` or
|
|
154
|
+
* `form` after the component loads still adds it to the form.
|
|
155
|
+
*/
|
|
156
|
+
if (type !== 'button') {
|
|
157
|
+
this.renderHiddenButton();
|
|
158
|
+
}
|
|
159
|
+
return (h(Host, { key: '89e8f2564f12f8c1cd8509a40359202347948846', onClick: this.onClick, "aria-disabled": disabled ? 'true' : null, class: createColorClasses(color, {
|
|
86
160
|
[mode]: true,
|
|
87
161
|
'fab-button-in-list': inList,
|
|
88
162
|
'fab-button-translucent-in-list': inList && translucent,
|
|
@@ -93,7 +167,7 @@ export class FabButton {
|
|
|
93
167
|
'ion-activatable': true,
|
|
94
168
|
'ion-focusable': true,
|
|
95
169
|
[`fab-button-${size}`]: size !== undefined,
|
|
96
|
-
}) }, h(TagType, Object.assign({ key: '
|
|
170
|
+
}) }, h(TagType, Object.assign({ key: '9e2fd462316e1aec50f53fa1b7ad7f02fd399592' }, attrs, { class: "button-native", part: "native", disabled: disabled, onFocus: this.onFocus, onBlur: this.onBlur, onClick: (ev) => type === 'button' && openURL(href, ev, this.routerDirection, this.routerAnimation) }, inheritedAttributes), h("ion-icon", { key: 'c3bdb8614eb249297afc963810066658dc85efd6', "aria-hidden": "true", icon: this.closeIcon, part: "close-icon", class: "close-icon", lazy: false }), h("span", { key: 'a6a505b24d21cc716d2e3351ad4c380b95c768d7', class: "button-inner" }, h("slot", { key: 'f44fc1e95533438ee3adc8cc5bdd9e1e85849fd6' })), mode === 'md' && h("ion-ripple-effect", { key: 'b7c7dc7fceb0bb5613239608225f040010b7b2bd' }))));
|
|
97
171
|
}
|
|
98
172
|
static get is() { return "ion-fab-button"; }
|
|
99
173
|
static get encapsulation() { return "shadow"; }
|
|
@@ -364,6 +438,30 @@ export class FabButton {
|
|
|
364
438
|
"attribute": "type",
|
|
365
439
|
"defaultValue": "'button'"
|
|
366
440
|
},
|
|
441
|
+
"form": {
|
|
442
|
+
"type": "string",
|
|
443
|
+
"mutable": false,
|
|
444
|
+
"complexType": {
|
|
445
|
+
"original": "string | HTMLFormElement",
|
|
446
|
+
"resolved": "HTMLFormElement | string | undefined",
|
|
447
|
+
"references": {
|
|
448
|
+
"HTMLFormElement": {
|
|
449
|
+
"location": "global",
|
|
450
|
+
"id": "global::HTMLFormElement"
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
"required": false,
|
|
455
|
+
"optional": true,
|
|
456
|
+
"docs": {
|
|
457
|
+
"tags": [],
|
|
458
|
+
"text": "The HTML form element or form element id. Used to submit a form when the button is not a child of the form."
|
|
459
|
+
},
|
|
460
|
+
"getter": false,
|
|
461
|
+
"setter": false,
|
|
462
|
+
"reflect": false,
|
|
463
|
+
"attribute": "form"
|
|
464
|
+
},
|
|
367
465
|
"size": {
|
|
368
466
|
"type": "string",
|
|
369
467
|
"mutable": false,
|
|
@@ -439,4 +537,10 @@ export class FabButton {
|
|
|
439
537
|
}];
|
|
440
538
|
}
|
|
441
539
|
static get elementRef() { return "el"; }
|
|
540
|
+
static get watchers() {
|
|
541
|
+
return [{
|
|
542
|
+
"propName": "disabled",
|
|
543
|
+
"methodName": "disabledChanged"
|
|
544
|
+
}];
|
|
545
|
+
}
|
|
442
546
|
}
|
|
@@ -387,6 +387,17 @@ a {
|
|
|
387
387
|
flex-direction: column;
|
|
388
388
|
}
|
|
389
389
|
|
|
390
|
+
:host(.item-focus-indicator-room) .input-wrapper {
|
|
391
|
+
-webkit-margin-start: -12px;
|
|
392
|
+
margin-inline-start: -12px;
|
|
393
|
+
-webkit-margin-end: -12px;
|
|
394
|
+
margin-inline-end: -12px;
|
|
395
|
+
-webkit-padding-start: 12px;
|
|
396
|
+
padding-inline-start: 12px;
|
|
397
|
+
-webkit-padding-end: 12px;
|
|
398
|
+
padding-inline-end: 12px;
|
|
399
|
+
}
|
|
400
|
+
|
|
390
401
|
:host(.item-multiple-inputs) ::slotted(ion-checkbox),
|
|
391
402
|
:host(.item-multiple-inputs) ::slotted(ion-datetime),
|
|
392
403
|
:host(.item-multiple-inputs) ::slotted(ion-radio) {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
|
-
import { Host, forceUpdate, h } from "@stencil/core";
|
|
4
|
+
import { Build, Host, forceUpdate, h } from "@stencil/core";
|
|
5
5
|
import { inheritAttributes, raf } from "../../utils/helpers";
|
|
6
6
|
import { createColorClasses, hostContext, openURL } from "../../utils/theme";
|
|
7
7
|
import { chevronForward } from "ionicons/icons";
|
|
8
8
|
import { getIonMode } from "../../global/ionic-global";
|
|
9
|
+
const INDICATOR_CONTROL_SELECTOR = 'ion-checkbox, ion-radio, ion-toggle';
|
|
9
10
|
/**
|
|
10
11
|
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
|
|
11
12
|
*
|
|
@@ -23,9 +24,11 @@ export class Item {
|
|
|
23
24
|
this.labelColorStyles = {};
|
|
24
25
|
this.itemStyles = new Map();
|
|
25
26
|
this.inheritedAriaAttributes = {};
|
|
27
|
+
this.didLoad = false;
|
|
26
28
|
this.multipleInputs = false;
|
|
27
29
|
this.focusable = true;
|
|
28
30
|
this.isInteractive = false;
|
|
31
|
+
this.hasSlottedIndicatorControl = false;
|
|
29
32
|
/**
|
|
30
33
|
* If `true`, a button tag will be rendered and the item will be tappable.
|
|
31
34
|
*/
|
|
@@ -51,6 +54,7 @@ export class Item {
|
|
|
51
54
|
this.updateInteractivityOnSlotChange = () => {
|
|
52
55
|
this.setIsInteractive();
|
|
53
56
|
this.setMultipleInputs();
|
|
57
|
+
this.setHasSlottedIndicatorControl();
|
|
54
58
|
};
|
|
55
59
|
}
|
|
56
60
|
buttonChanged() {
|
|
@@ -92,6 +96,14 @@ export class Item {
|
|
|
92
96
|
}
|
|
93
97
|
connectedCallback() {
|
|
94
98
|
this.hasStartEl();
|
|
99
|
+
/**
|
|
100
|
+
* `componentDidLoad` doesn't run again when the item is moved, so re-arm the
|
|
101
|
+
* observer and re-read the light DOM, which may have changed while detached.
|
|
102
|
+
*/
|
|
103
|
+
if (this.didLoad) {
|
|
104
|
+
this.watchForIndicatorControls();
|
|
105
|
+
this.updateInteractivityOnSlotChange();
|
|
106
|
+
}
|
|
95
107
|
}
|
|
96
108
|
componentWillLoad() {
|
|
97
109
|
this.inheritedAriaAttributes = inheritAttributes(this.el, ['aria-label']);
|
|
@@ -100,8 +112,17 @@ export class Item {
|
|
|
100
112
|
raf(() => {
|
|
101
113
|
this.setMultipleInputs();
|
|
102
114
|
this.setIsInteractive();
|
|
115
|
+
this.setHasSlottedIndicatorControl();
|
|
103
116
|
this.focusable = this.isFocusable();
|
|
104
117
|
});
|
|
118
|
+
this.watchForIndicatorControls();
|
|
119
|
+
this.didLoad = true;
|
|
120
|
+
}
|
|
121
|
+
disconnectedCallback() {
|
|
122
|
+
if (this.indicatorControlObserver) {
|
|
123
|
+
this.indicatorControlObserver.disconnect();
|
|
124
|
+
this.indicatorControlObserver = undefined;
|
|
125
|
+
}
|
|
105
126
|
}
|
|
106
127
|
totalNestedInputs() {
|
|
107
128
|
// The following elements have a clickable cover that is relative to the entire item
|
|
@@ -135,6 +156,50 @@ export class Item {
|
|
|
135
156
|
const { covers, inputs, clickables } = this.totalNestedInputs();
|
|
136
157
|
this.isInteractive = covers.length > 0 || inputs.length > 0 || clickables.length > 0;
|
|
137
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* `slotchange` only fires for nodes assigned directly to a slot, so a control
|
|
161
|
+
* inside a slotted wrapper (`<div><ion-toggle>`) never reaches
|
|
162
|
+
* `updateInteractivityOnSlotChange`. The light DOM is observed instead.
|
|
163
|
+
*
|
|
164
|
+
* The callback runs the whole handler because a control below a wrapper can also
|
|
165
|
+
* make the item multi-input, which is what decides whether the controls draw
|
|
166
|
+
* their own indicator at all.
|
|
167
|
+
*
|
|
168
|
+
* `:host(:has())` would avoid the observer, but the `:has()` fallback in
|
|
169
|
+
* `core.scss` is still open as FW-6106 and it's unreliable for slotted content
|
|
170
|
+
* in Android WebView. Worth revisiting when FW-6106 closes.
|
|
171
|
+
*/
|
|
172
|
+
watchForIndicatorControls() {
|
|
173
|
+
var _a;
|
|
174
|
+
if (!Build.isBrowser || typeof MutationObserver === 'undefined') {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
// `Node.moveBefore` relocates the item without either callback firing, so
|
|
178
|
+
// never leave a previous observer behind
|
|
179
|
+
(_a = this.indicatorControlObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
180
|
+
this.indicatorControlObserver = new MutationObserver((records) => {
|
|
181
|
+
// The subtree observer also fires for text and hidden input churn, so only
|
|
182
|
+
// re-read the DOM when a control was added or removed
|
|
183
|
+
if (records.some(touchesIndicatorControl)) {
|
|
184
|
+
this.updateInteractivityOnSlotChange();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
this.indicatorControlObserver.observe(this.el, { childList: true, subtree: true });
|
|
188
|
+
}
|
|
189
|
+
// These controls paint a focus indicator that overhangs their own bounds, and
|
|
190
|
+
// only the default slot is clipped, so only a control there needs extra room.
|
|
191
|
+
setHasSlottedIndicatorControl() {
|
|
192
|
+
const controls = this.el.querySelectorAll(INDICATOR_CONTROL_SELECTOR);
|
|
193
|
+
this.hasSlottedIndicatorControl = Array.from(controls).some((control) => {
|
|
194
|
+
// The control isn't always a direct child, so walk up to the element the item
|
|
195
|
+
// slots, which is the one carrying the slot name.
|
|
196
|
+
let slotted = control;
|
|
197
|
+
while (slotted !== null && slotted.parentElement !== this.el) {
|
|
198
|
+
slotted = slotted.parentElement;
|
|
199
|
+
}
|
|
200
|
+
return slotted !== null && !slotted.getAttribute('slot');
|
|
201
|
+
});
|
|
202
|
+
}
|
|
138
203
|
// If the item contains an input including a checkbox, datetime, select, or radio
|
|
139
204
|
// then the item will have a clickable input cover that covers the item
|
|
140
205
|
// that should get the hover, focused and activated states UNLESS it has multiple
|
|
@@ -236,7 +301,13 @@ export class Item {
|
|
|
236
301
|
* However, other form controls such as checkboxes and radios do.
|
|
237
302
|
*/
|
|
238
303
|
const firstInteractiveNeedsPointerCursor = firstInteractive !== undefined && !['ION-INPUT', 'ION-TEXTAREA'].includes(firstInteractive.tagName);
|
|
239
|
-
|
|
304
|
+
/**
|
|
305
|
+
* A control in a single-input item defers its indicator to the item, so there's
|
|
306
|
+
* nothing to clip and nothing to make room for. It draws its own indicator in a
|
|
307
|
+
* multi-input item, and in a clickable item, which is a second tab stop.
|
|
308
|
+
*/
|
|
309
|
+
const slottedIndicatorNeedsRoom = this.hasSlottedIndicatorControl && (multipleInputs || this.isClickable());
|
|
310
|
+
return (h(Host, { key: '0dc161ad7ac2fdedc5005015fceb34ed176010a6', "aria-disabled": ariaDisabled, class: Object.assign(Object.assign(Object.assign({}, childStyles), labelColorStyles), createColorClasses(this.color, {
|
|
240
311
|
item: true,
|
|
241
312
|
[mode]: true,
|
|
242
313
|
'item-lines-default': lines === undefined,
|
|
@@ -245,10 +316,11 @@ export class Item {
|
|
|
245
316
|
'item-disabled': disabled,
|
|
246
317
|
'in-list': inList,
|
|
247
318
|
'item-multiple-inputs': this.multipleInputs,
|
|
319
|
+
'item-focus-indicator-room': slottedIndicatorNeedsRoom,
|
|
248
320
|
'ion-activatable': canActivate,
|
|
249
321
|
'ion-focusable': this.focusable,
|
|
250
322
|
'item-rtl': document.dir === 'rtl',
|
|
251
|
-
})), role: inList ? 'listitem' : null }, h(TagType, Object.assign({ key: '
|
|
323
|
+
})), role: inList ? 'listitem' : null }, h(TagType, Object.assign({ key: 'd42583cf62133b2553de2e7a5357168640bb1834' }, attrs, inheritedAriaAttributes, { class: "item-native", part: "native", disabled: disabled }, clickFn), h("slot", { key: 'bd805ce49946cd52280d1c4b370104ee2472f1e2', name: "start", onSlotchange: this.updateInteractivityOnSlotChange }), h("div", { key: '0de589f9c5c13c31b6c8838566a7ad6f78f81028', class: "item-inner", part: "inner" }, h("div", { key: 'faaf6a39e072fb352753c645736ab1f47c2d7d25', class: "input-wrapper", part: "container" }, h("slot", { key: '70a34c31de29a0786a14b4a7d3156ba8d4d92eb4', onSlotchange: this.updateInteractivityOnSlotChange })), h("slot", { key: 'c8e764b62c5c30f67750896741ca266b741d5999', name: "end", onSlotchange: this.updateInteractivityOnSlotChange }), showDetail && (h("ion-icon", { key: 'af006856d8c4ed69174534261344215957cc751a', icon: detailIcon, lazy: false, class: "item-detail-icon", part: "detail-icon", "aria-hidden": "true", "flip-rtl": detailIcon === chevronForward }))), canActivate && mode === 'md' && h("ion-ripple-effect", { key: 'e5e151bafedd38feb8f71a387a0059aa1e9e90de' }))));
|
|
252
324
|
}
|
|
253
325
|
static get is() { return "ion-item"; }
|
|
254
326
|
static get encapsulation() { return "shadow"; }
|
|
@@ -543,7 +615,8 @@ export class Item {
|
|
|
543
615
|
return {
|
|
544
616
|
"multipleInputs": {},
|
|
545
617
|
"focusable": {},
|
|
546
|
-
"isInteractive": {}
|
|
618
|
+
"isInteractive": {},
|
|
619
|
+
"hasSlottedIndicatorControl": {}
|
|
547
620
|
};
|
|
548
621
|
}
|
|
549
622
|
static get elementRef() { return "el"; }
|
|
@@ -569,3 +642,11 @@ export class Item {
|
|
|
569
642
|
}];
|
|
570
643
|
}
|
|
571
644
|
}
|
|
645
|
+
const isIndicatorControl = (node) => {
|
|
646
|
+
if (node.nodeType !== Node.ELEMENT_NODE) {
|
|
647
|
+
return false;
|
|
648
|
+
}
|
|
649
|
+
const el = node;
|
|
650
|
+
return el.matches(INDICATOR_CONTROL_SELECTOR) || el.querySelector(INDICATOR_CONTROL_SELECTOR) !== null;
|
|
651
|
+
};
|
|
652
|
+
const touchesIndicatorControl = (record) => Array.from(record.addedNodes).some(isIndicatorControl) || Array.from(record.removedNodes).some(isIndicatorControl);
|