voyager-ionic-core 9.0.7 → 9.0.9
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-action-sheet.js +1 -1
- package/components/ion-alert.js +1 -1
- package/components/ion-backdrop.js +1 -1
- package/components/ion-datetime.js +1 -1
- package/components/ion-input.js +1 -1
- package/components/ion-loading.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-popover.js +1 -1
- package/components/ion-router-outlet.js +1 -1
- package/components/ion-select.js +1 -1
- package/components/ion-textarea.js +1 -1
- package/components/p-Bd8hg9FK.js +4 -0
- package/components/{p-DhfJAkyd.js → p-CKTjkpAq.js} +1 -1
- package/components/{p-DnGlmM4u.js → p-CONpe3oX.js} +1 -1
- package/components/{p-DMPsc_0r.js → p-CW84OFQa.js} +1 -1
- package/components/{p-D64Tyi-z.js → p-CYctYy9B.js} +1 -1
- package/components/{p-Dvyv9SMk.js → p-D0nwlsl_.js} +1 -1
- package/components/p-DfDIY2oP.js +4 -0
- package/components/p-SHHIB5F0.js +4 -0
- package/components/p-zQi7R3tS.js +4 -0
- package/css/core.css.map +1 -1
- package/css/ionic.bundle.css.map +1 -1
- package/dist/cjs/{index-DUiuw_fH.js → index-DCfr5hz-.js} +14 -2
- package/dist/cjs/index.cjs.js +3 -3
- package/dist/cjs/ion-app_8.cjs.entry.js +31 -3
- package/dist/cjs/ion-backdrop.cjs.entry.js +5 -1
- package/dist/cjs/ion-datetime.cjs.entry.js +55 -9
- package/dist/cjs/ion-input.cjs.entry.js +13 -13
- package/dist/cjs/ion-modal.cjs.entry.js +31 -81
- package/dist/cjs/ion-nav_2.cjs.entry.js +24 -2
- package/dist/cjs/ion-popover.cjs.entry.js +1 -1
- package/dist/cjs/ion-select_3.cjs.entry.js +12 -12
- package/dist/cjs/ion-textarea.cjs.entry.js +12 -12
- package/dist/cjs/{ios.transition-D85_rHf5.js → ios.transition-Ca6XODo9.js} +1 -1
- package/dist/cjs/{md.transition-Ce9X1WXr.js → md.transition-D8SfNYEp.js} +1 -1
- package/dist/cjs/{slot-mutation-controller-ClDxh3EK.js → slot-mutation-controller-B2OfJEBH.js} +67 -33
- package/dist/collection/components/backdrop/backdrop.js +6 -2
- package/dist/collection/components/datetime/datetime.js +56 -10
- package/dist/collection/components/input/input.ios.css +7 -3
- package/dist/collection/components/input/input.js +11 -11
- package/dist/collection/components/input/input.md.css +28 -15
- package/dist/collection/components/modal/gestures/sheet.js +30 -80
- package/dist/collection/components/nav/nav.js +24 -2
- package/dist/collection/components/router-outlet/router-outlet.js +31 -3
- package/dist/collection/components/select/select.ios.css +7 -3
- package/dist/collection/components/select/select.js +10 -10
- package/dist/collection/components/select/select.md.css +28 -15
- package/dist/collection/components/textarea/textarea.ios.css +7 -3
- package/dist/collection/components/textarea/textarea.js +10 -10
- package/dist/collection/components/textarea/textarea.md.css +28 -17
- package/dist/collection/utils/forms/index.js +1 -1
- package/dist/collection/utils/forms/{start-container-controller.js → start-end-container-controller.js} +66 -32
- package/dist/collection/utils/transition/index.js +11 -0
- package/dist/docs.json +3 -3
- package/dist/esm/{index-BBASprVu.js → index-z9vdZN7I.js} +14 -3
- package/dist/esm/index.js +3 -3
- package/dist/esm/ion-app_8.entry.js +31 -3
- package/dist/esm/ion-backdrop.entry.js +5 -1
- package/dist/esm/ion-datetime.entry.js +55 -9
- package/dist/esm/ion-input.entry.js +13 -13
- package/dist/esm/ion-modal.entry.js +31 -81
- package/dist/esm/ion-nav_2.entry.js +24 -2
- package/dist/esm/ion-popover.entry.js +1 -1
- package/dist/esm/ion-select_3.entry.js +12 -12
- package/dist/esm/ion-textarea.entry.js +12 -12
- package/dist/esm/{ios.transition-B8netzsb.js → ios.transition-BkUGiDUz.js} +1 -1
- package/dist/esm/{md.transition-DNCSSiXk.js → md.transition-B6hlrWjR.js} +1 -1
- package/dist/esm/{slot-mutation-controller-hAS2uJ7c.js → slot-mutation-controller-Btpr9-9O.js} +67 -33
- package/dist/html.html-data.json +1 -1
- package/dist/ionic/index.esm.js +1 -1
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-293cb1d5.entry.js +4 -0
- package/dist/ionic/p-61321cfa.entry.js +4 -0
- package/dist/ionic/p-657239f5.entry.js +4 -0
- package/dist/ionic/p-67f004eb.entry.js +4 -0
- package/dist/ionic/{p-230f510c.entry.js → p-76c189e7.entry.js} +1 -1
- package/dist/ionic/p-94f1f7df.entry.js +4 -0
- package/dist/ionic/{p-Bppj2PE0.js → p-BZTgGzGK.js} +1 -1
- package/dist/ionic/p-BpED_ep_.js +4 -0
- package/dist/ionic/{p-Bnc1L7Rk.js → p-CgcGX08t.js} +1 -1
- package/dist/ionic/p-Dk4Mdaru.js +4 -0
- package/dist/ionic/{p-4ed3c460.entry.js → p-a7d368b5.entry.js} +1 -1
- package/dist/ionic/p-e8e69c21.entry.js +4 -0
- package/dist/ionic/{p-af56702e.entry.js → p-f2116a54.entry.js} +1 -1
- package/dist/types/components/backdrop/backdrop.d.ts +4 -0
- package/dist/types/components/datetime/datetime.d.ts +20 -3
- package/dist/types/components/input/input.d.ts +2 -1
- package/dist/types/components/nav/nav.d.ts +1 -0
- package/dist/types/components/router-outlet/router-outlet.d.ts +1 -0
- package/dist/types/components/select/select.d.ts +2 -1
- package/dist/types/components/textarea/textarea.d.ts +2 -1
- package/dist/types/components.d.ts +3 -3
- package/dist/types/utils/forms/index.d.ts +1 -1
- package/dist/types/utils/forms/start-end-container-controller.d.ts +44 -0
- package/dist/types/utils/transition/index.d.ts +6 -0
- package/hydrate/index.js +254 -159
- package/hydrate/index.mjs +254 -159
- package/package.json +4 -4
- package/components/p-C7r5Gja6.js +0 -4
- package/components/p-CtiO6_sC.js +0 -4
- package/components/p-TLtgwTFF.js +0 -4
- package/components/p-Uk13YgEc.js +0 -4
- package/dist/ionic/p-27d96a9b.entry.js +0 -4
- package/dist/ionic/p-4d785062.entry.js +0 -4
- package/dist/ionic/p-6317d620.entry.js +0 -4
- package/dist/ionic/p-8b79061e.entry.js +0 -4
- package/dist/ionic/p-B8IuTu_r.js +0 -4
- package/dist/ionic/p-DQXtveHv.js +0 -4
- package/dist/ionic/p-c81ec0e8.entry.js +0 -4
- package/dist/ionic/p-d9a3dfd1.entry.js +0 -4
- package/dist/types/utils/forms/start-container-controller.d.ts +0 -34
package/dist/cjs/{slot-mutation-controller-ClDxh3EK.js → slot-mutation-controller-B2OfJEBH.js}
RENAMED
|
@@ -167,10 +167,19 @@ const SKIP_LABEL_TRANSITION_CLASS = 'skip-label-transition';
|
|
|
167
167
|
*/
|
|
168
168
|
const START_CONTAINER_ADJUSTMENT_VAR = '--internal-start-container-adjustment';
|
|
169
169
|
/**
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
170
|
+
* Set on the host as an inline style. The component stylesheets declare
|
|
171
|
+
* this property and read it with `var()`, so it must stay in sync with
|
|
172
|
+
* the `--internal-start-end-container-width` references in those files.
|
|
173
|
+
* Renaming it here requires renaming it there too.
|
|
174
|
+
*/
|
|
175
|
+
const START_END_CONTAINER_WIDTH_VAR = '--internal-start-end-container-width';
|
|
176
|
+
/**
|
|
177
|
+
* A utility to measure the start and end containers of form components
|
|
178
|
+
* (input, textarea, select) with `fill="outline"` and write the results
|
|
179
|
+
* to the host. The start container width offsets floating/stacked labels
|
|
180
|
+
* so they line up with the outline border. The combined start and end
|
|
181
|
+
* container width lets the floating label size itself to the full width
|
|
182
|
+
* of the outline while still being positioned relative to the control.
|
|
174
183
|
*
|
|
175
184
|
* Measurement is deferred with raf() to avoid forcing layout during
|
|
176
185
|
* component updates, and a ResizeObserver watches for dynamic width
|
|
@@ -179,29 +188,36 @@ const START_CONTAINER_ADJUSTMENT_VAR = '--internal-start-container-adjustment';
|
|
|
179
188
|
* @internal
|
|
180
189
|
* @param el - The host element (ion-input, ion-textarea, or ion-select).
|
|
181
190
|
* @param getStartContainer - A callback that returns the start container element.
|
|
182
|
-
* @param
|
|
191
|
+
* @param getEndContainer - A callback that returns the end container element.
|
|
192
|
+
* @param shouldMeasure - A callback that determines whether the containers should be measured.
|
|
183
193
|
*/
|
|
184
|
-
const
|
|
194
|
+
const createStartEndContainerController = (el, getStartContainer, getEndContainer, shouldMeasure) => {
|
|
185
195
|
let resizeObserver;
|
|
186
|
-
let
|
|
196
|
+
let observedStartContainer;
|
|
197
|
+
let observedEndContainer;
|
|
187
198
|
let measurementRaf;
|
|
188
199
|
let transitionRaf;
|
|
189
200
|
let appliedAdjustment;
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
201
|
+
let appliedStartEndWidth;
|
|
202
|
+
/*
|
|
203
|
+
* Round to one decimal place so fractional widths produce a stable
|
|
204
|
+
* label offset and width when a container is added, removed, or resized.
|
|
205
|
+
*/
|
|
206
|
+
const roundWidth = (width) => Math.round(width * 10) / 10;
|
|
207
|
+
const measureContainers = () => {
|
|
208
|
+
const startContainer = getStartContainer();
|
|
209
|
+
if (!startContainer || !shouldMeasure()) {
|
|
210
|
+
return { adjustment: '', startEndWidth: '' };
|
|
194
211
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
* label offset when the start container is added, removed, or resized.
|
|
198
|
-
*/
|
|
199
|
-
const startContainerWidth = startSlot.getBoundingClientRect().width;
|
|
200
|
-
const roundedWidth = Math.round(startContainerWidth * 10) / 10;
|
|
212
|
+
const startContainerWidth = roundWidth(startContainer.getBoundingClientRect().width);
|
|
213
|
+
const endContainerWidth = roundWidth(getEndContainer()?.getBoundingClientRect().width ?? 0);
|
|
201
214
|
const sign = dir.isRTL(el) ? '' : '-';
|
|
202
|
-
return
|
|
215
|
+
return {
|
|
216
|
+
adjustment: startContainerWidth ? `${sign}${startContainerWidth}px` : '0px',
|
|
217
|
+
startEndWidth: `${roundWidth(startContainerWidth + endContainerWidth)}px`,
|
|
218
|
+
};
|
|
203
219
|
};
|
|
204
|
-
const
|
|
220
|
+
const scheduleMeasurement = () => {
|
|
205
221
|
// Return if there is already a measurement queued to avoid a flicker
|
|
206
222
|
if (measurementRaf !== undefined) {
|
|
207
223
|
return;
|
|
@@ -212,7 +228,17 @@ const createStartContainerController = (el, getStartContainer, shouldApplyAdjust
|
|
|
212
228
|
*/
|
|
213
229
|
measurementRaf = helpers.raf(() => {
|
|
214
230
|
measurementRaf = undefined;
|
|
215
|
-
const adjustment =
|
|
231
|
+
const { adjustment, startEndWidth } = measureContainers();
|
|
232
|
+
/*
|
|
233
|
+
* The width only changes how much of the label fits, so it can
|
|
234
|
+
* animate. The transition is not suppressed here because the end
|
|
235
|
+
* container can change size while the label is floating, such as
|
|
236
|
+
* when async content loads, and that should not cut the float short.
|
|
237
|
+
*/
|
|
238
|
+
if (startEndWidth !== appliedStartEndWidth) {
|
|
239
|
+
el.style.setProperty(START_END_CONTAINER_WIDTH_VAR, startEndWidth);
|
|
240
|
+
appliedStartEndWidth = startEndWidth;
|
|
241
|
+
}
|
|
216
242
|
// Only suppress the label transition when the label has to move
|
|
217
243
|
if (adjustment !== appliedAdjustment) {
|
|
218
244
|
// Discard a removal queued by an earlier change
|
|
@@ -235,29 +261,36 @@ const createStartContainerController = (el, getStartContainer, shouldApplyAdjust
|
|
|
235
261
|
if (resizeObserver) {
|
|
236
262
|
resizeObserver.disconnect();
|
|
237
263
|
resizeObserver = undefined;
|
|
238
|
-
|
|
264
|
+
observedStartContainer = undefined;
|
|
265
|
+
observedEndContainer = undefined;
|
|
239
266
|
}
|
|
240
267
|
};
|
|
241
268
|
const setupResizeObserver = () => {
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
269
|
+
const startContainer = getStartContainer();
|
|
270
|
+
const endContainer = getEndContainer();
|
|
271
|
+
// Stop watching once the measurements no longer apply
|
|
272
|
+
if (!startContainer || !shouldMeasure()) {
|
|
245
273
|
disconnectResizeObserver();
|
|
246
274
|
return;
|
|
247
275
|
}
|
|
248
|
-
// Return when prerendering or already watching
|
|
249
|
-
if (typeof ResizeObserver !== 'function' ||
|
|
276
|
+
// Return when prerendering or already watching these containers
|
|
277
|
+
if (typeof ResizeObserver !== 'function' ||
|
|
278
|
+
(resizeObserver && observedStartContainer === startContainer && observedEndContainer === endContainer)) {
|
|
250
279
|
return;
|
|
251
280
|
}
|
|
252
281
|
disconnectResizeObserver();
|
|
253
282
|
resizeObserver = new ResizeObserver(() => {
|
|
254
|
-
|
|
283
|
+
scheduleMeasurement();
|
|
255
284
|
});
|
|
256
|
-
|
|
257
|
-
resizeObserver.observe(
|
|
285
|
+
observedStartContainer = startContainer;
|
|
286
|
+
resizeObserver.observe(startContainer);
|
|
287
|
+
if (endContainer) {
|
|
288
|
+
observedEndContainer = endContainer;
|
|
289
|
+
resizeObserver.observe(endContainer);
|
|
290
|
+
}
|
|
258
291
|
};
|
|
259
|
-
const
|
|
260
|
-
|
|
292
|
+
const calculateContainerWidths = () => {
|
|
293
|
+
scheduleMeasurement();
|
|
261
294
|
};
|
|
262
295
|
const destroy = () => {
|
|
263
296
|
disconnectResizeObserver();
|
|
@@ -273,9 +306,10 @@ const createStartContainerController = (el, getStartContainer, shouldApplyAdjust
|
|
|
273
306
|
}
|
|
274
307
|
// Re-apply from scratch if the element is connected again
|
|
275
308
|
appliedAdjustment = undefined;
|
|
309
|
+
appliedStartEndWidth = undefined;
|
|
276
310
|
};
|
|
277
311
|
return {
|
|
278
|
-
|
|
312
|
+
calculateContainerWidths,
|
|
279
313
|
destroy,
|
|
280
314
|
};
|
|
281
315
|
};
|
|
@@ -461,5 +495,5 @@ const createSlotMutationController = (el, slotName, mutationCallback) => {
|
|
|
461
495
|
exports.createClickController = createClickController;
|
|
462
496
|
exports.createNotchController = createNotchController;
|
|
463
497
|
exports.createSlotMutationController = createSlotMutationController;
|
|
464
|
-
exports.
|
|
498
|
+
exports.createStartEndContainerController = createStartEndContainerController;
|
|
465
499
|
exports.getSlottedClickContent = getSlottedClickContent;
|
|
@@ -7,6 +7,10 @@ export class Backdrop {
|
|
|
7
7
|
constructor() {
|
|
8
8
|
/**
|
|
9
9
|
* If `true`, the backdrop will be visible.
|
|
10
|
+
* This property controls whether or not the backdrop's
|
|
11
|
+
* default background is shown. It does not control whether
|
|
12
|
+
* or not the backdrop blocks interaction, and it does not
|
|
13
|
+
* override a custom background set on `ion-backdrop`.
|
|
10
14
|
*/
|
|
11
15
|
this.visible = true;
|
|
12
16
|
/**
|
|
@@ -32,7 +36,7 @@ export class Backdrop {
|
|
|
32
36
|
}
|
|
33
37
|
render() {
|
|
34
38
|
const mode = getIonMode(this);
|
|
35
|
-
return (h(Host, { key: '
|
|
39
|
+
return (h(Host, { key: '14141148f4a22371b8ad7cb9dcdc32398ec109aa', "aria-hidden": "true", class: {
|
|
36
40
|
[mode]: true,
|
|
37
41
|
'backdrop-hide': !this.visible,
|
|
38
42
|
'backdrop-no-tappable': !this.tappable,
|
|
@@ -66,7 +70,7 @@ export class Backdrop {
|
|
|
66
70
|
"optional": false,
|
|
67
71
|
"docs": {
|
|
68
72
|
"tags": [],
|
|
69
|
-
"text": "If `true`, the backdrop will be visible."
|
|
73
|
+
"text": "If `true`, the backdrop will be visible.\nThis property controls whether or not the backdrop's\ndefault background is shown. It does not control whether\nor not the backdrop blocks interaction, and it does not\noverride a custom background set on `ion-backdrop`."
|
|
70
74
|
},
|
|
71
75
|
"getter": false,
|
|
72
76
|
"setter": false,
|
|
@@ -56,6 +56,13 @@ import { checkForPresentationFormatMismatch, warnIfTimeZoneProvided } from "./ut
|
|
|
56
56
|
export class Datetime {
|
|
57
57
|
constructor() {
|
|
58
58
|
this.inputId = `ion-dt-${datetimeIds++}`;
|
|
59
|
+
/**
|
|
60
|
+
* Set when the user selects a value, cleared when it's confirmed
|
|
61
|
+
* or `processValue` runs (`reset()`, or `value` being set to a
|
|
62
|
+
* non-empty value). The overlay present listener only discards a
|
|
63
|
+
* selection while this is set.
|
|
64
|
+
*/
|
|
65
|
+
this.hasUnconfirmedSelection = false;
|
|
59
66
|
this.prevPresentation = null;
|
|
60
67
|
this.showMonthAndYear = false;
|
|
61
68
|
this.activeParts = [];
|
|
@@ -219,6 +226,7 @@ export class Datetime {
|
|
|
219
226
|
if (popoverOrModal) {
|
|
220
227
|
popoverOrModal.dismiss(undefined, role);
|
|
221
228
|
}
|
|
229
|
+
return popoverOrModal !== null;
|
|
222
230
|
};
|
|
223
231
|
this.setWorkingParts = (parts) => {
|
|
224
232
|
this.workingParts = {
|
|
@@ -233,6 +241,7 @@ export class Datetime {
|
|
|
233
241
|
if (this.readonly) {
|
|
234
242
|
return;
|
|
235
243
|
}
|
|
244
|
+
this.hasUnconfirmedSelection = true;
|
|
236
245
|
const { multiple, minParts, maxParts, activeParts } = this;
|
|
237
246
|
/**
|
|
238
247
|
* When setting the active parts, it is possible
|
|
@@ -653,7 +662,8 @@ export class Datetime {
|
|
|
653
662
|
this.loadTimeout = undefined;
|
|
654
663
|
}
|
|
655
664
|
};
|
|
656
|
-
this.processValue = (value) => {
|
|
665
|
+
this.processValue = (value, animated = true) => {
|
|
666
|
+
this.hasUnconfirmedSelection = false;
|
|
657
667
|
const hasValue = value !== null && value !== undefined && value !== '' && (!Array.isArray(value) || value.length > 0);
|
|
658
668
|
const valueToProcess = hasValue ? parseDate(value) : this.defaultParts;
|
|
659
669
|
const { minParts, maxParts, workingParts, el } = this;
|
|
@@ -716,13 +726,14 @@ export class Datetime {
|
|
|
716
726
|
const didChangeMonth = (month !== undefined && month !== workingParts.month) || (year !== undefined && year !== workingParts.year);
|
|
717
727
|
const bodyIsVisible = el.classList.contains('datetime-ready');
|
|
718
728
|
const { isGridStyle, showMonthAndYear } = this;
|
|
719
|
-
if (isGridStyle && didChangeMonth && bodyIsVisible && !showMonthAndYear) {
|
|
729
|
+
if (isGridStyle && didChangeMonth && bodyIsVisible && !showMonthAndYear && animated) {
|
|
720
730
|
/**
|
|
721
731
|
* Only animate if:
|
|
722
732
|
* 1. We're using grid style (wheel style pickers should just jump to new value)
|
|
723
733
|
* 2. The month and/or year actually changed, and both are defined (otherwise there's nothing to animate to)
|
|
724
734
|
* 3. The calendar body is visible (prevents animation when in collapsed datetime-button, for example)
|
|
725
735
|
* 4. The month/year picker is not open (since you wouldn't see the animation anyway)
|
|
736
|
+
* 5. The caller did not opt out (e.g. the parent overlay is still hidden)
|
|
726
737
|
*/
|
|
727
738
|
this.animateToDate(targetValue);
|
|
728
739
|
}
|
|
@@ -893,6 +904,7 @@ export class Datetime {
|
|
|
893
904
|
this.setValue(convertDataToISO(activeParts));
|
|
894
905
|
}
|
|
895
906
|
}
|
|
907
|
+
this.hasUnconfirmedSelection = false;
|
|
896
908
|
if (closeOverlay) {
|
|
897
909
|
this.closeParentOverlay(CONFIRM_ROLE);
|
|
898
910
|
}
|
|
@@ -908,17 +920,22 @@ export class Datetime {
|
|
|
908
920
|
this.processValue(startDate);
|
|
909
921
|
}
|
|
910
922
|
/**
|
|
911
|
-
* Emits the ionCancel event
|
|
912
|
-
*
|
|
913
|
-
*
|
|
923
|
+
* Emits the ionCancel event, discards
|
|
924
|
+
* any unconfirmed selection, and
|
|
925
|
+
* optionally closes the popover or
|
|
926
|
+
* modal that the datetime was
|
|
914
927
|
* presented in.
|
|
915
928
|
*
|
|
916
929
|
* @param closeOverlay If `true`, closes the parent overlay. Defaults to `false`.
|
|
917
930
|
*/
|
|
918
931
|
async cancel(closeOverlay = false) {
|
|
919
932
|
this.ionCancel.emit();
|
|
920
|
-
|
|
921
|
-
|
|
933
|
+
/**
|
|
934
|
+
* When a parent overlay closes, the present listener discards
|
|
935
|
+
* the selection on the next open. Otherwise discard it now.
|
|
936
|
+
*/
|
|
937
|
+
if (!closeOverlay || !this.closeParentOverlay(CANCEL_ROLE)) {
|
|
938
|
+
this.processValue(this.value);
|
|
922
939
|
}
|
|
923
940
|
}
|
|
924
941
|
/**
|
|
@@ -940,6 +957,7 @@ export class Datetime {
|
|
|
940
957
|
this.loadTimeout = setTimeout(() => {
|
|
941
958
|
this.ensureReadyIfVisible();
|
|
942
959
|
}, 100);
|
|
960
|
+
this.initializeOverlayPresentListener();
|
|
943
961
|
}
|
|
944
962
|
disconnectedCallback() {
|
|
945
963
|
if (this.clearFocusVisible) {
|
|
@@ -947,6 +965,34 @@ export class Datetime {
|
|
|
947
965
|
this.clearFocusVisible = undefined;
|
|
948
966
|
}
|
|
949
967
|
this.loadTimeoutCleanup();
|
|
968
|
+
if (this.destroyOverlayPresentListener) {
|
|
969
|
+
this.destroyOverlayPresentListener();
|
|
970
|
+
this.destroyOverlayPresentListener = undefined;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* A datetime in a modal or popover stays mounted between
|
|
975
|
+
* presentations, so an unconfirmed selection would still show when
|
|
976
|
+
* it reopens. Discarding on present rather than dismiss lets app
|
|
977
|
+
* code that runs on dismiss still confirm or reset the selection,
|
|
978
|
+
* whatever order its listeners were added in.
|
|
979
|
+
*/
|
|
980
|
+
initializeOverlayPresentListener() {
|
|
981
|
+
const overlay = this.el.closest('ion-modal, ion-popover');
|
|
982
|
+
if (!overlay) {
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
const eventName = overlay.tagName === 'ION-MODAL' ? 'ionModalWillPresent' : 'ionPopoverWillPresent';
|
|
986
|
+
const onPresent = (ev) => {
|
|
987
|
+
// Ignore nested overlays and skip when there's nothing to discard.
|
|
988
|
+
if (ev.target !== overlay || !this.hasUnconfirmedSelection) {
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
// The overlay is still hidden, so skip the month animation.
|
|
992
|
+
this.processValue(this.value, false);
|
|
993
|
+
};
|
|
994
|
+
overlay.addEventListener(eventName, onPresent);
|
|
995
|
+
this.destroyOverlayPresentListener = () => overlay.removeEventListener(eventName, onPresent);
|
|
950
996
|
}
|
|
951
997
|
initializeListeners() {
|
|
952
998
|
this.initializeCalendarListener();
|
|
@@ -1829,7 +1875,7 @@ export class Datetime {
|
|
|
1829
1875
|
const hasDatePresentation = presentation === 'date' || presentation === 'date-time' || presentation === 'time-date';
|
|
1830
1876
|
const hasWheelVariant = hasDatePresentation && preferWheel;
|
|
1831
1877
|
renderHiddenInput(true, el, name, formatValue(value), disabled);
|
|
1832
|
-
return (h(Host, { key: '
|
|
1878
|
+
return (h(Host, { key: 'e9b99bb5610fc7fe879200a56dcc865c6e2f1404', "aria-disabled": disabled ? 'true' : null, onFocus: this.onFocus, onBlur: this.onBlur, class: {
|
|
1833
1879
|
...createColorClasses(color, {
|
|
1834
1880
|
[mode]: true,
|
|
1835
1881
|
['datetime-readonly']: readonly,
|
|
@@ -1841,7 +1887,7 @@ export class Datetime {
|
|
|
1841
1887
|
[`datetime-prefer-wheel`]: hasWheelVariant,
|
|
1842
1888
|
[`datetime-grid`]: isGridStyle,
|
|
1843
1889
|
}),
|
|
1844
|
-
} }, h("div", { key: '
|
|
1890
|
+
} }, h("div", { key: 'a5e61be2989468dc24ec8fb394116d9339b7824c', class: "intersection-tracker", ref: (el) => (this.intersectionTrackerRef = el) }), this.renderDatetime(mode)));
|
|
1845
1891
|
}
|
|
1846
1892
|
static get is() { return "ion-datetime"; }
|
|
1847
1893
|
static get encapsulation() { return "shadow"; }
|
|
@@ -2721,7 +2767,7 @@ export class Datetime {
|
|
|
2721
2767
|
"return": "Promise<void>"
|
|
2722
2768
|
},
|
|
2723
2769
|
"docs": {
|
|
2724
|
-
"text": "Emits the ionCancel event and\noptionally closes the popover\
|
|
2770
|
+
"text": "Emits the ionCancel event, discards\nany unconfirmed selection, and\noptionally closes the popover or\nmodal that the datetime was\npresented in.",
|
|
2725
2771
|
"tags": [{
|
|
2726
2772
|
"name": "param",
|
|
2727
2773
|
"text": "closeOverlay If `true`, closes the parent overlay. Defaults to `false`."
|
|
@@ -425,7 +425,11 @@
|
|
|
425
425
|
* would not be able to see what they are typing.
|
|
426
426
|
*/
|
|
427
427
|
max-width: 200px;
|
|
428
|
-
|
|
428
|
+
/**
|
|
429
|
+
* The max-width animates with the transform so the label does not
|
|
430
|
+
* reveal its full floating width while it is still at full size.
|
|
431
|
+
*/
|
|
432
|
+
transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1), transform 150ms cubic-bezier(0.4, 0, 0.2, 1), max-width 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
429
433
|
/**
|
|
430
434
|
* This ensures that double tapping this text
|
|
431
435
|
* clicks the <label> and focuses the input
|
|
@@ -439,8 +443,8 @@
|
|
|
439
443
|
* disable the label transition to prevent the floating label
|
|
440
444
|
* from animating as its position is adjusted.
|
|
441
445
|
*
|
|
442
|
-
* This class is applied by the start container controller,
|
|
443
|
-
* renaming it here requires renaming it there too.
|
|
446
|
+
* This class is applied by the start and end container controller,
|
|
447
|
+
* so renaming it here requires renaming it there too.
|
|
444
448
|
*/
|
|
445
449
|
:host(.skip-label-transition) .label-text-wrapper {
|
|
446
450
|
transition: none;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* (C) Ionic http://ionicframework.com - MIT License
|
|
3
3
|
*/
|
|
4
4
|
import { Build, Host, forceUpdate, h, } from "@stencil/core";
|
|
5
|
-
import { createClickController, createNotchController,
|
|
5
|
+
import { createClickController, createNotchController, createStartEndContainerController, checkInvalidState, } from "../../utils/forms/index";
|
|
6
6
|
import { inheritAriaAttributes, debounceEvent, inheritAttributes, componentOnReady } from "../../utils/helpers";
|
|
7
7
|
import { createSlotMutationController } from "../../utils/slot-mutation-controller";
|
|
8
8
|
import { createColorClasses, hostContext } from "../../utils/theme";
|
|
@@ -238,16 +238,16 @@ export class Input {
|
|
|
238
238
|
connectedCallback() {
|
|
239
239
|
const { el } = this;
|
|
240
240
|
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => {
|
|
241
|
-
this.
|
|
241
|
+
this.startEndContainerController?.calculateContainerWidths();
|
|
242
242
|
this.setSlottedLabelId();
|
|
243
243
|
forceUpdate(this);
|
|
244
244
|
});
|
|
245
245
|
this.setSlottedLabelId();
|
|
246
246
|
this.notchController = createNotchController(el, () => this.notchSpacerEl, () => this.labelSlot);
|
|
247
|
-
this.
|
|
247
|
+
this.startEndContainerController = createStartEndContainerController(el, () => this.startContainerEl, () => this.endContainerEl, () => {
|
|
248
248
|
return Build.isBrowser && getIonMode(this) === 'md' && this.fill === 'outline';
|
|
249
249
|
});
|
|
250
|
-
this.
|
|
250
|
+
this.startEndContainerController.calculateContainerWidths();
|
|
251
251
|
this.clickController = createClickController(el, () => this.nativeInput);
|
|
252
252
|
// Watch for class changes to update validation state
|
|
253
253
|
if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
|
|
@@ -286,7 +286,7 @@ export class Input {
|
|
|
286
286
|
}
|
|
287
287
|
componentDidRender() {
|
|
288
288
|
this.notchController?.calculateNotchWidth();
|
|
289
|
-
this.
|
|
289
|
+
this.startEndContainerController?.calculateContainerWidths();
|
|
290
290
|
}
|
|
291
291
|
disconnectedCallback() {
|
|
292
292
|
if (Build.isBrowser) {
|
|
@@ -302,9 +302,9 @@ export class Input {
|
|
|
302
302
|
this.notchController.destroy();
|
|
303
303
|
this.notchController = undefined;
|
|
304
304
|
}
|
|
305
|
-
if (this.
|
|
306
|
-
this.
|
|
307
|
-
this.
|
|
305
|
+
if (this.startEndContainerController) {
|
|
306
|
+
this.startEndContainerController.destroy();
|
|
307
|
+
this.startEndContainerController = undefined;
|
|
308
308
|
}
|
|
309
309
|
// Clean up validation observer to prevent memory leaks
|
|
310
310
|
if (this.validationObserver) {
|
|
@@ -524,7 +524,7 @@ export class Input {
|
|
|
524
524
|
* the input has a value or is focused.
|
|
525
525
|
*/
|
|
526
526
|
const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus));
|
|
527
|
-
return (h(Host, { key: '
|
|
527
|
+
return (h(Host, { key: '0d03d37173639105b19083c0b44da25d9115a449', class: createColorClasses(this.color, {
|
|
528
528
|
[mode]: true,
|
|
529
529
|
'has-value': hasValue,
|
|
530
530
|
'has-focus': hasFocus,
|
|
@@ -535,14 +535,14 @@ export class Input {
|
|
|
535
535
|
'in-item': inItem,
|
|
536
536
|
'in-item-color': hostContext('ion-item.ion-color', this.el),
|
|
537
537
|
'input-disabled': disabled,
|
|
538
|
-
}) }, h("label", { key: '
|
|
538
|
+
}) }, h("label", { key: '1d62890b6a54229fccae0832eebe92e546e6e53d', class: "input-wrapper", htmlFor: inputId, onClick: this.onLabelClick }, hasOutlineFill && this.renderOutlineContainer(), h("div", { key: 'b1ceeb7666678569f6479729d556a29b8e549ce5', class: "input-start", ref: (el) => (this.startContainerEl = el) }, h("slot", { key: '1af55c89b1390ff9063a6c6d49a1e8bd01c7c78e', name: "start" })), h("div", { key: '6df6bc5521352ca87aeb400d169b0c9129521249', class: "input-control" }, this.renderLabel(), h("div", { key: '55d2b3a235332d7fbf61356b13052b5c9cda01de', class: "native-wrapper", onClick: this.onLabelClick }, h("input", { key: 'a938cb7ef2e87bde9da310817e1a48e7a5c67bc3', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect ? 'on' : 'off', autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.isInvalid ? 'true' : undefined, "aria-labelledby": this.getLabelledById(), ...this.inheritedAttributes }))), h("div", { key: 'c99685e001ee133220d74cfa877a72063d46433c', class: "input-end", ref: (el) => (this.endContainerEl = el) }, this.clearInput && !readonly && !disabled && (h("button", { key: '0e0e154182cca8b5367c1586b3c8c5b20b38006b', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
|
|
539
539
|
/**
|
|
540
540
|
* This prevents mobile browsers from
|
|
541
541
|
* blurring the input when the clear
|
|
542
542
|
* button is activated.
|
|
543
543
|
*/
|
|
544
544
|
ev.preventDefault();
|
|
545
|
-
}, onClick: this.clearTextInput }, h("ion-icon", { key: '
|
|
545
|
+
}, onClick: this.clearTextInput }, h("ion-icon", { key: '655e8d25940dbfb96e7e83b6e89575337e3e91d3', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '6104c04ae34a26da62095de70c752efb2a0417ab', name: "end" })), shouldRenderHighlight && h("div", { key: 'c85cd8e07397e0bbd1e5a120014d83f391dfbc4c', class: "input-highlight" })), this.renderBottomContent()));
|
|
546
546
|
}
|
|
547
547
|
static get is() { return "ion-input"; }
|
|
548
548
|
static get encapsulation() { return "scoped"; }
|
|
@@ -425,7 +425,11 @@
|
|
|
425
425
|
* would not be able to see what they are typing.
|
|
426
426
|
*/
|
|
427
427
|
max-width: 200px;
|
|
428
|
-
|
|
428
|
+
/**
|
|
429
|
+
* The max-width animates with the transform so the label does not
|
|
430
|
+
* reveal its full floating width while it is still at full size.
|
|
431
|
+
*/
|
|
432
|
+
transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1), transform 150ms cubic-bezier(0.4, 0, 0.2, 1), max-width 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
429
433
|
/**
|
|
430
434
|
* This ensures that double tapping this text
|
|
431
435
|
* clicks the <label> and focuses the input
|
|
@@ -439,8 +443,8 @@
|
|
|
439
443
|
* disable the label transition to prevent the floating label
|
|
440
444
|
* from animating as its position is adjusted.
|
|
441
445
|
*
|
|
442
|
-
* This class is applied by the start container controller,
|
|
443
|
-
* renaming it here requires renaming it there too.
|
|
446
|
+
* This class is applied by the start and end container controller,
|
|
447
|
+
* so renaming it here requires renaming it there too.
|
|
444
448
|
*/
|
|
445
449
|
:host(.skip-label-transition) .label-text-wrapper {
|
|
446
450
|
transition: none;
|
|
@@ -946,12 +950,20 @@
|
|
|
946
950
|
--padding-end: 16px;
|
|
947
951
|
/**
|
|
948
952
|
* The measured width of the start container, as a negative offset
|
|
949
|
-
* in LTR and a positive one in RTL. Written to the host by the
|
|
950
|
-
*
|
|
953
|
+
* in LTR and a positive one in RTL. Written to the host by the start
|
|
954
|
+
* and end container controller, so renaming it here requires renaming
|
|
951
955
|
* it there too. Declared with a default so the label is positioned
|
|
952
956
|
* correctly before the first measurement runs.
|
|
953
957
|
*/
|
|
954
958
|
--internal-start-container-adjustment: 0px;
|
|
959
|
+
/**
|
|
960
|
+
* The measured combined width of the start and end containers.
|
|
961
|
+
* Written to the host by the start and end container controller, so
|
|
962
|
+
* renaming it here requires renaming it there too. Declared with a
|
|
963
|
+
* default so the floating label falls back to the width of the
|
|
964
|
+
* control before the first measurement runs.
|
|
965
|
+
*/
|
|
966
|
+
--internal-start-end-container-width: 0px;
|
|
955
967
|
min-height: 56px;
|
|
956
968
|
}
|
|
957
969
|
|
|
@@ -1007,11 +1019,12 @@
|
|
|
1007
1019
|
}
|
|
1008
1020
|
|
|
1009
1021
|
/**
|
|
1010
|
-
* The
|
|
1011
|
-
* the
|
|
1012
|
-
*
|
|
1022
|
+
* The label is positioned relative to the control whether it is resting
|
|
1023
|
+
* over the input or floating in the notch, so its max-width always resolves
|
|
1024
|
+
* against the same width. This lets the max-width animate between the
|
|
1025
|
+
* resting and floating values without jumping.
|
|
1013
1026
|
*/
|
|
1014
|
-
:host(.input-fill-outline
|
|
1027
|
+
:host(.input-fill-outline) .input-control {
|
|
1015
1028
|
position: relative;
|
|
1016
1029
|
}
|
|
1017
1030
|
|
|
@@ -1020,9 +1033,8 @@
|
|
|
1020
1033
|
transform-origin: left top;
|
|
1021
1034
|
position: absolute;
|
|
1022
1035
|
/**
|
|
1023
|
-
*
|
|
1024
|
-
* excludes the start and end slots.
|
|
1025
|
-
* containing block becomes the wrapper and the rule below takes over.
|
|
1036
|
+
* This is the width of the text input, which already
|
|
1037
|
+
* excludes the start and end slots.
|
|
1026
1038
|
*/
|
|
1027
1039
|
max-width: 100%;
|
|
1028
1040
|
}
|
|
@@ -1055,10 +1067,11 @@
|
|
|
1055
1067
|
margin-top: 0;
|
|
1056
1068
|
margin-bottom: 0;
|
|
1057
1069
|
/**
|
|
1058
|
-
* Label text should not extend
|
|
1059
|
-
*
|
|
1070
|
+
* Label text should not extend beyond the bounds of the input.
|
|
1071
|
+
* The control excludes the start and end slots, but the notch spans
|
|
1072
|
+
* the full width of the outline, so their width is added back.
|
|
1060
1073
|
*/
|
|
1061
|
-
max-width: calc((100%
|
|
1074
|
+
max-width: calc((100% + var(--internal-start-end-container-width) - 8px) / 0.75);
|
|
1062
1075
|
}
|
|
1063
1076
|
|
|
1064
1077
|
:host(.input-fill-outline) .input-outline-container {
|