Compare commits

..

18 Commits

Author SHA1 Message Date
github-actions
5e448d9c74 v6.0.3 2022-01-19 15:01:39 +00:00
Liam DeBeasi
be022f7de8 fix(angular-server): use correct @ionic/angular dependency version (#24593)
resolves #24592
2022-01-18 16:25:25 -05:00
Zac Hayes
1a9be747f2 docs(datetime): fix typo in overview (#24577) 2022-01-14 12:58:29 -05:00
Sean Perkins
af01a8b307 fix(item): error slot visible in Safari (#24579)
Resolves #24575
2022-01-14 12:36:14 -05:00
Sean Perkins
e00c9cbd4c docs(textarea): available autocapitalize options (#24574) 2022-01-13 13:00:33 -05:00
Sean Perkins
e284d7a2c7 docs(menu-toggle): add usage examples and slot docs (#24570) 2022-01-13 11:38:56 -05:00
Amanda Smith
c8a392aef5 fix(react): prevent errors when dismissing inline popover after containing element is removed (#24569) 2022-01-12 14:51:50 -06:00
Sean Perkins
273ae2cc08 fix(angular): apply touch, dirty and pristine form control classes (#24558)
Resolves #24483
2022-01-12 14:48:01 -05:00
Sean Perkins
9a15753fd9 fix(modal): life cycle events for controller modals (#24508)
Resolves #24460
2022-01-12 13:46:01 -05:00
Sean Perkins
a753d3438a test(overlays): wait for modal events to fire (#24568) 2022-01-12 12:18:29 -05:00
Liam DeBeasi
7704ac3a37 fix(menu): remove main attribute that was supposed to removed in v5 (#24565)
resolves #24563
2022-01-12 11:52:37 -05:00
Liam DeBeasi
8c442059b5 docs(input): clarify intended placeholder behavior for certain input types (#24567)
resolves #24557
2022-01-12 11:44:52 -05:00
Liam DeBeasi
3d20959221 fix(datetime): showing calendar grid no longer causes month to switch on ios 15 (#24554)
resolves #24405
2022-01-12 10:24:29 -05:00
Liam DeBeasi
88602a9acf chore(dev-build): bump patch version for lerna dev builds (#24564) 2022-01-12 10:05:08 -05:00
Sean Perkins
f5b4382fd5 fix(overlays): getTop now returns the top-most presented overlay (#24547)
Resolves #19111
2022-01-11 15:13:35 -05:00
Liam DeBeasi
c4745d24ac chore(dev-build): use exact versioning so npm installs correct Ionic Core (#24555) 2022-01-11 15:04:10 -05:00
Amanda Smith
bce849c5f3 fix(react): add useRef wrapper to useIonOverlay state to avoid stale references (#24553) 2022-01-11 13:40:46 -06:00
Liam DeBeasi
bb9e5f68b4 merge release-6.0.2
Release 6.0.2
2022-01-11 12:32:30 -05:00
54 changed files with 735 additions and 225 deletions

View File

@@ -25,11 +25,11 @@ jobs:
run: |
echo "HASH=$(git log -1 --format=%H | cut -c 1-7)" >> $GITHUB_ENV
echo "TIMESTAMP=$(date +%s)" >> $GITHUB_ENV
echo "CURRENT_VERSION=$(node -p -e "require('./core/package.json').version")" >> $GITHUB_ENV
echo "CURRENT_VERSION=$(node ./.scripts/bump-version.js)" >> $GITHUB_ENV
shell: bash
- name: Create Dev Build
run: |
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ env.CURRENT_VERSION }}")-dev.$(echo "${{ env.TIMESTAMP }}").$(echo "${{ env.HASH }}") --no-verify-access --yes --force-publish='*' --dist-tag dev --no-git-tag-version --no-push
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ env.CURRENT_VERSION }}")-dev.$(echo "${{ env.TIMESTAMP }}").$(echo "${{ env.HASH }}") --no-verify-access --yes --force-publish='*' --dist-tag dev --no-git-tag-version --no-push --exact
shell: bash
- id: dev-build
run: echo "::set-output name=version::$(echo "${{ env.CURRENT_VERSION }}")-dev.$(echo "${{ env.TIMESTAMP }}").$(echo "${{ env.HASH }}")"

10
.scripts/bump-version.js Normal file
View File

@@ -0,0 +1,10 @@
const semver = require('semver');
const getDevVersion = () => {
const originalVersion = require('../lerna.json').version;
const baseVersion = semver.inc(originalVersion, 'patch');
return baseVersion;
}
console.log(getDevVersion());

View File

@@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.3](https://github.com/ionic-team/ionic-framework/compare/v6.0.2...v6.0.3) (2022-01-19)
### Bug Fixes
* **angular-server:** use correct @ionic/angular dependency version ([#24593](https://github.com/ionic-team/ionic-framework/issues/24593)) ([be022f7](https://github.com/ionic-team/ionic-framework/commit/be022f7de8df85ae842b0e111722b03448d60387)), closes [#24592](https://github.com/ionic-team/ionic-framework/issues/24592)
* **angular:** apply touch, dirty and pristine form control classes ([#24558](https://github.com/ionic-team/ionic-framework/issues/24558)) ([273ae2c](https://github.com/ionic-team/ionic-framework/commit/273ae2cc087b2a5a30fb50a1b0eaeb0a221900fc)), closes [#24483](https://github.com/ionic-team/ionic-framework/issues/24483)
* **datetime:** showing calendar grid no longer causes month to switch on ios 15 ([#24554](https://github.com/ionic-team/ionic-framework/issues/24554)) ([3d20959](https://github.com/ionic-team/ionic-framework/commit/3d2095922147ea3763e977412977edd9586fec5d)), closes [#24405](https://github.com/ionic-team/ionic-framework/issues/24405)
* **item:** error slot visible in Safari ([#24579](https://github.com/ionic-team/ionic-framework/issues/24579)) ([af01a8b](https://github.com/ionic-team/ionic-framework/commit/af01a8b3073dce784cc042923d712b9492638d32)), closes [#24575](https://github.com/ionic-team/ionic-framework/issues/24575)
* **menu:** remove main attribute that was supposed to removed in v5 ([#24565](https://github.com/ionic-team/ionic-framework/issues/24565)) ([7704ac3](https://github.com/ionic-team/ionic-framework/commit/7704ac3a3710396248590daecb945b76825a0539)), closes [#24563](https://github.com/ionic-team/ionic-framework/issues/24563)
* **modal:** life cycle events for controller modals ([#24508](https://github.com/ionic-team/ionic-framework/issues/24508)) ([9a15753](https://github.com/ionic-team/ionic-framework/commit/9a15753fd95e32155abdeb490ec57cb72385ad1a)), closes [#24460](https://github.com/ionic-team/ionic-framework/issues/24460)
* **overlays:** getTop now returns the top-most presented overlay ([#24547](https://github.com/ionic-team/ionic-framework/issues/24547)) ([f5b4382](https://github.com/ionic-team/ionic-framework/commit/f5b4382fd5728365e4badf39bc1dd0c149b45c2c)), closes [#19111](https://github.com/ionic-team/ionic-framework/issues/19111)
* **react:** add useRef wrapper to useIonOverlay state to avoid stale references ([#24553](https://github.com/ionic-team/ionic-framework/issues/24553)) ([bce849c](https://github.com/ionic-team/ionic-framework/commit/bce849c5f324522002eff7f8a5e5023150e9201c))
* **react:** prevent errors when dismissing inline popover after containing element is removed ([#24569](https://github.com/ionic-team/ionic-framework/issues/24569)) ([c8a392a](https://github.com/ionic-team/ionic-framework/commit/c8a392aef5fbf25f59a573897d970c41abac04d2))
## [6.0.2](https://github.com/ionic-team/ionic-framework/compare/v6.0.1...v6.0.2) (2022-01-11)

View File

@@ -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.
## [6.0.3](https://github.com/ionic-team/ionic/compare/v6.0.2...v6.0.3) (2022-01-19)
### Bug Fixes
* **angular:** apply touch, dirty and pristine form control classes ([#24558](https://github.com/ionic-team/ionic/issues/24558)) ([273ae2c](https://github.com/ionic-team/ionic/commit/273ae2cc087b2a5a30fb50a1b0eaeb0a221900fc)), closes [#24483](https://github.com/ionic-team/ionic/issues/24483)
## [6.0.2](https://github.com/ionic-team/ionic/compare/v6.0.1...v6.0.2) (2022-01-11)

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "6.0.2",
"version": "6.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "6.0.2",
"version": "6.0.3",
"description": "Angular specific wrappers for @ionic/core",
"keywords": [
"ionic",
@@ -44,7 +44,7 @@
"validate": "npm i && npm run lint && npm run test && npm run build"
},
"dependencies": {
"@ionic/core": "^6.0.2",
"@ionic/core": "^6.0.3",
"jsonc-parser": "^3.0.0",
"tslib": "^2.0.0"
},

View File

@@ -96,7 +96,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
if (formControl) {
const methodsToPatch = ['markAsTouched', 'markAllAsTouched', 'markAsUntouched', 'markAsDirty', 'markAsPristine'];
methodsToPatch.forEach((method) => {
if (formControl.get(method)) {
if (typeof formControl[method] !== 'undefined') {
const oldFn = formControl[method].bind(formControl);
formControl[method] = (...params: any[]) => {
oldFn(...params);

View File

@@ -8,6 +8,16 @@ describe('Form', () => {
cy.get('#input-touched').click();
cy.get('#touched-input-test').should('have.class', 'ion-touched');
});
describe('markAllAsTouched', () => {
it('should apply .ion-touched to nearest ion-item', () => {
cy.get('#mark-all-touched-button').click();
cy.get('form ion-item').each(item => {
cy.wrap(item).should('have.class', 'ion-touched');
});
});
});
});
describe('change', () => {

View File

@@ -12,7 +12,8 @@
<ion-item>
<ion-label>DateTime</ion-label>
<ion-datetime formControlName="datetime" min="1994-03-14" max="2017-12-09" display-format="MM/DD/YYYY"></ion-datetime>
<ion-datetime formControlName="datetime" min="1994-03-14" max="2017-12-09" display-format="MM/DD/YYYY">
</ion-datetime>
</ion-item>
<ion-item>
@@ -65,6 +66,7 @@
<p>
Form Submit: <span id="submit">{{submitted}}</span>
</p>
<ion-button id="mark-all-touched-button" (click)="markAllAsTouched()">Mark all as touched</ion-button>
<ion-button id="submit-button" type="submit" [disabled]="!profileForm.valid">Submit</ion-button>
</form>

View File

@@ -46,4 +46,8 @@ export class FormComponent {
});
}
markAllAsTouched() {
this.profileForm.markAllAsTouched();
}
}

View File

@@ -3,6 +3,21 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.3](https://github.com/ionic-team/ionic/compare/v6.0.2...v6.0.3) (2022-01-19)
### Bug Fixes
* **datetime:** showing calendar grid no longer causes month to switch on ios 15 ([#24554](https://github.com/ionic-team/ionic/issues/24554)) ([3d20959](https://github.com/ionic-team/ionic/commit/3d2095922147ea3763e977412977edd9586fec5d)), closes [#24405](https://github.com/ionic-team/ionic/issues/24405)
* **item:** error slot visible in Safari ([#24579](https://github.com/ionic-team/ionic/issues/24579)) ([af01a8b](https://github.com/ionic-team/ionic/commit/af01a8b3073dce784cc042923d712b9492638d32)), closes [#24575](https://github.com/ionic-team/ionic/issues/24575)
* **menu:** remove main attribute that was supposed to removed in v5 ([#24565](https://github.com/ionic-team/ionic/issues/24565)) ([7704ac3](https://github.com/ionic-team/ionic/commit/7704ac3a3710396248590daecb945b76825a0539)), closes [#24563](https://github.com/ionic-team/ionic/issues/24563)
* **modal:** life cycle events for controller modals ([#24508](https://github.com/ionic-team/ionic/issues/24508)) ([9a15753](https://github.com/ionic-team/ionic/commit/9a15753fd95e32155abdeb490ec57cb72385ad1a)), closes [#24460](https://github.com/ionic-team/ionic/issues/24460)
* **overlays:** getTop now returns the top-most presented overlay ([#24547](https://github.com/ionic-team/ionic/issues/24547)) ([f5b4382](https://github.com/ionic-team/ionic/commit/f5b4382fd5728365e4badf39bc1dd0c149b45c2c)), closes [#19111](https://github.com/ionic-team/ionic/issues/19111)
## [6.0.2](https://github.com/ionic-team/ionic/compare/v6.0.1...v6.0.2) (2022-01-11)

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "6.0.2",
"version": "6.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "6.0.2",
"version": "6.0.3",
"description": "Base components for Ionic",
"keywords": [
"ionic",

View File

@@ -1081,7 +1081,7 @@ export namespace Components {
*/
"pattern"?: string;
/**
* Instructional text that shows before the input has a value.
* Instructional text that shows before the input has a value. This property applies only when the `type` property is set to `"email"`, `"number"`, `"password"`, `"search"`, `"tel"`, `"text"`, or `"url"`, otherwise it is ignored.
*/
"placeholder"?: string;
/**
@@ -2722,7 +2722,7 @@ export namespace Components {
*/
"autoGrow": boolean;
/**
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
*/
"autocapitalize": string;
/**
@@ -4802,7 +4802,7 @@ declare namespace LocalJSX {
*/
"pattern"?: string;
/**
* Instructional text that shows before the input has a value.
* Instructional text that shows before the input has a value. This property applies only when the `type` property is set to `"email"`, `"number"`, `"password"`, `"search"`, `"tel"`, `"text"`, or `"url"`, otherwise it is ignored.
*/
"placeholder"?: string;
/**
@@ -6430,7 +6430,7 @@ declare namespace LocalJSX {
*/
"autoGrow"?: boolean;
/**
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
*/
"autocapitalize"?: string;
/**

View File

@@ -79,24 +79,49 @@
* unhiding the calendar content.
* To workaround this, we set the opacity
* of the content to 0 and hide it offscreen.
* TODO: This is fixed in Safari 15+, so remove
* when Safari 14 support is dropped.
*
* -webkit-named-image is something only WebKit supports
* so we use this to detect general WebKit support.
* aspect-ratio is only supported in Safari 15+
* so by checking lack of aspect-ratio support, we know
* that we are in a pre-Safari 15 browser.
*
* TODO(FW-554): Remove when iOS 14 support is dropped.
*/
:host(.show-month-and-year) .calendar-next-prev,
:host(.show-month-and-year) .calendar-days-of-week,
:host(.show-month-and-year) .calendar-body,
:host(.show-month-and-year) .datetime-time {
@include position(null, null, null, -99999px);
@supports (background: -webkit-named-image(apple-pay-logo-black)) and (not (aspect-ratio: 1/1)) {
:host(.show-month-and-year) .calendar-next-prev,
:host(.show-month-and-year) .calendar-days-of-week,
:host(.show-month-and-year) .calendar-body,
:host(.show-month-and-year) .datetime-time {
@include position(null, null, null, -99999px);
position: absolute;
position: absolute;
/**
* Use visibility instead of
* opacity to ensure element
* cannot receive focus
*/
visibility: hidden;
pointer-events: none;
/**
* Use visibility instead of
* opacity to ensure element
* cannot receive focus
*/
visibility: hidden;
pointer-events: none;
}
}
/**
* This support check two cases:
* 1. A WebKit browser that supports aspect-ratio (Safari 15+)
* 2. Any non-WebKit browser.
* Note that just overriding this display: none is not
* sufficient to resolve the issue mentioned above, which
* is why we do another set of @supports checks.
*/
@supports (not (background: -webkit-named-image(apple-pay-logo-black))) or ((background: -webkit-named-image(apple-pay-logo-black)) and (aspect-ratio: 1/1)) {
:host(.show-month-and-year) .calendar-next-prev,
:host(.show-month-and-year) .calendar-days-of-week,
:host(.show-month-and-year) .calendar-body,
:host(.show-month-and-year) .datetime-time {
display: none;
}
}
:host(.datetime-readonly),

View File

@@ -1,6 +1,6 @@
# ion-datetime
Datetimes present a calendar interface and time wheel, making it easy for users to select dates and times. Datetimes are similar to the native `input` elements of `datetime-local`, however, Ionic Framework's Datetime component makes it easy to display the date and time in the a preferred format, and manage the datetime values.
Datetimes present a calendar interface and time wheel, making it easy for users to select dates and times. Datetimes are similar to the native `input` elements of `datetime-local`, however, Ionic Framework's Datetime component makes it easy to display the date and time in the preferred format, and manage the datetime values.
### Datetime Data

View File

@@ -151,6 +151,8 @@ export class Input implements ComponentInterface {
/**
* Instructional text that shows before the input has a value.
* This property applies only when the `type` property is set to `"email"`,
* `"number"`, `"password"`, `"search"`, `"tel"`, `"text"`, or `"url"`, otherwise it is ignored.
*/
@Prop() placeholder?: string;

View File

@@ -340,7 +340,7 @@ export default defineComponent({
| `multiple` | `multiple` | If `true`, the user can enter more than one value. This attribute applies when the type attribute is set to `"email"` or `"file"`, otherwise it is ignored. | `boolean \| undefined` | `undefined` |
| `name` | `name` | The name of the control, which is submitted with the form data. | `string` | `this.inputId` |
| `pattern` | `pattern` | A regular expression that the value is checked against. The pattern must match the entire value, not just some subset. Use the title attribute to describe the pattern to help the user. This attribute applies when the value of the type attribute is `"text"`, `"search"`, `"tel"`, `"url"`, `"email"`, `"date"`, or `"password"`, otherwise it is ignored. When the type attribute is `"date"`, `pattern` will only be used in browsers that do not support the `"date"` input type natively. See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date for more information. | `string \| undefined` | `undefined` |
| `placeholder` | `placeholder` | Instructional text that shows before the input has a value. | `string \| undefined` | `undefined` |
| `placeholder` | `placeholder` | Instructional text that shows before the input has a value. This property applies only when the `type` property is set to `"email"`, `"number"`, `"password"`, `"search"`, `"tel"`, `"text"`, or `"url"`, otherwise it is ignored. | `string \| undefined` | `undefined` |
| `readonly` | `readonly` | If `true`, the user cannot modify the value. | `boolean` | `false` |
| `required` | `required` | If `true`, the user must fill in a value before submitting a form. | `boolean` | `false` |
| `size` | `size` | The initial size of the control. This value is in pixels unless the value of the type attribute is `"text"` or `"password"`, in which case it is an integer number of characters. This attribute applies only when the `type` attribute is set to `"text"`, `"search"`, `"tel"`, `"url"`, `"email"`, or `"password"`, otherwise it is ignored. | `number \| undefined` | `undefined` |

View File

@@ -433,6 +433,12 @@ button, a {
display: none;
}
::slotted([slot="error"]) {
display: none;
color: var(--highlight-color-invalid);
}
:host(.item-interactive.ion-invalid) ::slotted([slot="error"]) {
display: block;
}
@@ -527,12 +533,6 @@ ion-ripple-effect {
z-index: 1;
}
::slotted([slot="error"]) {
display: none;
color: var(--highlight-color-invalid);
}
// Item: Reduced Motion
// --------------------------------------------------

View File

@@ -5,6 +5,9 @@ import { menuController } from '../../utils/menu-controller';
import { updateVisibility } from './menu-toggle-util';
/**
* @slot - Content is placed inside the toggle to act as the click target.
*/
@Component({
tag: 'ion-menu-toggle',
styleUrl: 'menu-toggle.scss',

View File

@@ -9,6 +9,151 @@ In case it's desired to keep `ion-menu-toggle` always visible, the `autoHide` pr
<!-- Auto Generated Below -->
## Usage
### Angular
```html
<ion-app>
<ion-menu side="start" menuId="first" contentId="main">
<ion-header>
<ion-toolbar color="secondary">
<ion-title>Example Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>Menu Item</ion-item>
</ion-list>
</ion-content>
</ion-menu>
<div class="ion-page" id="main">
<ion-content class="ion-padding">
<ion-menu-toggle>
<ion-button>Toggle Menu</ion-button>
</ion-menu-toggle>
</ion-content>
</div>
</ion-app>
```
### Javascript
```html
<ion-app>
<ion-menu side="start" menu-id="first" content-id="main">
<ion-header>
<ion-toolbar color="secondary">
<ion-title>Example Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>Menu Item</ion-item>
</ion-list>
</ion-content>
</ion-menu>
<div class="ion-page" id="main">
<ion-content class="ion-padding">
<ion-menu-toggle>
<ion-button>Toggle Menu</ion-button>
</ion-menu-toggle>
</ion-content>
</div>
</ion-app>
```
### React
```tsx
import React from 'react';
import { IonMenu, IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem, IonMenuToggle, IonButton } from '@ionic/react';
export const MenuExample: React.FC = () => (
<>
<IonMenu side="start" menuId="first">
<IonHeader>
<IonToolbar color="primary">
<IonTitle>Example Menu</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<IonList>
<IonItem>Menu Item</IonItem>
</IonList>
</IonContent>
</IonMenu>
<IonContent>
<IonMenuToggle>
<IonButton>Toggle Menu</IonButton>
</IonMenuToggle>
</IonContent>
</>
);
```
### Vue
```html
<template>
<ion-menu side="start" menu-id="first" content-id="main">
<ion-header>
<ion-toolbar color="primary">
<ion-title>Example Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>Menu Item</ion-item>
</ion-list>
</ion-content>
</ion-menu>
<div class="ion-page" id="main">
<ion-content>
<ion-menu-toggle>
<ion-button>Toggle Menu</ion-button>
</ion-menu-toggle>
</ion-content>
</div>
</template>
<script>
import {
IonContent,
IonHeader,
IonItem,
IonList,
IonMenu,
IonMenuToggle,
IonButton,
IonTitle,
IonToolbar,
menuController
} from '@ionic/vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: {
IonContent,
IonHeader,
IonItem,
IonList,
IonMenu,
IonMenuToggle,
IonButton,
IonTitle,
IonToolbar
}
});
</script>
```
## Properties
| Property | Attribute | Description | Type | Default |
@@ -17,6 +162,13 @@ In case it's desired to keep `ion-menu-toggle` always visible, the `autoHide` pr
| `menu` | `menu` | Optional property that maps to a Menu's `menuId` prop. Can also be `start` or `end` for the menu side. This is used to find the correct menu to toggle. If this property is not used, `ion-menu-toggle` will toggle the first menu that is active. | `string \| undefined` | `undefined` |
## Slots
| Slot | Description |
| ---- | --------------------------------------------------------------- |
| | Content is placed inside the toggle to act as the click target. |
----------------------------------------------
*Built with [StencilJS](https://stenciljs.com/)*

View File

@@ -0,0 +1,24 @@
```html
<ion-app>
<ion-menu side="start" menuId="first" contentId="main">
<ion-header>
<ion-toolbar color="secondary">
<ion-title>Example Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>Menu Item</ion-item>
</ion-list>
</ion-content>
</ion-menu>
<div class="ion-page" id="main">
<ion-content class="ion-padding">
<ion-menu-toggle>
<ion-button>Toggle Menu</ion-button>
</ion-menu-toggle>
</ion-content>
</div>
</ion-app>
```

View File

@@ -0,0 +1,24 @@
```html
<ion-app>
<ion-menu side="start" menu-id="first" content-id="main">
<ion-header>
<ion-toolbar color="secondary">
<ion-title>Example Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>Menu Item</ion-item>
</ion-list>
</ion-content>
</ion-menu>
<div class="ion-page" id="main">
<ion-content class="ion-padding">
<ion-menu-toggle>
<ion-button>Toggle Menu</ion-button>
</ion-menu-toggle>
</ion-content>
</div>
</ion-app>
```

View File

@@ -0,0 +1,28 @@
```tsx
import React from 'react';
import { IonMenu, IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem, IonMenuToggle, IonButton, IonPage } from '@ionic/react';
export const MenuExample: React.FC = () => (
<>
<IonMenu side="start" menuId="first" contentId="main">
<IonHeader>
<IonToolbar color="primary">
<IonTitle>Example Menu</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<IonList>
<IonItem>Menu Item</IonItem>
</IonList>
</IonContent>
</IonMenu>
<IonPage id="main">
<IonContent>
<IonMenuToggle>
<IonButton>Toggle Menu</IonButton>
</IonMenuToggle>
</IonContent>
</IonPage>
</>
);
```

View File

@@ -0,0 +1,53 @@
```html
<template>
<ion-menu side="start" menu-id="first" content-id="main">
<ion-header>
<ion-toolbar color="primary">
<ion-title>Example Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>Menu Item</ion-item>
</ion-list>
</ion-content>
</ion-menu>
<div class="ion-page" id="main">
<ion-content>
<ion-menu-toggle>
<ion-button>Toggle Menu</ion-button>
</ion-menu-toggle>
</ion-content>
</div>
</template>
<script>
import {
IonContent,
IonHeader,
IonItem,
IonList,
IonMenu,
IonMenuToggle,
IonButton,
IonTitle,
IonToolbar
} from '@ionic/vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: {
IonContent,
IonHeader,
IonItem,
IonList,
IonMenu,
IonMenuToggle,
IonButton,
IonTitle,
IonToolbar
}
});
</script>
```

View File

@@ -169,26 +169,11 @@ export class Menu implements ComponentInterface, MenuI {
return;
}
const el = this.el;
const parent = el.parentNode as any;
if (this.contentId === undefined) {
console.warn(`[DEPRECATED][ion-menu] Using the [main] attribute is deprecated, please use the "contentId" property instead:
BEFORE:
<ion-menu>...</ion-menu>
<div main>...</div>
AFTER:
<ion-menu contentId="main-content"></ion-menu>
<div id="main-content">...</div>
`);
}
const content = this.contentId !== undefined
? document.getElementById(this.contentId)
: parent && parent.querySelector && parent.querySelector('[main]');
: null;
if (!content || !content.tagName) {
// requires content element
if (content === null) {
console.error('Menu: must have a "content" element to listen for drag events on.');
return;
}

View File

@@ -216,28 +216,28 @@ export class Modal implements ComponentInterface, OverlayInterface {
*/
@Event({ eventName: 'ionModalDidDismiss' }) didDismiss!: EventEmitter<OverlayEventDetail>;
/**
* Emitted after the modal has presented.
* Shorthand for ionModalWillDismiss.
*/
/**
* Emitted after the modal has presented.
* Shorthand for ionModalWillDismiss.
*/
@Event({ eventName: 'didPresent' }) didPresentShorthand!: EventEmitter<void>;
/**
* Emitted before the modal has presented.
* Shorthand for ionModalWillPresent.
*/
/**
* Emitted before the modal has presented.
* Shorthand for ionModalWillPresent.
*/
@Event({ eventName: 'willPresent' }) willPresentShorthand!: EventEmitter<void>;
/**
* Emitted before the modal has dismissed.
* Shorthand for ionModalWillDismiss.
*/
/**
* Emitted before the modal has dismissed.
* Shorthand for ionModalWillDismiss.
*/
@Event({ eventName: 'willDismiss' }) willDismissShorthand!: EventEmitter<OverlayEventDetail>;
/**
* Emitted after the modal has dismissed.
* Shorthand for ionModalDidDismiss.
*/
/**
* Emitted after the modal has dismissed.
* Shorthand for ionModalDidDismiss.
*/
@Event({ eventName: 'didDismiss' }) didDismissShorthand!: EventEmitter<OverlayEventDetail>;
@Watch('swipeToClose')
@@ -494,19 +494,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
if (dismissed) {
const { delegate } = this.getDelegate();
/**
* If the modal is presented through a controller, we don't need to detach
* since the el was already removed during the `dismiss` call above. Skipping
* this step also prevents an issue where rapdily dismissing right after
* presenting could cause `detachComponent` to be called after the present
* finished, blanking out the newly opened modal.
*
* TODO(FW-423) try and find a way to resolve the race condition directly
*/
if (this.inline) {
await detachComponent(delegate, this.usersElement);
}
await detachComponent(delegate, this.usersElement);
if (this.animation) {
this.animation.destroy();

View File

@@ -288,7 +288,7 @@ export default defineComponent({
| Property | Attribute | Description | Type | Default |
| ---------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | -------------- |
| `autoGrow` | `auto-grow` | If `true`, the element height will increase based on the value. | `boolean` | `false` |
| `autocapitalize` | `autocapitalize` | Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. | `string` | `'none'` |
| `autocapitalize` | `autocapitalize` | Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`. | `string` | `'none'` |
| `autofocus` | `autofocus` | This Boolean attribute lets you specify that a form control should have input focus when the page loads. | `boolean` | `false` |
| `clearOnEdit` | `clear-on-edit` | If `true`, the value will be cleared after focus upon edit. Defaults to `true` when `type` is `"password"`, `false` for all other types. | `boolean` | `false` |
| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `undefined` |

View File

@@ -47,6 +47,7 @@ export class Textarea implements ComponentInterface {
/**
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
* Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
*/
@Prop() autocapitalize = 'none';

View File

@@ -120,6 +120,8 @@ export const focusFirstDescendant = (ref: Element, overlay: HTMLIonOverlayElemen
}
};
const isOverlayHidden = (overlay: Element) => overlay.classList.contains('overlay-hidden');
const focusLastDescendant = (ref: Element, overlay: HTMLIonOverlayElement) => {
const inputs = Array.from(ref.querySelectorAll(focusableQueryString)) as HTMLElement[];
let lastInput = inputs.length > 0 ? inputs[inputs.length - 1] : null;
@@ -291,7 +293,7 @@ export const connectListeners = (doc: Document) => {
// handle back-button click
doc.addEventListener('ionBackButton', ev => {
const lastOverlay = getTopOpenOverlay(doc);
const lastOverlay = getOverlay(doc);
if (lastOverlay && lastOverlay.backdropDismiss) {
(ev as BackButtonEvent).detail.register(OVERLAY_BACK_BUTTON_PRIORITY, () => {
return lastOverlay.dismiss(undefined, BACKDROP);
@@ -302,7 +304,7 @@ export const connectListeners = (doc: Document) => {
// handle ESC to close overlay
doc.addEventListener('keyup', ev => {
if (ev.key === 'Escape') {
const lastOverlay = getTopOpenOverlay(doc);
const lastOverlay = getOverlay(doc);
if (lastOverlay && lastOverlay.backdropDismiss) {
lastOverlay.dismiss(undefined, BACKDROP);
}
@@ -328,30 +330,14 @@ export const getOverlays = (doc: Document, selector?: string): HTMLIonOverlayEle
};
/**
* Gets the top-most/last opened
* overlay that is currently presented.
* Returns an overlay element
* @param doc The document to find the element within.
* @param overlayTag The selector for the overlay, defaults to Ionic overlay components.
* @param id The unique identifier for the overlay instance.
* @returns The overlay element or `undefined` if no overlay element is found.
*/
const getTopOpenOverlay = (doc: Document): HTMLIonOverlayElement | undefined => {
const overlays = getOverlays(doc);
for (let i = overlays.length - 1; i >= 0; i--) {
const overlay = overlays[i];
/**
* Only consider overlays that
* are presented. Presented overlays
* will not have the .overlay-hidden
* class on the host.
*/
if (!overlay.classList.contains('overlay-hidden')) {
return overlay;
}
}
return;
}
export const getOverlay = (doc: Document, overlayTag?: string, id?: string): HTMLIonOverlayElement | undefined => {
const overlays = getOverlays(doc, overlayTag);
const getOverlay = (doc: Document, overlayTag?: string, id?: string): HTMLIonOverlayElement | undefined => {
const overlays = getOverlays(doc, overlayTag).filter(o => !isOverlayHidden(o));
return (id === undefined)
? overlays[overlays.length - 1]
: overlays.find(o => o.id === id);

View File

@@ -1,44 +1,48 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Overlays</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<script type="module">
import { modalController, createAnimation } from '../../../../../dist/ionic/index.esm.js';
window.modalController = modalController;
</script>
</head>
<body>
<ion-app>
<ion-menu content-id="main-content">
<ion-content>
<ion-button onclick="openModal(event)">Open Modal</ion-button>
</ion-content>
</ion-menu>
<div class="ion-page" id="main-content">
<ion-header>
<ion-toolbar>
<ion-title>Modal - Inline</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button id="create" onclick="createModal()">Create a Modal</ion-button>
<ion-button id="present" onclick="presentHiddenModal()">Present a Hidden Modal</ion-button>
<ion-button id="create-and-present" onclick="createAndPresentModal()">Create and Present a Modal</ion-button>
<ion-button id="simulate" onclick="backButton()">Simulate Hardware Back Button</ion-button>
</ion-content>
</div>
</ion-app>
<head>
<meta charset="UTF-8">
<title>Overlays</title>
<meta name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<script type="module">
import { modalController, createAnimation } from '../../../../../dist/ionic/index.esm.js';
window.modalController = modalController;
</script>
</head>
<script>
const createModal = async () => {
const div = document.createElement('div');
div.innerHTML = `
<body>
<ion-app>
<ion-menu content-id="main-content">
<ion-content>
<ion-button onclick="openModal(event)">Open Modal</ion-button>
</ion-content>
</ion-menu>
<div class="ion-page" id="main-content">
<ion-header>
<ion-toolbar>
<ion-title>Modal - Inline</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-button id="create" onclick="createModal()">Create a Modal</ion-button>
<ion-button id="create-nested" onclick="createNestedOverlayModal()">Create Nested Overlay Modal</ion-button>
<ion-button id="present" onclick="presentHiddenModal()">Present a Hidden Modal</ion-button>
<ion-button id="create-and-present" onclick="createAndPresentModal()">Create and Present a Modal</ion-button>
<ion-button id="simulate" onclick="backButton()">Simulate Hardware Back Button</ion-button>
</ion-content>
</div>
</ion-app>
<script>
const createModal = async () => {
const div = document.createElement('div');
div.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-title>Modal</ion-title>
@@ -53,46 +57,88 @@
</ion-content>
`;
const createButton = div.querySelector('ion-button#modal-create');
createButton.onclick = () => {
createModal();
}
const createButton = div.querySelector('ion-button#modal-create');
createButton.onclick = () => {
createModal();
}
const simulateButton = div.querySelector('ion-button#modal-simulate');
simulateButton.onclick = () => {
backButton();
}
const simulateButton = div.querySelector('ion-button#modal-simulate');
simulateButton.onclick = () => {
backButton();
}
const modal = await modalController.create({
component: div
const modal = await modalController.create({
component: div
});
return modal;
}
const createAndPresentModal = async () => {
const modal = await createModal();
await modal.present();
}
const backButton = () => {
const ev = new CustomEvent('backbutton');
document.dispatchEvent(ev);
}
const presentHiddenModal = () => {
const modal = document.querySelector('ion-modal.overlay-hidden');
if (modal) {
modal.present();
}
}
const createNestedOverlayModal = async () => {
const div = document.createElement('div');
div.innerHTML = `
<ion-header>
<ion-toolbar>
<ion-title>Modal</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
Modal Content
<ion-button id="modal-nested-overlay">Datetime inline modal</ion-button>
<ion-button id="dismiss-modal-nested-overlay">Dismiss nested overlay</ion-button>
<ion-modal trigger="modal-nested-overlay">
<ion-content>
<ion-datetime show-default-buttons></ion-datetime>
</ion-content>
</ion-modal>
</ion-content>
`;
const dismissModalNestedOverlay = div.querySelector('ion-button#dismiss-modal-nested-overlay');
dismissModalNestedOverlay.onclick = () => {
window.modalController.getTop().then(top => {
top.dismiss();
});
return modal;
}
const createAndPresentModal = async () => {
const modal = await createModal();
await modal.present();
}
const modal = await modalController.create({
component: div
});
const backButton = () => {
const ev = new CustomEvent('backbutton');
document.dispatchEvent(ev);
}
const presentHiddenModal = () => {
const modal = document.querySelector('ion-modal.overlay-hidden');
if (modal) {
modal.present();
}
}
await modal.present();
window.Ionic = {
config: {
hardwareBackButton: true
}
}
</script>
return modal;
};
window.Ionic = {
config: {
hardwareBackButton: true
}
}
</script>
</body>
</body>
</html>

View File

@@ -1,6 +1,6 @@
import { newE2EPage } from '@stencil/core/testing';
test('overlays: hardware back button: should dismss a presented overlay', async () => {
test('overlays: hardware back button: should dismiss a presented overlay', async () => {
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
const createAndPresentButton = await page.find('#create-and-present');
@@ -24,7 +24,7 @@ test('overlays: hardware back button: should dismss a presented overlay', async
await page.waitForSelector('ion-modal', { hidden: true })
});
test('overlays: hardware back button: should dismss the presented overlay, even though another hidden modal was added last', async () => {
test('overlays: hardware back button: should dismiss the presented overlay, even though another hidden modal was added last', async () => {
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
const createAndPresentButton = await page.find('#create-and-present');
@@ -56,7 +56,7 @@ test('overlays: hardware back button: should dismss the presented overlay, even
expect(await modals[1].evaluate(node => node.classList.contains('overlay-hidden'))).toEqual(true);
});
test('overlays: Esc: should dismss a presented overlay', async () => {
test('overlays: Esc: should dismiss a presented overlay', async () => {
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
const createAndPresentButton = await page.find('#create-and-present');
@@ -78,7 +78,7 @@ test('overlays: Esc: should dismss a presented overlay', async () => {
});
test('overlays: Esc: should dismss the presented overlay, even though another hidden modal was added last', async () => {
test('overlays: Esc: should dismiss the presented overlay, even though another hidden modal was added last', async () => {
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
const createAndPresentButton = await page.find('#create-and-present');
@@ -102,3 +102,28 @@ test('overlays: Esc: should dismss the presented overlay, even though another hi
await page.waitForSelector('ion-modal#ion-overlay-1', { hidden: true });
});
test('overlays: Nested: should dismiss the top overlay', async () => {
const page = await newE2EPage({ url: '/src/utils/test/overlays?ionic:_testing=true' });
const createNestedButton = await page.find('#create-nested');
const ionModalDidPresent = await page.spyOnEvent('ionModalDidPresent');
await createNestedButton.click();
await ionModalDidPresent.next();
const modal = await page.find('ion-modal');
expect(modal).not.toBe(null);
const dismissNestedOverlayButton = await page.find('#dismiss-modal-nested-overlay');
const ionModalDidDismiss = await page.spyOnEvent('ionModalDidDismiss');
await dismissNestedOverlayButton.click();
await ionModalDidDismiss.next();
const modals = await page.$$('ion-modal');
expect(modals.length).toEqual(0);
});

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.3](https://github.com/ionic-team/ionic-docs/compare/v6.0.2...v6.0.3) (2022-01-19)
**Note:** Version bump only for package @ionic/docs
## [6.0.2](https://github.com/ionic-team/ionic-docs/compare/v6.0.1...v6.0.2) (2022-01-11)
**Note:** Version bump only for package @ionic/docs

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/docs",
"version": "6.0.2",
"version": "6.0.3",
"description": "Pre-packaged API documentation for the Ionic docs.",
"main": "core.json",
"types": "core.d.ts",

View File

@@ -5,5 +5,5 @@
"angular",
"packages/*"
],
"version": "6.0.2"
"version": "6.0.3"
}

View File

@@ -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.
## [6.0.3](https://github.com/ionic-team/ionic/compare/v6.0.2...v6.0.3) (2022-01-19)
### Bug Fixes
* **angular-server:** use correct @ionic/angular dependency version ([#24593](https://github.com/ionic-team/ionic/issues/24593)) ([be022f7](https://github.com/ionic-team/ionic/commit/be022f7de8df85ae842b0e111722b03448d60387)), closes [#24592](https://github.com/ionic-team/ionic/issues/24592)
## [6.0.2](https://github.com/ionic-team/ionic/compare/v6.0.1...v6.0.2) (2022-01-11)
**Note:** Version bump only for package @ionic/angular-server

View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/angular-server",
"version": "6.0.2",
"version": "6.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular-server",
"version": "6.0.1",
"version": "6.0.2",
"license": "MIT",
"devDependencies": {
"@angular-eslint/eslint-plugin": "^12.6.1",
@@ -18,7 +18,7 @@
"@angular/platform-browser": "^12.0.0",
"@angular/platform-browser-dynamic": "^12.2.10",
"@angular/platform-server": "^12.0.0",
"@ionic/core": "6.0.0",
"@ionic/core": "^6.0.2",
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@typescript-eslint/eslint-plugin": "^5.2.0",
@@ -31,7 +31,7 @@
"peerDependencies": {
"@angular/core": ">=12.0.0",
"@angular/platform-server": ">=12.0.0",
"@ionic/angular": "6.0.0",
"@ionic/angular": "^6.0.2",
"rxjs": ">=6.6.0",
"zone.js": ">=0.11.0"
}
@@ -786,12 +786,12 @@
"license": "BSD-3-Clause"
},
"node_modules/@ionic/core": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.0.0.tgz",
"integrity": "sha512-3e5EJhDebK4pCiFREpNB95o2kBSAdhRb3eMsBDOCYWYuFlcZEOGOpiGx+kYF/klYVQnB45UXAmR8nCX1indmHQ==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.0.2.tgz",
"integrity": "sha512-PE1dfI+klx9sNBFWLLX06jlP05ciQ2rvAJOJTJeo0ydqiIaK1ZELByRwjxaRC29QMXEuiCwANesmVIKScHHz8w==",
"dev": true,
"dependencies": {
"@stencil/core": "~2.11.0-0",
"@stencil/core": "~2.12.0",
"ionicons": "^6.0.0",
"tslib": "^2.1.0"
}
@@ -1158,9 +1158,9 @@
"license": "MIT"
},
"node_modules/@stencil/core": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.11.0.tgz",
"integrity": "sha512-/IubCWhVXCguyMUp/3zGrg3c882+RJNg/zpiKfyfJL3kRCOwe+/MD8OoAXVGdd+xAohZKIi1Ik+EHFlsptsjLg==",
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.12.1.tgz",
"integrity": "sha512-u24TZ+FEvjnZt5ZgIkLjLpUNsO6Ml3mUZqwmqk81w6RWWz75hgB5p4RFI5rvuErFeh2xvMIGo+pNdG24XUBz1A==",
"dev": true,
"bin": {
"stencil": "bin/stencil"
@@ -7116,12 +7116,12 @@
"dev": true
},
"@ionic/core": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.0.0.tgz",
"integrity": "sha512-3e5EJhDebK4pCiFREpNB95o2kBSAdhRb3eMsBDOCYWYuFlcZEOGOpiGx+kYF/klYVQnB45UXAmR8nCX1indmHQ==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.0.2.tgz",
"integrity": "sha512-PE1dfI+klx9sNBFWLLX06jlP05ciQ2rvAJOJTJeo0ydqiIaK1ZELByRwjxaRC29QMXEuiCwANesmVIKScHHz8w==",
"dev": true,
"requires": {
"@stencil/core": "~2.11.0-0",
"@stencil/core": "~2.12.0",
"ionicons": "^6.0.0",
"tslib": "^2.1.0"
},
@@ -7337,9 +7337,9 @@
}
},
"@stencil/core": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.11.0.tgz",
"integrity": "sha512-/IubCWhVXCguyMUp/3zGrg3c882+RJNg/zpiKfyfJL3kRCOwe+/MD8OoAXVGdd+xAohZKIi1Ik+EHFlsptsjLg==",
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.12.1.tgz",
"integrity": "sha512-u24TZ+FEvjnZt5ZgIkLjLpUNsO6Ml3mUZqwmqk81w6RWWz75hgB5p4RFI5rvuErFeh2xvMIGo+pNdG24XUBz1A==",
"dev": true
},
"@types/estree": {

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular-server",
"version": "6.0.2",
"version": "6.0.3",
"description": "Angular SSR Module for Ionic",
"keywords": [
"ionic",
@@ -39,7 +39,7 @@
"peerDependencies": {
"@angular/core": ">=12.0.0",
"@angular/platform-server": ">=12.0.0",
"@ionic/angular": "6.0.0",
"@ionic/angular": "^6.0.2",
"rxjs": ">=6.6.0",
"zone.js": ">=0.11.0"
},
@@ -53,7 +53,7 @@
"@angular/platform-browser": "^12.0.0",
"@angular/platform-browser-dynamic": "^12.2.10",
"@angular/platform-server": "^12.0.0",
"@ionic/core": "^6.0.2",
"@ionic/core": "^6.0.3",
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@typescript-eslint/eslint-plugin": "^5.2.0",

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.3](https://github.com/ionic-team/ionic/compare/v6.0.2...v6.0.3) (2022-01-19)
**Note:** Version bump only for package @ionic/react-router
## [6.0.2](https://github.com/ionic-team/ionic/compare/v6.0.1...v6.0.2) (2022-01-11)
**Note:** Version bump only for package @ionic/react-router

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/react-router",
"version": "6.0.2",
"version": "6.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/react-router",
"version": "6.0.2",
"version": "6.0.3",
"description": "React Router wrapper for @ionic/react",
"keywords": [
"ionic",
@@ -37,7 +37,7 @@
"dist/"
],
"dependencies": {
"@ionic/react": "^6.0.2",
"@ionic/react": "^6.0.3",
"tslib": "*"
},
"peerDependencies": {

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.3](https://github.com/ionic-team/ionic/compare/v6.0.2...v6.0.3) (2022-01-19)
### Bug Fixes
* **react:** add useRef wrapper to useIonOverlay state to avoid stale references ([#24553](https://github.com/ionic-team/ionic/issues/24553)) ([bce849c](https://github.com/ionic-team/ionic/commit/bce849c5f324522002eff7f8a5e5023150e9201c))
* **react:** prevent errors when dismissing inline popover after containing element is removed ([#24569](https://github.com/ionic-team/ionic/issues/24569)) ([c8a392a](https://github.com/ionic-team/ionic/commit/c8a392aef5fbf25f59a573897d970c41abac04d2))
## [6.0.2](https://github.com/ionic-team/ionic/compare/v6.0.1...v6.0.2) (2022-01-11)

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/react",
"version": "6.0.2",
"version": "6.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/react",
"version": "6.0.2",
"version": "6.0.3",
"description": "React specific wrapper for @ionic/core",
"keywords": [
"ionic",
@@ -41,7 +41,7 @@
"css/"
],
"dependencies": {
"@ionic/core": "^6.0.2",
"@ionic/core": "^6.0.3",
"ionicons": "^6.0.0",
"tslib": "*"
},

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom';
import { ReactComponentOrElement } from '../models';
@@ -25,12 +25,29 @@ export const IonOverlayManager: React.FC<IonOverlayManagerProps> = ({
onAddOverlay,
onRemoveOverlay,
}) => {
const [overlays, setOverlays] = useState<{
type OverlaysList = {
[key: string]: {
component: any;
containerElement: HTMLDivElement;
};
}>({});
};
/**
* Because of the way we're passing around the addOverlay and removeOverlay
* callbacks, by the time they finally get called, they use a stale reference
* to the state that only has the initial values. So if two overlays are opened
* at the same time, both using useIonModal or similar (such as through nesting),
* the second will erase the first from the overlays list. This causes the content
* of the first overlay to unmount.
*
* We wrap the state in useRef to ensure the two callbacks always use the most
* up-to-date version.
*
* Further reading: https://stackoverflow.com/a/56554056
*/
const [overlays, setOverlays] = useState<OverlaysList>({});
const overlaysRef = useRef<OverlaysList>({});
overlaysRef.current = overlays;
useEffect(() => {
/* Setup the callbacks that get called from <IonApp /> */
@@ -43,13 +60,13 @@ export const IonOverlayManager: React.FC<IonOverlayManagerProps> = ({
component: ReactComponentOrElement,
containerElement: HTMLDivElement
) => {
const newOverlays = { ...overlays };
const newOverlays = { ...overlaysRef.current };
newOverlays[id] = { component, containerElement };
setOverlays(newOverlays);
};
const removeOverlay = (id: string) => {
const newOverlays = { ...overlays };
const newOverlays = { ...overlaysRef.current };
delete newOverlays[id];
setOverlays(newOverlays);
};

View File

@@ -78,10 +78,19 @@ export const createInlineOverlayComponent = <PropType, ElementType>(
* cleanup properly.
*/
this.ref.current?.addEventListener('didDismiss', (evt: any) => {
const wrapper = this.wrapperRef.current!;
this.ref.current!.append(wrapper);
const wrapper = this.wrapperRef.current;
const el = this.ref.current;
this.setState({ isOpen: false });
/**
* This component might be unmounted already, if the containing
* element was removed while the popover was still open. (For
* example, if an item contains an inline popover with a button
* that removes the item.)
*/
if (wrapper && el) {
el.append(wrapper);
this.setState({ isOpen: false });
}
this.props.onDidDismiss && this.props.onDidDismiss(evt);
});

View File

@@ -18,4 +18,12 @@ describe('IonPopover', () => {
cy.get('ion-popover ion-list-header').contains('Ionic');
});
it('display popover and remove containing element', () => {
//show popover, remove containing item
cy.get('#openPopover').click();
cy.get('#removeItem').click();
//verify popover is gone
cy.get('#popoverInItem').should('not.exist');
});
});

View File

@@ -30,6 +30,8 @@ const PopoverComponent: React.FC = () => {
event: undefined,
});
const [renderItem, setRenderItem] = useState(true);
return (
<IonPage>
<IonContent>
@@ -70,6 +72,12 @@ const PopoverComponent: React.FC = () => {
>
Show Popover, hide after 250 ms
</IonButton>
{renderItem && <IonItem>
<IonButton id="openPopover">Open</IonButton>
<IonPopover id="popoverInItem" trigger="openPopover" dismissOnSelect={true}>
<IonButton id="removeItem" onClick={() => setRenderItem(false)}>Remove Item</IonButton>
</IonPopover>
</IonItem>}
</IonContent>
</IonPage>
);

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.3](https://github.com/ionic-team/ionic/compare/v6.0.2...v6.0.3) (2022-01-19)
**Note:** Version bump only for package @ionic/vue-router
## [6.0.2](https://github.com/ionic-team/ionic/compare/v6.0.1...v6.0.2) (2022-01-11)

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/vue-router",
"version": "6.0.2",
"version": "6.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/vue-router",
"version": "6.0.2",
"version": "6.0.3",
"description": "Vue Router integration for @ionic/vue",
"scripts": {
"prepublishOnly": "npm run build",
@@ -44,7 +44,7 @@
},
"homepage": "https://github.com/ionic-team/ionic#readme",
"dependencies": {
"@ionic/vue": "^6.0.2"
"@ionic/vue": "^6.0.3"
},
"devDependencies": {
"@types/jest": "^26.0.13",

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.3](https://github.com/ionic-team/ionic/compare/v6.0.2...v6.0.3) (2022-01-19)
**Note:** Version bump only for package @ionic/vue
## [6.0.2](https://github.com/ionic-team/ionic/compare/v6.0.1...v6.0.2) (2022-01-11)

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/vue",
"version": "6.0.2",
"version": "6.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/vue",
"version": "6.0.2",
"version": "6.0.3",
"description": "Vue specific wrapper for @ionic/core",
"scripts": {
"prepublishOnly": "npm run build",
@@ -60,7 +60,7 @@
"vue-router": "^4.0.0-rc.4"
},
"dependencies": {
"@ionic/core": "^6.0.2",
"@ionic/core": "^6.0.3",
"ionicons": "^6.0.0"
},
"vetur": {