Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
security-events: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: github/codeql-action/init@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
- uses: github/codeql-action/init@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
with:
languages: javascript
- uses: github/codeql-action/analyze@24c54180a607b1449ed407dd24f251e4e9147c8d # v4.38.3
- uses: github/codeql-action/analyze@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [9.0.9](https://github.com/ionic-team/ionic-framework/compare/v9.0.8...v9.0.9) (2026-10-21)

### Bug Fixes

* **angular:** import tab proxies directly in standalone tabs ([#31596](https://github.com/ionic-team/ionic-framework/issues/31596)) ([cb963f8](https://github.com/ionic-team/ionic-framework/commit/cb963f886ee08ce3abf25d6cb4e33c5cc3ac0003)), closes [#31594](https://github.com/ionic-team/ionic-framework/issues/31594)
* **datetime:** discard unconfirmed selection on cancel ([#31558](https://github.com/ionic-team/ionic-framework/issues/31558)) ([cecd3af](https://github.com/ionic-team/ionic-framework/commit/cecd3afdaea91a2e5f4ada6cd4aaedf6f436e6c9)), closes [#30777](https://github.com/ionic-team/ionic-framework/issues/30777) [#27975](https://github.com/ionic-team/ionic-framework/issues/27975) [#27976](https://github.com/ionic-team/ionic-framework/issues/27976)
* **input, select, textarea:** prevent floating label from overflowing while it floats ([#31567](https://github.com/ionic-team/ionic-framework/issues/31567)) ([ecd6f5a](https://github.com/ionic-team/ionic-framework/commit/ecd6f5a7c0ed987f4357b7a0e3954d9291be170d))
* **nav:** defer non-animated swipe back until the gesture completes ([#31552](https://github.com/ionic-team/ionic-framework/issues/31552)) ([5f3f5fc](https://github.com/ionic-team/ionic-framework/commit/5f3f5fcae2bc83cb4301bc6e47c7e0b64f6d3d76)), closes [#29793](https://github.com/ionic-team/ionic-framework/issues/29793)
* **vue-router:** keep back working after replacing a page in tabs ([#31583](https://github.com/ionic-team/ionic-framework/issues/31583)) ([86390f6](https://github.com/ionic-team/ionic-framework/commit/86390f6deab23b58498c3b9ae782f9e0fc5a4e29)), closes [#30617](https://github.com/ionic-team/ionic-framework/issues/30617) [#31142](https://github.com/ionic-team/ionic-framework/issues/31142)


## [9.0.8](https://github.com/ionic-team/ionic-framework/compare/v9.0.7...v9.0.8) (2026-10-14)

### Bug Fixes
Expand Down
9 changes: 9 additions & 0 deletions core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## [9.0.9](https://github.com/ionic-team/ionic-framework/compare/v9.0.8...v9.0.9) (2026-10-21)

### Bug Fixes

* **datetime:** discard unconfirmed selection on cancel ([#31558](https://github.com/ionic-team/ionic-framework/issues/31558)) ([cecd3af](https://github.com/ionic-team/ionic-framework/commit/cecd3afdaea91a2e5f4ada6cd4aaedf6f436e6c9)), closes [#30777](https://github.com/ionic-team/ionic-framework/issues/30777) [#27975](https://github.com/ionic-team/ionic-framework/issues/27975) [#27976](https://github.com/ionic-team/ionic-framework/issues/27976)
* **input, select, textarea:** prevent floating label from overflowing while it floats ([#31567](https://github.com/ionic-team/ionic-framework/issues/31567)) ([ecd6f5a](https://github.com/ionic-team/ionic-framework/commit/ecd6f5a7c0ed987f4357b7a0e3954d9291be170d))
* **nav:** defer non-animated swipe back until the gesture completes ([#31552](https://github.com/ionic-team/ionic-framework/issues/31552)) ([5f3f5fc](https://github.com/ionic-team/ionic-framework/commit/5f3f5fcae2bc83cb4301bc6e47c7e0b64f6d3d76)), closes [#29793](https://github.com/ionic-team/ionic-framework/issues/29793)


## [9.0.8](https://github.com/ionic-team/ionic-framework/compare/v9.0.7...v9.0.8) (2026-10-14)

### Bug Fixes
Expand Down
22 changes: 11 additions & 11 deletions core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "9.0.8",
"version": "9.0.9",
"description": "Base components for Ionic",
"engines": {
"node": ">= 16"
Expand Down Expand Up @@ -86,7 +86,7 @@
"@stencil/angular-output-target": "^1.4.1",
"@stencil/react-output-target": "^1.6.2",
"@stencil/sass": "^3.0.9",
"@stencil/vue-output-target": "0.14.2",
"@stencil/vue-output-target": "0.14.6",
"@types/jest": "^29.5.6",
"@types/node": "^24.13.3",
"@typescript-eslint/eslint-plugin": "^8.0.0",
Expand Down
6 changes: 3 additions & 3 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ export namespace Components {
*/
"tappable": boolean;
/**
* If `true`, the backdrop will be visible.
* If `true`, the backdrop will be visible. This property controls whether or not the backdrop's default background is shown. It does not control whether or not the backdrop blocks interaction, and it does not override a custom background set on `ion-backdrop`.
* @default true
*/
"visible": boolean;
Expand Down Expand Up @@ -912,7 +912,7 @@ export namespace Components {
}
interface IonDatetime {
/**
* Emits the ionCancel event and optionally closes the popover or modal that the datetime was presented in.
* Emits the ionCancel event, discards any unconfirmed selection, and optionally closes the popover or modal that the datetime was presented in.
* @param closeOverlay If `true`, closes the parent overlay. Defaults to `false`.
*/
"cancel": (closeOverlay?: boolean) => Promise<void>;
Expand Down Expand Up @@ -5512,7 +5512,7 @@ declare namespace LocalJSX {
*/
"tappable"?: boolean;
/**
* If `true`, the backdrop will be visible.
* If `true`, the backdrop will be visible. This property controls whether or not the backdrop's default background is shown. It does not control whether or not the backdrop blocks interaction, and it does not override a custom background set on `ion-backdrop`.
* @default true
*/
"visible"?: boolean;
Expand Down
4 changes: 4 additions & 0 deletions core/src/components/backdrop/backdrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import { getIonMode } from '../../global/ionic-global';
export class Backdrop implements ComponentInterface {
/**
* If `true`, the backdrop will be visible.
* This property controls whether or not the backdrop's
* default background is shown. It does not control whether
* or not the backdrop blocks interaction, and it does not
* override a custom background set on `ion-backdrop`.
*/
@Prop() visible = true;

Expand Down
70 changes: 63 additions & 7 deletions core/src/components/datetime/datetime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@ export class Datetime implements ComponentInterface {

private destroyCalendarListener?: () => void;
private destroyKeyboardMO?: () => void;
private destroyOverlayPresentListener?: () => void;
/**
* Set when the user selects a value, cleared when it's confirmed
* or `processValue` runs (`reset()`, or `value` being set to a
* non-empty value). The overlay present listener only discards a
* selection while this is set.
*/
private hasUnconfirmedSelection = false;

// TODO(FW-2832): types (DatetimeParts causes some errors that need untangling)
private minParts?: any;
Expand Down Expand Up @@ -565,6 +573,8 @@ export class Datetime implements ComponentInterface {
}
}

this.hasUnconfirmedSelection = false;

if (closeOverlay) {
this.closeParentOverlay(CONFIRM_ROLE);
}
Expand All @@ -583,9 +593,10 @@ export class Datetime implements ComponentInterface {
}

/**
* Emits the ionCancel event and
* optionally closes the popover
* or modal that the datetime was
* Emits the ionCancel event, discards
* any unconfirmed selection, and
* optionally closes the popover or
* modal that the datetime was
* presented in.
*
* @param closeOverlay If `true`, closes the parent overlay. Defaults to `false`.
Expand All @@ -594,8 +605,12 @@ export class Datetime implements ComponentInterface {
async cancel(closeOverlay = false) {
this.ionCancel.emit();

if (closeOverlay) {
this.closeParentOverlay(CANCEL_ROLE);
/**
* When a parent overlay closes, the present listener discards
* the selection on the next open. Otherwise discard it now.
*/
if (!closeOverlay || !this.closeParentOverlay(CANCEL_ROLE)) {
this.processValue(this.value);
}
}

Expand Down Expand Up @@ -665,6 +680,8 @@ export class Datetime implements ComponentInterface {
if (popoverOrModal) {
popoverOrModal.dismiss(undefined, role);
}

return popoverOrModal !== null;
};

private setWorkingParts = (parts: DatetimeParts) => {
Expand All @@ -682,6 +699,8 @@ export class Datetime implements ComponentInterface {
return;
}

this.hasUnconfirmedSelection = true;

const { multiple, minParts, maxParts, activeParts } = this;

/**
Expand Down Expand Up @@ -1102,6 +1121,7 @@ export class Datetime implements ComponentInterface {
this.loadTimeout = setTimeout(() => {
this.ensureReadyIfVisible();
}, 100);
this.initializeOverlayPresentListener();
}

disconnectedCallback() {
Expand All @@ -1110,6 +1130,39 @@ export class Datetime implements ComponentInterface {
this.clearFocusVisible = undefined;
}
this.loadTimeoutCleanup();

if (this.destroyOverlayPresentListener) {
this.destroyOverlayPresentListener();
this.destroyOverlayPresentListener = undefined;
}
}

/**
* A datetime in a modal or popover stays mounted between
* presentations, so an unconfirmed selection would still show when
* it reopens. Discarding on present rather than dismiss lets app
* code that runs on dismiss still confirm or reset the selection,
* whatever order its listeners were added in.
*/
private initializeOverlayPresentListener() {
const overlay = this.el.closest('ion-modal, ion-popover');
if (!overlay) {
return;
}

const eventName = overlay.tagName === 'ION-MODAL' ? 'ionModalWillPresent' : 'ionPopoverWillPresent';
const onPresent = (ev: Event) => {
// Ignore nested overlays and skip when there's nothing to discard.
if (ev.target !== overlay || !this.hasUnconfirmedSelection) {
return;
}

// The overlay is still hidden, so skip the month animation.
this.processValue(this.value, false);
};

overlay.addEventListener(eventName, onPresent);
this.destroyOverlayPresentListener = () => overlay.removeEventListener(eventName, onPresent);
}

/**
Expand Down Expand Up @@ -1363,7 +1416,9 @@ export class Datetime implements ComponentInterface {
});
}

private processValue = (value?: string | string[] | null) => {
private processValue = (value?: string | string[] | null, animated = true) => {
this.hasUnconfirmedSelection = false;

const hasValue =
value !== null && value !== undefined && value !== '' && (!Array.isArray(value) || value.length > 0);
const valueToProcess = hasValue ? parseDate(value) : this.defaultParts;
Expand Down Expand Up @@ -1435,13 +1490,14 @@ export class Datetime implements ComponentInterface {
const bodyIsVisible = el.classList.contains('datetime-ready');
const { isGridStyle, showMonthAndYear } = this;

if (isGridStyle && didChangeMonth && bodyIsVisible && !showMonthAndYear) {
if (isGridStyle && didChangeMonth && bodyIsVisible && !showMonthAndYear && animated) {
/**
* Only animate if:
* 1. We're using grid style (wheel style pickers should just jump to new value)
* 2. The month and/or year actually changed, and both are defined (otherwise there's nothing to animate to)
* 3. The calendar body is visible (prevents animation when in collapsed datetime-button, for example)
* 4. The month/year picker is not open (since you wouldn't see the animation anyway)
* 5. The caller did not opt out (e.g. the parent overlay is still hidden)
*/
this.animateToDate(targetValue);
} else {
Expand Down
Loading
Loading