Compare commits

..

24 Commits

Author SHA1 Message Date
Liam DeBeasi
02ef5ae179 5.9.3 2021-12-15 10:10:07 -05:00
Sean Perkins
4aab72b061 fix(vue): tabs no longer get unmounted when navigating back to a tabs context (#24337)
resolves #24332

Co-authored-by: Liam DeBeasi <liamdebeasi@icloud.com>
2021-12-15 09:55:29 -05:00
Liam DeBeasi
9c9e28ccc9 perf(content): remove global click listener to improve interaction performance (#24360)
resolves #24359
2021-12-15 09:55:22 -05:00
Liam DeBeasi
1c2875044a fix(vue): improve query params handling in tabs (#24355)
resolves #24353
2021-12-15 09:55:05 -05:00
Liam DeBeasi
d665ace5c4 chore(): create 5.9.x history
chore(): create 5.9.x history
2021-12-08 09:06:27 -05:00
Liam DeBeasi
351c30ce42 merge release-5.8.5
Release 5.8.5
2021-10-27 09:15:12 -04:00
Liam DeBeasi
3b9b9082b8 merge release-5.8.2
Release 5.8.2
2021-10-06 10:24:24 -04:00
Liam DeBeasi
0774cca2cd merge release-5.8.1
Release 5.8.1
2021-09-22 10:48:57 -04:00
Liam DeBeasi
6c366aaf87 merge release-5.8.0
Release 5.8.0
2021-09-15 11:37:12 -04:00
Liam DeBeasi
6876fd089f merge release-5.7.0
Release 5.7.0
2021-09-01 10:07:42 -04:00
Liam DeBeasi
22a8842ac2 merge release-5.6.14
Release 5.6.14
2021-08-18 09:33:30 -04:00
Liam DeBeasi
2d5faa75db merge release-5.6.13
Release 5.6.13
2021-08-04 10:25:37 -04:00
Liam DeBeasi
cab2a5103f merge release-5.6.12
Release 5.6.12
2021-07-21 09:37:04 -04:00
Liam DeBeasi
d36050918a merge release-5.6.11
Release 5.6.11
2021-07-01 12:03:38 -04:00
Liam DeBeasi
64f128be07 merge release-5.6.10
Release 5.6.10
2021-06-22 10:01:58 -04:00
Liam DeBeasi
87999e3c7a merge release-5.6.9
Release 5.6.9
2021-06-08 09:38:37 -04:00
Liam DeBeasi
bb4554211d merge release-5.6.8
Release 5.6.8
2021-05-27 16:01:36 -04:00
Liam DeBeasi
f71109b088 merge release-5.6.7
Release 5.6.7
2021-05-13 10:00:26 -04:00
Liam DeBeasi
44e18bd795 merge release-5.6.6
Release 5.6.6
2021-04-29 10:31:45 -04:00
Liam DeBeasi
f4d265eb60 merge release-5.6.5
Release 5.6.5
2021-04-22 13:37:44 -04:00
Liam DeBeasi
1e8dfb7d85 merge release-5.6.4
Release 5.6.4
2021-04-08 13:14:26 -04:00
Liam DeBeasi
9f023c92c4 merge release-5.6.3
Release 5.6.3
2021-03-23 11:21:16 -04:00
Liam DeBeasi
694d47b794 merge release-5.6.2
Release 5.6.2
2021-03-22 17:07:08 -04:00
Liam DeBeasi
b87c555a6e merge release-5.6.1
Release 5.6.1
2021-03-18 09:36:55 -04:00
682 changed files with 53089 additions and 129084 deletions

15
.github/CODEOWNERS vendored
View File

@@ -1,15 +0,0 @@
# Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners.
# More details are here: https://help.github.com/articles/about-codeowners/
# The '*' pattern is global owners.
# Order is important. The last matching pattern has the most precedence.
# The folders are ordered as follows:
# In each subsection folders are ordered first by depth, then alphabetically.
# This should make it easy to add new rules without breaking existing ones.
# Global owners
* @ionic-team/framework-core

View File

@@ -12,7 +12,6 @@
- [Accessibility](#accessibility)
* [Checkbox](#checkbox)
* [Switch](#switch)
* [Accordion](#accordion)
- [Rendering Anchor or Button](#rendering-anchor-or-button)
* [Example Components](#example-components-1)
* [Component Structure](#component-structure-1)
@@ -625,19 +624,6 @@ You are currently on a switch. To select or deselect this checkbox, press Contro
There is a WebKit bug open for this: https://bugs.webkit.org/show_bug.cgi?id=196354
### Accordion
#### Example Components
- [ion-accordion](https://github.com/ionic-team/ionic/tree/master/core/src/components/accordion)
- [ion-accordion-group](https://github.com/ionic-team/ionic/tree/master/core/src/components/accordion-group)
#### NVDA
In order to use the arrow keys to navigate the accordions, users must be in "Focus Mode". Typically, NVDA automatically switches between Browse and Focus modes when inside of a form, but not every accordion needs a form.
You can either wrap your `ion-accordion-group` in a form, or manually toggle Focus Mode using NVDA's keyboard shortcut.
## Rendering Anchor or Button

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -13,14 +13,14 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json')}}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json')}}-v1
- name: Cache Angular Node Modules
uses: actions/cache@v2
env:
cache-name: angular-node-modules
with:
path: ./angular/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./angular/package-lock.json')}}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./angular/package-lock.json')}}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core

View File

@@ -14,7 +14,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- name: Install Dependencies
run: npm install
working-directory: ./core

View File

@@ -13,16 +13,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
- name: Install Dependencies
run: npm install --legacy-peer-deps
shell: bash
working-directory: ./packages/react-router
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-react
path: ./packages/react
filename: ReactBuild.zip
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
@@ -49,6 +40,10 @@ runs:
run: npm run build
shell: bash
working-directory: ./packages/react-router
- name: Test Spec
run: npm run test.spec
shell: bash
working-directory: ./packages/react-router
- uses: ./.github/workflows/actions/upload-archive
with:
name: ionic-react-router

View File

@@ -13,7 +13,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core

View File

@@ -13,7 +13,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core

View File

@@ -13,7 +13,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core

View File

@@ -13,7 +13,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core
@@ -30,7 +30,7 @@ runs:
path: ./packages/angular-server
filename: AngularServerBuild.zip
- name: Install Dependencies
run: npm install
run: npm install --legacy-peer-deps
shell: bash
working-directory: ./angular/test/test-app
- name: Run Tests

View File

@@ -13,7 +13,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core

View File

@@ -13,7 +13,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- name: Lint
run: npm run lint
shell: bash

View File

@@ -18,7 +18,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core

View File

@@ -18,7 +18,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core

View File

@@ -13,7 +13,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core

View File

@@ -13,7 +13,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core

View File

@@ -13,7 +13,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core

View File

@@ -13,7 +13,7 @@ runs:
cache-name: core-node-modules
with:
path: ./core/node_modules
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v1
- uses: ./.github/workflows/actions/download-archive
with:
name: ionic-core

View File

@@ -6,37 +6,13 @@ on:
jobs:
dev-build:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.dev-build.outputs.version }}
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/setup-node@v1
with:
node-version: 16
- name: Install Dependencies
run: npm ci --no-package-lock && lerna bootstrap --ignore-scripts -- --legacy-peer-deps
shell: bash
- name: Prepare NPM Token
run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc
node-version: 15.x
- name: Create Dev Build
run: npm run release.dev -- --skip-prompt
shell: bash
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Create Dev Hash
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
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
shell: bash
- id: dev-build
run: echo "::set-output name=version::$(echo "${{ env.CURRENT_VERSION }}")-dev.$(echo "${{ env.TIMESTAMP }}").$(echo "${{ env.HASH }}")"
get-build:
name: Get your dev build!
runs-on: ubuntu-latest
needs: dev-build
steps:
- run: echo ${{ needs.dev-build.outputs.version }}

View File

@@ -1,48 +0,0 @@
name: 'Ionic Pre-Release'
on:
workflow_dispatch:
inputs:
version:
required: true
type: choice
description: Which version should be published?
options:
- prepatch
- preminor
- premajor
prefix:
required: true
type: choice
description: What kind of pre-release is this?
default: beta
options:
- alpha
- beta
- rc
jobs:
build-ionic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 16
- name: Configure Identity
run: |
git config user.name github-actions
git config user.email github-actions@github.com
shell: bash
- name: Install Dependencies
run: npm ci --no-package-lock && lerna bootstrap --ignore-scripts -- --legacy-peer-deps
shell: bash
- name: Prepare NPM Token
run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc
shell: bash
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Release
run: |
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ github.event.inputs.version }}") --no-verify-access --yes --force-publish='*' --dist-tag next --no-git-tag-version --no-push --skip-npm --preid $(echo "${{ github.events.inputs.prefix }}")
shell: bash

View File

@@ -1,51 +0,0 @@
name: 'Ionic Production Release'
on:
workflow_dispatch:
inputs:
version:
required: true
type: choice
description: Which version should be published?
options:
- patch
- minor
- major
tag:
required: true
type: choice
description: Which npm tag should this be published to?
options:
- latest
- v5-lts
- v4-lts
jobs:
build-ionic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: actions/setup-node@v2
with:
node-version: 16
- name: Configure Identity
run: |
git config user.name github-actions
git config user.email github-actions@github.com
shell: bash
- name: Install Dependencies
run: npm ci --no-package-lock && lerna bootstrap --ignore-scripts -- --legacy-peer-deps
shell: bash
- name: Prepare NPM Token
run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc
shell: bash
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Release
run: |
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ github.event.inputs.version }}") --no-verify-access --yes --force-publish='*' --dist-tag $(echo "${{ github.event.inputs.tag }}") --conventional-commits --create-release github
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash

2
.gitignore vendored
View File

@@ -66,5 +66,3 @@ core/loader/
core/www/
.stencil/
angular/build/
.npmrc

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
package-lock=false

View File

@@ -8,15 +8,15 @@ const fs = require('fs');
// core
{
files: [
'../core/css/core.css',
'../core/css/core.css.map',
'../core/css/normalize.css',
'../core/css/normalize.css.map',
'../core/components/index.js',
'../core/components/index.d.ts',
'../core/css/core.css',
'../core/css/core.css.map',
'../core/css/normalize.css',
'../core/css/normalize.css.map',
'../core/components/index.js',
'../core/components/index.d.ts',
'../core/components/package.json',
'../core/dist/index.js',
'../core/dist/ionic/index.esm.js',
'../core/dist/index.js',
'../core/dist/ionic/index.esm.js',
]
},
// hydrate
@@ -31,17 +31,19 @@ const fs = require('fs');
{
files: [
'../angular/dist/schematics/collection.json',
'../angular/dist/fesm5/ionic-angular.js',
'../angular/dist/fesm2015/ionic-angular.js',
'../angular/dist/esm2015/ionic-angular.js',
'../angular/dist/ionic-angular.d.ts'
'../angular/dist/ionic-angular.d.ts',
'../angular/dist/ionic-angular.metadata.json'
]
},
// angular-server
{
files: [
'../packages/angular-server/dist/esm2015/ionic-angular-server.js',
'../packages/angular-server/dist/fesm5/ionic-angular-server.js',
'../packages/angular-server/dist/fesm2015/ionic-angular-server.js',
'../packages/angular-server/dist/ionic-angular-server.d.ts'
'../packages/angular-server/dist/ionic-angular-server.d.ts',
'../packages/angular-server/dist/ionic-angular-server.metadata.json'
]
},
// react

107
BETA.md
View File

@@ -1,107 +0,0 @@
# Ionic Framework v6 Beta
Thanks for your interest in trying out the Framework v6 beta! We are looking for developers to help test our new changes and provide feedback so that we can make Framework v6 the best release yet! Follow this guide to get setup with the beta.
## Installation
We have worked to make the Framework v6 migration as easy as possible, so the upgrade process should be a breeze!
Developers can follow the guide below to begin updating their existing apps to Framework v6. If you want to try out Framework v6 in a new app, you can create a starter application using `ionic start` with the Ionic CLI and then follow the guide below. See https://ionicframework.com/docs/intro/cli for information on how to get started with a new Ionic Framework application.
> Note: Framework v6 is currently in beta, so do not push any apps running v6 to production!
### Ionic Vue
Ionic Vue developers should first begin by upgrading to the latest version of `vue` and `vue-router`. As of Framework v6, `vue@3.0.6+` is required.
```shell
npm install vue@next vue-router@4
```
Ionic Vue users have access to the new Custom Elements build of Framework v6. To make the most out of this improvement, we recommend using Webpack 5. To do this, developers should first install the latest version of the Vue CLI:
```shell
npm install -g @vue/cli@next
```
From there, they can upgrade all Vue CLI plugins which will automatically migrate them to Webpack 5:
```shell
vue upgrade --next
```
The new Vue CLI will automatically generate two different bundles based on your `browserslist` configuration: one for modern browsers and one for legacy browsers. New Ionic Vue starter apps will only generate the bundle for modern browsers, but some older starter apps may need to have their `.browserslistrc` file updated. You can ensure your app only builds for modern browsers by setting `.browserlistrc` to have the following content:
```
> 1%, last 2 versions, not dead, not ie 11
```
From there, developers can install the Framework v6 beta:
```shell
npm install @ionic/vue@next @ionic/vue-router@next
```
Next, developers should review the breaking changes and make any changes necessary in their apps: https://github.com/ionic-team/ionic-framework/blob/next/BREAKING.md
After that, you should be good to go! Check out https://beta.ionicframework.com/docs for the Framework v6 documentation.
### Ionic React
Ionic React developers should first begin by upgrading to the latest version of `react` and `react-dom`. As of Framework v6, `react@17+` is required:
```shell
npm install react@latest react-dom@latest
```
From there, developers can install the Framework v6 beta:
```shell
npm install @ionic/react@next @ionic/react-router@next
```
Next, developers should review the breaking changes and make any changes necessary in their apps: https://github.com/ionic-team/ionic-framework/blob/next/BREAKING.md
After that, you should be good to go! Be sure to review the other breaking changes: https://github.com/ionic-team/ionic-framework/blob/next/BREAKING.md
Check out https://beta.ionicframework.com/docs for the Framework v6 documentation.
### Ionic Angular
Ionic Angular developers should first begin by upgrading to the latest version of Angular. As of Framework v6, Angular 11+ is required.
Please see https://update.angular.io/ for a guide on how to update to the latest version of Angular.
From there, developers can install the Framework v6 beta:
```shell
npm install @ionic/angular@next
```
Next, developers should review the breaking changes and make any changes necessary in their apps: https://github.com/ionic-team/ionic-framework/blob/next/BREAKING.md
After that, you should be good to go! Check out https://beta.ionicframework.com/docs for the Framework v6 documentation.
### Ionic Core
Developers using `@ionic/core` directly should install the Framework v6 beta directly:
```shell
npm install @ionic/core@next
```
If you are using Ionic Framework in a Stencil app, be sure to update to the latest version of Stencil as well:
```shell
npm install @stencil/core@latest
```
Next, developers should review the breaking changes and make any changes necessary in their apps: https://github.com/ionic-team/ionic-framework/blob/next/BREAKING.md
After that, you should be good to go! Check out https://beta.ionicframework.com/docs for the Framework v6 documentation.
## Providing Feedback
Feedback should be provided on our GitHub repo by creating a new issue: https://github.com/ionic-team/ionic-framework/issues/new/choose
Please note in the issue title that you are using the Framework v6 beta!

View File

@@ -4,402 +4,11 @@ This is a comprehensive list of the breaking changes introduced in the major ver
## Versions
- [Version 6.x](#version-6x)
- [Version 5.x](#version-5x)
- [Version 4.x](#version-4x)
- [Legacy](#legacy)
## Version 6.x
- [Components](#components)
* [Datetime](#datetime)
* [Header](#header)
* [Icons](#icons)
* [Input](#input)
* [Modal](#modal)
* [Popover](#popover)
* [Radio](#radio)
* [Searchbar](#searchbar)
* [Select](#select)
* [Tab Bar](#tab-bar)
* [Textarea](#textarea)
* [Toast](#toast)
* [Toolbar](#toolbar)
- [Config](#config)
* [Transition Shadow](#transition-shadow)
- [Angular](#angular)
* [Config](#config-1)
- [Vue](#vue)
* [Config](#config-2)
* [Tabs Config](#tabs-config)
* [Tabs Router Outlet](#tabs-router-outlet)
* [Overlay Events](#overlay-events)
* [Utility Function Types](#utility-function-types)
- [React](#react)
* [Config](#config-3)
- [Browser and Platform Support](#browser-and-platform-support)
### Components
#### Datetime
The `ion-datetime` component has undergone a complete rewrite and uses a new calendar style. As a result, some of the properties no longer apply and have been removed.
- `ion-datetime` now displays the calendar inline by default, allowing for more flexibility in presentation. As a result, the `placeholder` property has been removed. Additionally, the `text` and `placeholder` Shadow Parts have been removed.
- The `--padding-bottom`, `--padding-end`, `--padding-start`, `--padding-top`, and `--placeholder-color` CSS Variables have been removed since `ion-datetime` now displays inline by default.
- The `displayFormat` and `displayTimezone` properties have been removed since `ion-datetime` now displays inline with a calendar picker. To parse the UTC string provided in the payload of the `ionChange` event, we recommend using a 3rd-party date library like [date-fns](https://date-fns.org/). Here is an example of how you can take the UTC string from `ion-datetime` and format it to whatever style you prefer:
```typescript
import { format, parseISO } from 'date-fns';
/**
* This is provided in the event
* payload from the `ionChange` event.
*/
const dateFromIonDatetime = '2021-06-04T14:23:00-04:00';
const formattedString = format(parseISO(dateFromIonDatetime), 'MMM d, yyyy');
console.log(formattedString); // Jun 4, 2021
```
- The `pickerOptions` and `pickerFormat` properties have been removed since `ion-datetime` now uses a calendar style rather than a wheel picker style.
- The `monthNames`, `monthShortNames`, `dayNames`, and `dayShortNames` properties have been removed. `ion-datetime` can now automatically format these values according to your devices locale thanks to the [Intl.DateTimeFormat API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat). If you wish to force a specific locale, you can use the new `locale` property:
```html
<ion-datetime locale="fr-FR"></ion-datetime>
```
- The `open` method has been removed. To present the datetime in an overlay, you can pass it into an `ion-modal` or `ion-popover` component and call the `present` method on the overlay instance. Alternatively, you can use the `trigger` property on `ion-modal` or `ion-popover` to present the overlay on a button click:
```html
<ion-button id="open-modal">Open Datetime Modal</ion-button>
<ion-modal trigger="open-modal">
<ion-datetime></ion-datetime>
</ion-modal>
```
#### Header
When using a collapsible large title, the last toolbar in the header with `collapse="condense"` no longer has a border. This does not affect the toolbar when the large title is collapsed.
To get the old style back, add the following CSS to your global stylesheet:
```css
ion-header.header-collapse-condense ion-toolbar:last-of-type {
--border-width: 0 0 0.55px;
}
```
#### Icons
Ionic 6 now ships with Ionicons 6. Please be sure to review the [Ionicons 6.0.0 Changelog](https://github.com/ionic-team/ionicons/releases/tag/v6.0.0) and make any necessary changes.
#### Input
The `placeholder` property now has a type of `string | undefined` rather than `null | string | undefined`.
#### Modal
Converted `ion-modal` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
If you were targeting the internals of `ion-modal` in your CSS, you will need to target the `backdrop` or `content` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.
Developers dynamically creating modals using `document.createElement('ion-modal')` will now need to call `modal.remove()` after the modal has been dismissed if they want the modal to be removed from the DOM.
#### Popover
Converted `ion-popover` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
If you were targeting the internals of `ion-popover` in your CSS, you will need to target the `backdrop`, `arrow`, or `content` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.
Developers dynamically creating popovers using `document.createElement('ion-popover')` will now need to call `popover.remove()` after the popover has been dismissed if they want the popover to be removed from the DOM.
#### Radio
The `RadioChangeEventDetail` interface has been removed. Instead, listen for the `ionChange` event on `ion-radio-group` and use the `RadioGroupChangeEventDetail` interface.
#### Searchbar
The `showClearButton` property now defaults to `'always'` for improved usability with screen readers.
To get the old behavior, set `showClearButton` to `'focus'`.
#### Select
The `placeholder` property now has a type of `string | undefined` rather than `null | string | undefined`.
#### Tab Bar
The default iOS tab bar background color has been updated to better reflect the latest iOS styles. The new default value is:
```css
var(--ion-tab-bar-background, var(--ion-color-step-50, #f7f7f7));
```
#### Textarea
The `placeholder` property now has a type of `string | undefined` rather than `null | string | undefined`.
#### Toast
The `--white-space` CSS variable now defaults to `normal` instead of `pre-wrap`.
#### Toolbar
The default iOS toolbar background color has been updated to better reflect the latest iOS styles. The new default value is:
```css
var(--ion-toolbar-background, var(--ion-color-step-50, #f7f7f7));
```
### Config
#### Transition Shadow
The `experimentalTransitionShadow` config option has been removed. The transition shadow is now enabled when running in `ios` mode.
### Angular
#### Config
The `Config.set()` method has been removed. See https://ionicframework.com/docs/angular/config for examples on how to set config globally, per-component, and per-platform.
Additionally, the `setupConfig` function is no longer exported from `@ionic/angular`. Developers should use `IonicModule.forRoot` to set the config instead. See https://ionicframework.com/docs/angular/config for more information.
### React
#### Config
All Ionic React applications must now import `setupIonicReact` from `@ionic/react` and call it. If you are setting a custom config with `setupConfig`, pass your config directly to `setupIonicReact` instead:
**Old**
```javascript
import { setupConfig } from '@ionic/react';
setupConfig({
mode: 'md'
})
```
**New**
```javascript
import { setupIonicReact } from '@ionic/react';
setupIonicReact({
mode: 'md'
})
```
Note that all Ionic React applications must call `setupIonicReact` even if they are not setting custom configuration.
Additionally, the `setupConfig` function is no longer exported from `@ionic/react`.
### Vue
#### Config
The `setupConfig` function is no longer exported from `@ionic/vue`. Developers should pass their config into the `IonicVue` plugin. See https://ionicframework.com/docs/vue/config for more information.
#### Tabs Config
Support for child routes nested inside of tabs has been removed to better conform to Vue Router's best practices. Additional routes should be written as sibling routes with the parent tab as the path prefix:
**Old**
```typescript
const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/tabs/tab1'
},
{
path: '/tabs/',
component: Tabs,
children: [
{
path: '',
redirect: 'tab1'
},
{
path: 'tab1',
component: () => import('@/views/Tab1.vue'),
children: {
{
path: 'view',
component: () => import('@/views/Tab1View.vue')
}
}
},
{
path: 'tab2',
component: () => import('@/views/Tab2.vue')
},
{
path: 'tab3',
component: () => import('@/views/Tab3.vue')
}
]
}
]
```
**New**
```typescript
const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/tabs/tab1'
},
{
path: '/tabs/',
component: Tabs,
children: [
{
path: '',
redirect: 'tab1'
},
{
path: 'tab1',
component: () => import('@/views/Tab1.vue')
},
{
path: 'tab1/view',
component: () => import('@/views/Tab1View.vue')
},
{
path: 'tab2',
component: () => import('@/views/Tab2.vue')
},
{
path: 'tab3',
component: () => import('@/views/Tab3.vue')
}
]
}
]
```
In the example above `tabs/tab1/view` has been rewritten has a sibling route to `tabs/tab1`. The `path` field now includes the `tab1` prefix.
#### Tabs Router Outlet
Developers must now provide an `ion-router-outlet` inside of `ion-tabs`. Previously one was generated automatically, but this made it difficult for developers to access the properties on the generated `ion-router-outlet`.
**Old**
```html
<ion-tabs>
<ion-tab-bar slot="bottom">
...
</ion-tab-bar>
</ion-tabs>
<script>
import { IonTabs, IonTabBar } from '@ionic/vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: { IonTabs, IonTabBar }
});
</script>
```
**New**
```html
<ion-tabs>
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar slot="bottom">
...
</ion-tab-bar>
</ion-tabs>
<script>
import { IonTabs, IonTabBar, IonRouterOutlet } from '@ionic/vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: { IonTabs, IonTabBar, IonRouterOutlet }
});
</script>
```
#### Overlay Events
Overlay events `onWillPresent`, `onDidPresent`, `onWillDismiss`, and `onDidDismiss` have been removed in favor of `willPresent`, `didPresent`, `willDismiss`, and `didDismiss`.
This applies to the following components: `ion-action-sheet`, `ion-alert`, `ion-loading`, `ion-modal`, `ion-picker`, `ion-popover`, and `ion-toast`.
**Old**
```html
<ion-modal
:is-open="modalOpenRef"
@onWillPresent="onModalWillPresentHandler"
@onDidPresent="onModalDidPresentHandler"
@onWillDismiss="onModalWillDismissHandler"
@onDidDismiss="onModalDidDismissHandler"
>
...
</ion-modal>
```
**New**
```html
<ion-modal
:is-open="modalOpenRef"
@willPresent="onModalWillPresentHandler"
@didPresent="onModalDidPresentHandler"
@willDismiss="onModalWillDismissHandler"
@didDismiss="onModalDidDismissHandler"
>
...
</ion-modal>
```
#### Utility Function Types
- The `IonRouter` type for `useIonRouter` has been renamed to `UseIonRouterResult`.
- The `IonKeyboardRef` type for `useKeyboard` has been renamed to `UseKeyboardResult`.
### Browser and Platform Support
This section details the desktop browser, JavaScript framework, and mobile platform versions that are supported by Ionic Framework v6.
**Minimum Browser Versions**
| Desktop Browser | Supported Versions |
| --------------- | ----------------- |
| Chrome | 60+ |
| Safari | 13+ |
| Firefox | 63+ |
| Edge | 79+ |
**Minimum JavaScript Framework Versions**
| Framework | Supported Version |
| --------- | --------------------- |
| Angular | 12+ |
| React | 17+ |
| Vue | 3.0.6+ |
**Minimum Mobile Platform Versions**
| Platform | Supported Version |
| -------- | --------------------------------------- |
| iOS | 13+ |
| Android | 5.0+ with Chromium 60+ (See note below) |
Starting with Android 5.0, the webview was moved to a separate application that can be updated independently of Android. This means that most Android 5.0+ devices are going to be running a modern version of Chromium. However, there are a still a subset of Android devices whose manufacturer has locked the webview version and does not allow the webview to update. These webviews are typically stuck at the version that was available when the device initially shipped.
As a result, Ionic Framework only supports Android devices and emulators running Android 5.0+ with a webview of Chromium 60 or newer. For context, this is the version that Stencil can support with no polyfills: https://stenciljs.com/docs/browser-support
## Version 5.x
- [CSS](#css)

View File

@@ -1,55 +1,3 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.2](https://github.com/ionic-team/ionic-framework/compare/v6.0.1...v6.0.2) (2022-01-11)
### Bug Fixes
* **angular:** attach change detector ref for inline overlays ([#24521](https://github.com/ionic-team/ionic-framework/issues/24521)) ([5c54593](https://github.com/ionic-team/ionic-framework/commit/5c54593dde64ae61347568405ebf74502cfff370)), closes [#24502](https://github.com/ionic-team/ionic-framework/issues/24502)
* **angular:** popover will respect side attribute value ([#24470](https://github.com/ionic-team/ionic-framework/issues/24470)) ([e6955a2](https://github.com/ionic-team/ionic-framework/commit/e6955a26b92fc536c5c73b60b5943881c7d58ee1)), closes [#24466](https://github.com/ionic-team/ionic-framework/issues/24466)
* **breadcrumb:** support routerLink on breadcrumb ([#24509](https://github.com/ionic-team/ionic-framework/issues/24509)) ([5bb1414](https://github.com/ionic-team/ionic-framework/commit/5bb1414f7fa04ea07954cb3f68883ee2f162586a)), closes [#24493](https://github.com/ionic-team/ionic-framework/issues/24493)
* **css:** inline css source in source maps ([#24514](https://github.com/ionic-team/ionic-framework/issues/24514)) ([987d46c](https://github.com/ionic-team/ionic-framework/commit/987d46cfa6e48a932330f04f2e8eb7054b11baf8)), closes [#24441](https://github.com/ionic-team/ionic-framework/issues/24441)
* **datetime:** add top padding to MD calendar month grid ([#24522](https://github.com/ionic-team/ionic-framework/issues/24522)) ([bd82b5d](https://github.com/ionic-team/ionic-framework/commit/bd82b5dc1d06ba22a5410858802d57735fdcf450)), closes [#24408](https://github.com/ionic-team/ionic-framework/issues/24408)
* **datetime:** RTL will no longer infinitely scroll ([#24475](https://github.com/ionic-team/ionic-framework/issues/24475)) ([8f00008](https://github.com/ionic-team/ionic-framework/commit/8f000089c2986f292147c7f501f23c8c7d1df457)), closes [#24472](https://github.com/ionic-team/ionic-framework/issues/24472)
* **datetime:** time picker format with hourCycle h23 ([#24476](https://github.com/ionic-team/ionic-framework/issues/24476)) ([a3724e6](https://github.com/ionic-team/ionic-framework/commit/a3724e6a5662c5bc1b724d80540530472827506e)), closes [#24474](https://github.com/ionic-team/ionic-framework/issues/24474)
* **datetime:** update active day styling when day is selected ([#24454](https://github.com/ionic-team/ionic-framework/issues/24454)) ([4304391](https://github.com/ionic-team/ionic-framework/commit/430439191dba824c11290d7f8622fea10ced6c40)), closes [#24414](https://github.com/ionic-team/ionic-framework/issues/24414) [#24451](https://github.com/ionic-team/ionic-framework/issues/24451)
* **datetime:** wheel picker shows correct column order in rtl ([#24546](https://github.com/ionic-team/ionic-framework/issues/24546)) ([c90ce31](https://github.com/ionic-team/ionic-framework/commit/c90ce311a86ccb7c06b1cde91a4659f6682df04d)), closes [#24378](https://github.com/ionic-team/ionic-framework/issues/24378)
* **overlays:** define custom element children ([#24439](https://github.com/ionic-team/ionic-framework/issues/24439)) ([4715b83](https://github.com/ionic-team/ionic-framework/commit/4715b83abb30ec5930710d16e5bfe8fc88a940ce)), closes [#24393](https://github.com/ionic-team/ionic-framework/issues/24393)
* **popover:** allow arrow configuration with controller approach ([#24512](https://github.com/ionic-team/ionic-framework/issues/24512)) ([b39003a](https://github.com/ionic-team/ionic-framework/commit/b39003a4c67cd7e01d09be012c9e12d99ca1730a)), closes [#24487](https://github.com/ionic-team/ionic-framework/issues/24487)
* **radio:** fix radio not showing checked state when not in a group ([#24423](https://github.com/ionic-team/ionic-framework/issues/24423)) ([94a781c](https://github.com/ionic-team/ionic-framework/commit/94a781cb6a3d92c5e6cab1a7603bfe25826a753c))
* **react,vue:** backdrop for inline modal/popover overlay ([#24453](https://github.com/ionic-team/ionic-framework/issues/24453)) ([77f8412](https://github.com/ionic-team/ionic-framework/commit/77f8412b746222793cd9d17f12f50d512ab5e886)), closes [#24449](https://github.com/ionic-team/ionic-framework/issues/24449)
* **react:** building app for production now works correctly with vite ([#24515](https://github.com/ionic-team/ionic-framework/issues/24515)) ([32fad3d](https://github.com/ionic-team/ionic-framework/commit/32fad3d02cb6b012a772de03eafe3e3a6b1300e0)), closes [#24229](https://github.com/ionic-team/ionic-framework/issues/24229)
* **react:** scrolling to bottom of modal contents ([#24510](https://github.com/ionic-team/ionic-framework/issues/24510)) ([1462cef](https://github.com/ionic-team/ionic-framework/commit/1462cef69225e20582e2f9a0b8fd655ca2066b79)), closes [#24478](https://github.com/ionic-team/ionic-framework/issues/24478)
* **refresher:** import icons to avoid errors in react and vue ([#24525](https://github.com/ionic-team/ionic-framework/issues/24525)) ([388622f](https://github.com/ionic-team/ionic-framework/commit/388622f9734b7b832bca3ede99820a7124faa618)), closes [#24480](https://github.com/ionic-team/ionic-framework/issues/24480)
* **vue:** correct route is replaced when using router.replace ([#24533](https://github.com/ionic-team/ionic-framework/issues/24533)) ([90458da](https://github.com/ionic-team/ionic-framework/commit/90458da406e2f7a6675be185409ea78595a35128)), closes [#24226](https://github.com/ionic-team/ionic-framework/issues/24226)
## [6.0.1](https://github.com/ionic-team/ionic/compare/v6.0.0...v6.0.1) (2021-12-15)
### Bug Fixes
* **datetime:** datetime now appears correctly when presented in modal ([#24385](https://github.com/ionic-team/ionic/issues/24385)) ([e7d0674](https://github.com/ionic-team/ionic/commit/e7d06743ae2e09864510940bf8a97bc312ef1cf8)), closes [#24112](https://github.com/ionic-team/ionic-framework/issues/24112)
* **item:** remove empty padding space for item bottom ([#24323](https://github.com/ionic-team/ionic/issues/24323)) ([500985c](https://github.com/ionic-team/ionic/commit/500985ce04783f502a1f5c50fbd8b4c5e93294d7)), closes [#23892](https://github.com/ionic-team/ionic/issues/23892)
* **modal:** fix timing issue when rapidly closing and opening controller modal ([#24380](https://github.com/ionic-team/ionic/issues/24380)) ([732f8e1](https://github.com/ionic-team/ionic/commit/732f8e10ce604f1a3e98518ae9c3a4afd7803e9a)), closes [#24230](https://github.com/ionic-team/ionic-framework/issues/24230)
* **overlays:** define children custom elements for picker ([#24372](https://github.com/ionic-team/ionic/issues/24372)) ([7c700b4](https://github.com/ionic-team/ionic/commit/7c700b4caa35d7eb50c877d794f9db9fad6ed88b)), closes [#24366](https://github.com/ionic-team/ionic/issues/24366)
* **vue:** improve query params handling in tabs ([#24355](https://github.com/ionic-team/ionic/issues/24355)) ([6309d5d](https://github.com/ionic-team/ionic/commit/6309d5ddbaa7da5e37eda4e19866baf380069578)), closes [#24353](https://github.com/ionic-team/ionic/issues/24353)
* **vue:** strongly typed controller methods ([#24388](https://github.com/ionic-team/ionic/issues/24388)) ([a5d56b3](https://github.com/ionic-team/ionic/commit/a5d56b3d5a0a64fd4c62f4beab69a3a1681c0b70)), closes [#24387](https://github.com/ionic-team/ionic/issues/24387)
* **vue:** tabs no longer get unmounted when navigating back to a tabs context ([#24337](https://github.com/ionic-team/ionic/issues/24337)) ([bf8e436](https://github.com/ionic-team/ionic/commit/bf8e436ee3f7441ebbc7eaf53ec8d04545dab476)), closes [#24332](https://github.com/ionic-team/ionic/issues/24332)
### Performance Improvements
* **content:** remove global click listener to improve interaction performance ([#24360](https://github.com/ionic-team/ionic/issues/24360)) ([1bfac52](https://github.com/ionic-team/ionic/commit/1bfac52331d3f296e5721b2a6c3fd94a97450a1d)), closes [#24359](https://github.com/ionic-team/ionic/issues/24359)
## [5.9.3](https://github.com/ionic-team/ionic/compare/v5.9.2...v5.9.3) (2021-12-15)
@@ -65,34 +13,6 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
# [6.0.0 Titanium](https://github.com/ionic-team/ionic/compare/v6.0.0-rc.4...v6.0.0) (2021-12-08)
Enjoy! 🚀
> We recommend updating to version `5.9.2` before updating to version `6.0.0` in order to see deprecation warnings related to your app [in the developer console](https://javascript.info/devtools).
Please see the [Ionic 6 Upgrade Guide](https://ionicframework.com/docs/next/intro/upgrading-to-ionic-6) for a step-by-step list of what you need to do to get started with Ionic 6.
# [6.0.0-rc.4](https://github.com/ionic-team/ionic/compare/v6.0.0-rc.3...v6.0.0-rc.4) (2021-12-07)
### Bug Fixes
* **accordion:** improve functionality with nested accordions ([#24302](https://github.com/ionic-team/ionic/issues/24302)) ([0920797](https://github.com/ionic-team/ionic/commit/0920797612a5ee3aac1c38d8bffe4fd1e80b6987))
* **content:** ensure scrollEl is always available in scroll methods ([#24255](https://github.com/ionic-team/ionic/issues/24255)) ([36a096c](https://github.com/ionic-team/ionic/commit/36a096c9b60bd6b3b086f2c966a1cd40dbc54473)), closes [#24168](https://github.com/ionic-team/ionic-framework/issues/24168)
* **datetime:** keyboard navigation now works in time picker ([#24251](https://github.com/ionic-team/ionic/issues/24251)) ([8bdcd3c](https://github.com/ionic-team/ionic/commit/8bdcd3c6c99d84a0a46b0f08dceca6b6929fd8f8)), closes [#24070](https://github.com/ionic-team/ionic-framework/issues/24070)
* **datetime:** prevent multiple items from being highlighted at once in month/year and time pickers ([#24268](https://github.com/ionic-team/ionic/issues/24268)) ([c2bef8d](https://github.com/ionic-team/ionic/commit/c2bef8df14111dc00c382a3ab36c27a08a92f0b7)), closes [#24067](https://github.com/ionic-team/ionic-framework/issues/24067)
* **datetime:** update active calendar display when value changes ([#24244](https://github.com/ionic-team/ionic/issues/24244)) ([ec3bc52](https://github.com/ionic-team/ionic/commit/ec3bc52ff194f1e4db4ce49548c1418c259b8795)), closes [#24241](https://github.com/ionic-team/ionic-framework/issues/24241)
* **item:** counter property now defaults to false to make upgrade easier ([#24263](https://github.com/ionic-team/ionic/issues/24263)) ([f61f356](https://github.com/ionic-team/ionic/commit/f61f35600072c5df069a24c3b24eb8f283d586f8))
* **react, vue:** remove side effects to improve treeshaking ([#24313](https://github.com/ionic-team/ionic/issues/24313)) ([13d4418](https://github.com/ionic-team/ionic/commit/13d4418588b98d301b05ebd94e0eac670163a553)), closes [#24280](https://github.com/ionic-team/ionic-framework/issues/24280)
### Features
* **react:** add setupIonicReact function ([#24254](https://github.com/ionic-team/ionic/issues/24254)) ([55db38d](https://github.com/ionic-team/ionic/commit/55db38ddc541c2632c7d3e4e4c9400ff5b5dfe8c)), closes [#24139](https://github.com/ionic-team/ionic/issues/24139)
## [5.9.2](https://github.com/ionic-team/ionic/compare/v5.9.1...v5.9.2) (2021-12-07)
@@ -110,285 +30,6 @@ Please see the [Ionic 6 Upgrade Guide](https://ionicframework.com/docs/next/intr
# [6.0.0-rc.3](https://github.com/ionic-team/ionic/compare/v6.0.0-rc.2...v6.0.0-rc.3) (2021-11-17)
### Bug Fixes
* **all:** Ionic components that use child Ionic components are now correctly defined ([#24191](https://github.com/ionic-team/ionic/issues/24191)) ([5a2a335](https://github.com/ionic-team/ionic/commit/5a2a335784aab581cda90448193e48f687df6b15)), closes [#23571](https://github.com/ionic-team/ionic/issues/23571) [#24116](https://github.com/ionic-team/ionic/issues/24116) [#24129](https://github.com/ionic-team/ionic/issues/24129)
* **angular:** prevent duplicate event emissions ([#24200](https://github.com/ionic-team/ionic/issues/24200)) ([fc1eae9](https://github.com/ionic-team/ionic/commit/fc1eae982d7493f5b69fb18829f9c796f05a0d47))
* **icon:** update to ionicons 6 to resolve typescript 4.4 errors ([#24185](https://github.com/ionic-team/ionic/issues/24185)) ([118c606](https://github.com/ionic-team/ionic/commit/118c606703f792f830d92f1148882b5daa3f180f))
* **input:** date type in ion-input now aligns correctly on iOS 15 ([#24213](https://github.com/ionic-team/ionic/issues/24213)) ([9cf7c89](https://github.com/ionic-team/ionic/commit/9cf7c897043854a9d0db81d18ad6c016eb964de8))
* **input:** ionInput event emits with type of InputEvent ([#24111](https://github.com/ionic-team/ionic/issues/24111)) ([52cd5d0](https://github.com/ionic-team/ionic/commit/52cd5d0ccedb8013c860198fc69f6bc0d4e6d386))
* **item:** allow click targets inside of label ([#24225](https://github.com/ionic-team/ionic/issues/24225)) ([3949a94](https://github.com/ionic-team/ionic/commit/3949a949dfe112668c69a36d64e5f01a5aef1435))
* **label:** apply error appearance when control is touched ([#24072](https://github.com/ionic-team/ionic/issues/24072)) ([009dff5](https://github.com/ionic-team/ionic/commit/009dff5584fea6398bb99aa55760d25dafd7fbcc))
* **modal, popover:** opening modal and popover now works even if overlay was added to ion-app directly ([#24174](https://github.com/ionic-team/ionic/issues/24174)) ([da339a8](https://github.com/ionic-team/ionic/commit/da339a8a743548f9bde8b5a22f1a9d6b191f6e7b)), closes [#23728](https://github.com/ionic-team/ionic/issues/23728)
* **modal:** border radius is now correctly applied to card modals ([#24204](https://github.com/ionic-team/ionic/issues/24204)) ([1f4f8eb](https://github.com/ionic-team/ionic/commit/1f4f8eb6ca2b8adb543ade83c309177ac7f2044d))
* **modal:** card modal shadow now shows up correctly on ipad ([#24203](https://github.com/ionic-team/ionic/issues/24203)) ([5d4f5af](https://github.com/ionic-team/ionic/commit/5d4f5af36083eafcf7de91b22749ff307701087f))
* **overlays:** declarative modals now work properly with the hardware back button ([#24165](https://github.com/ionic-team/ionic/issues/24165)) ([b3759ae](https://github.com/ionic-team/ionic/commit/b3759aed5bd1ec6a7c744af03d0dac9c8055c5af))
* **react:** improve component compatibility with preact ([#24132](https://github.com/ionic-team/ionic/issues/24132)) ([15fc293](https://github.com/ionic-team/ionic/commit/15fc293d75aa21426616459c2596b46e2d460f49)), closes [#23516](https://github.com/ionic-team/ionic/issues/23516)
* **textarea:** floating label layout is correct with autogrow textareas ([#24202](https://github.com/ionic-team/ionic/issues/24202)) ([713f0f5](https://github.com/ionic-team/ionic/commit/713f0f55261205d3f7e25874939cb1f998f38d4a))
# [6.0.0-rc.2](https://github.com/ionic-team/ionic/compare/v6.0.0-rc.1...v6.0.0-rc.2) (2021-11-03)
### Bug Fixes
* **datetime:** resolve month and year jumping issue on ios ([#24142](https://github.com/ionic-team/ionic/issues/24142)) ([27aef93](https://github.com/ionic-team/ionic/commit/27aef9343cada9a83adec8fe00e8bc3bafa8e049)), closes [#23910](https://github.com/ionic-team/ionic/issues/23910)
# [6.0.0-rc.1](https://github.com/ionic-team/ionic/compare/v5.8.5...v6.0.0-rc.1) (2021-10-27)
### Bug Fixes
* **accordion-group:** ionChange is now fired properly in vue ([#24063](https://github.com/ionic-team/ionic/issues/24063)) ([61b99d1](https://github.com/ionic-team/ionic/commit/61b99d13bfab5c57617cbcdc7e54e43f88885f66)), closes [#23762](https://github.com/ionic-team/ionic/issues/23762)
* **datetime:** clear button is now rendered even if showDefaultButtons is false ([#24075](https://github.com/ionic-team/ionic/issues/24075)) ([e3996cf](https://github.com/ionic-team/ionic/commit/e3996cfbd50f5e9ae54ffcbe2594124e3b9969b0))
* **datetime:** default sizing preserves shape of datetime ([#24104](https://github.com/ionic-team/ionic/issues/24104)) ([71fab0f](https://github.com/ionic-team/ionic/commit/71fab0fa124254f8cdc3b513627aa7b045993f4e))
* **infinite-scroll:** infinite scroll event now fired with custom elements build ([#24043](https://github.com/ionic-team/ionic/issues/24043)) ([8a86cfb](https://github.com/ionic-team/ionic/commit/8a86cfb7050989e914fa85ccc1ea755d73f58c90)), closes [#24034](https://github.com/ionic-team/ionic/issues/24034)
* **modal:** fix backdrop animation for sheets with off-center backdropBreakpoint ([#24061](https://github.com/ionic-team/ionic/issues/24061)) ([49db6d0](https://github.com/ionic-team/ionic/commit/49db6d02883b11b5f179300e2eaa298002a381e8))
* **react:** overlays shown with useIonModal and useIonPopover no longer render outside of main react tree ([f3e492c](https://github.com/ionic-team/ionic/commit/f3e492c897c8cda2b98050156f130654f4d7014a)), closes [#23516](https://github.com/ionic-team/ionic/issues/23516) [#23516](https://github.com/ionic-team/ionic/issues/23516)
### Features
* **angular:** build for angular 12.0 ([#23970](https://github.com/ionic-team/ionic/issues/23970)) ([3451a34](https://github.com/ionic-team/ionic/commit/3451a34ad0c893be0b6c17dc91ac9a75d2b9b52c))
# [6.0.0-rc.0](https://github.com/ionic-team/ionic/compare/v6.0.0-beta.7...v6.0.0-rc.0) (2021-10-07)
### Bug Fixes
* **angular:** setup config properly ([#24028](https://github.com/ionic-team/ionic/issues/24028)) ([907996c](https://github.com/ionic-team/ionic/commit/907996ce16446d0dc12939da325b7b5dae09ebd9))
# [6.0.0-beta.7](https://github.com/ionic-team/ionic/compare/v5.8.2...v6.0.0-beta.7) (2021-10-06)
### Bug Fixes
* **datetime:** add ionBlur/ionFocus events to whole component ([#23980](https://github.com/ionic-team/ionic/issues/23980)) ([86a77bd](https://github.com/ionic-team/ionic/commit/86a77bd379c6dca57d5feb9694d18afe6d82934d))
* **datetime:** change now emitted when picker is typed into ([#24018](https://github.com/ionic-team/ionic/issues/24018)) ([0320164](https://github.com/ionic-team/ionic/commit/03201643ba9ae34fa969c1542742d9cd95298c81))
* **datetime:** ionChange is no longer called for out of range dates ([#23940](https://github.com/ionic-team/ionic/issues/23940)) ([ea39c6e](https://github.com/ionic-team/ionic/commit/ea39c6e5b3781ceb4c87277cf4a5e0be9c75bc20)), closes [#23939](https://github.com/ionic-team/ionic/issues/23939)
* **datetime:** time picker uses new iOS 15 style ([#23996](https://github.com/ionic-team/ionic/issues/23996)) ([0ab37b5](https://github.com/ionic-team/ionic/commit/0ab37b5061728bd60fd42781645b96add130a79f)), closes [#23768](https://github.com/ionic-team/ionic/issues/23768)
* **modal:** backdropBreakpoint is now an exclusive value ([#23954](https://github.com/ionic-team/ionic/issues/23954)) ([ed455ab](https://github.com/ionic-team/ionic/commit/ed455ab4c6df73f801a3c941da21261c205c9634))
* **react:** ensure inline modal content is visible ([#23968](https://github.com/ionic-team/ionic/issues/23968)) ([285a371](https://github.com/ionic-team/ionic/commit/285a371101e714e74d6df68701cbee9dfe23605e))
* **reorder-group:** wait for content to render before getting scroll position ([#24007](https://github.com/ionic-team/ionic/issues/24007)) ([225a278](https://github.com/ionic-team/ionic/commit/225a2787407c5ce68a953ee3448647d00af26517)), closes [#23875](https://github.com/ionic-team/ionic/issues/23875)
* **select:** ensure popover options with number values are searched for correctly ([#23998](https://github.com/ionic-team/ionic/issues/23998)) ([c204083](https://github.com/ionic-team/ionic/commit/c20408369bd332b5e225a3d50ec94978f6f5ec97))
* **select:** focus selected item in popovers ([#23991](https://github.com/ionic-team/ionic/issues/23991)) ([2497a53](https://github.com/ionic-team/ionic/commit/2497a53255dc43052755bba842dfcf556d930dcd))
### Features
* **all:** add CustomEvents types to components that emit events ([#23956](https://github.com/ionic-team/ionic/issues/23956)) ([8708095](https://github.com/ionic-team/ionic/commit/87080951112a409893a4bac2def1deca06642b16)), closes [#22925](https://github.com/ionic-team/ionic/issues/22925)
* **header, footer:** add ios fading header style ([#24011](https://github.com/ionic-team/ionic/issues/24011)) ([7ce3959](https://github.com/ionic-team/ionic/commit/7ce3959b66a08e980c7dac3bb7d7df6bf0ae874e))
### BREAKING CHANGES
* **all:** The `RadioChangeEventDetail` interface has been removed in favor of `RadioGroupChangeEventDetail`.
# [6.0.0-beta.6](https://github.com/ionic-team/ionic/compare/v5.8.0...v6.0.0-beta.6) (2021-09-15)
### Bug Fixes
* **modal:** add sheet modal properties for angular ([#23899](https://github.com/ionic-team/ionic/issues/23899)) ([d1763fc](https://github.com/ionic-team/ionic/commit/d1763fc8b56c8cb5272224ae0faaebfe3e516fdb))
* **modal:** expose breakpoint props in ModalOptions interface ([#23867](https://github.com/ionic-team/ionic/issues/23867)) ([5fd80fd](https://github.com/ionic-team/ionic/commit/5fd80fd43885a5d0cd65f0eef4e0ff15e82c4fe0)), closes [#23866](https://github.com/ionic-team/ionic/issues/23866)
* **modal:** handle on sheet modal can now be turned off ([#23900](https://github.com/ionic-team/ionic/issues/23900)) ([e2d2ad6](https://github.com/ionic-team/ionic/commit/e2d2ad6f8eaf798c6f4b4a69f2b8176f0ac22d32))
* **modal:** modal displays in middle of screen on desktop ([#23911](https://github.com/ionic-team/ionic/issues/23911)) ([9d87028](https://github.com/ionic-team/ionic/commit/9d87028e81723a0f1498c8cf231319676078eda0))
* **modal:** sheet animation works correctly if breakpoints value does not include 1 ([#23927](https://github.com/ionic-team/ionic/issues/23927)) ([414f246](https://github.com/ionic-team/ionic/commit/414f24685cbc67a7fff142b7786d33ce1cd67a0c))
* **modal:** sheet modal handle is now positioned correctly ([#23901](https://github.com/ionic-team/ionic/issues/23901)) ([58a4ba2](https://github.com/ionic-team/ionic/commit/58a4ba285389e45276df49a0b4a3412daa95e92c))
* **modal:** sheet modal now accounts for safe area ([#23884](https://github.com/ionic-team/ionic/issues/23884)) ([195d817](https://github.com/ionic-team/ionic/commit/195d8179676155315f8532636b6371dd2a63e4b9)), closes [#23874](https://github.com/ionic-team/ionic/issues/23874)
### Features
* **datetime:** add ability to select only month, year, or month and year ([#23913](https://github.com/ionic-team/ionic/issues/23913)) ([4ae44b7](https://github.com/ionic-team/ionic/commit/4ae44b7a236004738d593406d7b1236600bc6d95))
* **datetime:** add clear button ([#23920](https://github.com/ionic-team/ionic/issues/23920)) ([18765e7](https://github.com/ionic-team/ionic/commit/18765e7e39b9f205f47f394d26d6ecc4b53e17ef)), closes [#17482](https://github.com/ionic-team/ionic/issues/17482)
* **menu:** add console error for incorrect usage of contentId ([#23871](https://github.com/ionic-team/ionic/issues/23871)) ([879ab8e](https://github.com/ionic-team/ionic/commit/879ab8ebdacc1468ed206701c00b60100dbab9e4)), closes [#23810](https://github.com/ionic-team/ionic/issues/23810)
* **platform:** add ability to override platform detection methods ([#23915](https://github.com/ionic-team/ionic/issues/23915)) ([45cabae](https://github.com/ionic-team/ionic/commit/45cabae04bf9236cd069793fbf2ac8f68c372cc3)), closes [#19737](https://github.com/ionic-team/ionic/issues/19737)
* **react:** add custom elements bundle ([#23896](https://github.com/ionic-team/ionic/issues/23896)) ([c50d895](https://github.com/ionic-team/ionic/commit/c50d895370a56d0809019dc59fe32ec840b72f03))
# [6.0.0-beta.5](https://github.com/ionic-team/ionic/compare/v5.7.0...v6.0.0-beta.5) (2021-09-01)
### Bug Fixes
* **angular:** overlay interfaces are now properly exported ([#23847](https://github.com/ionic-team/ionic/issues/23847)) ([c925274](https://github.com/ionic-team/ionic/commit/c925274c3bb22532a323b2a07771d7448f7de542)), closes [#23846](https://github.com/ionic-team/ionic/issues/23846)
* **datetime:** prevent vertical page scroll on interaction ([#23780](https://github.com/ionic-team/ionic/issues/23780)) ([950350a](https://github.com/ionic-team/ionic/commit/950350a948320f889589a0c9d2ec9045637215e5)), closes [#23554](https://github.com/ionic-team/ionic/issues/23554)
* **item:** form validation states are now properly shown ([#23853](https://github.com/ionic-team/ionic/issues/23853)) ([5ca2ce9](https://github.com/ionic-team/ionic/commit/5ca2ce91971408218d7bdc52509ce61a6ebb46aa)), closes [#23733](https://github.com/ionic-team/ionic/issues/23733) [#23850](https://github.com/ionic-team/ionic/issues/23850)
* **overlays:** thrown errors are no longer suppressed ([#23831](https://github.com/ionic-team/ionic/issues/23831)) ([a212eb5](https://github.com/ionic-team/ionic/commit/a212eb52599e35d3706e2d3cef751e490e3a7259)), closes [#22724](https://github.com/ionic-team/ionic/issues/22724)
### Features
* **modal:** add bottom sheet functionality ([#23828](https://github.com/ionic-team/ionic/issues/23828)) ([12216d3](https://github.com/ionic-team/ionic/commit/12216d378df091e16fd77d271b107e819278481c)), closes [#21039](https://github.com/ionic-team/ionic/issues/21039)
* **popover:** add ability to pass event to present method ([#23827](https://github.com/ionic-team/ionic/issues/23827)) ([1d2ee92](https://github.com/ionic-team/ionic/commit/1d2ee92ca01b77bcf87c7783b50d59efcf0a402a)), closes [#23813](https://github.com/ionic-team/ionic/issues/23813)
# [6.0.0-beta.4](https://github.com/ionic-team/ionic/compare/v5.6.14...v6.0.0-beta.4) (2021-08-18)
### Bug Fixes
* **datetime:** reduce time presentation min height ([#23771](https://github.com/ionic-team/ionic/issues/23771)) ([bc4e826](https://github.com/ionic-team/ionic/commit/bc4e8267aa00e7f162cd01579d8d3adbf3cd7a83)), closes [#23690](https://github.com/ionic-team/ionic/issues/23690)
* **datetime:** text color on ios mode now accounts for color contrast ([#23729](https://github.com/ionic-team/ionic/issues/23729)) ([5980db4](https://github.com/ionic-team/ionic/commit/5980db44e5a765d15e681471325e916d566eca8d)), closes [#23723](https://github.com/ionic-team/ionic/issues/23723)
* **item:** highlight now appears above helper/error text ([#23763](https://github.com/ionic-team/ionic/issues/23763)) ([2995e33](https://github.com/ionic-team/ionic/commit/2995e337c8b4612a87eb7111224ec702494fd1d7)), closes [#23510](https://github.com/ionic-team/ionic/issues/23510)
* **toast:** ToastOptions interface now contains icon prop ([#23737](https://github.com/ionic-team/ionic/issues/23737)) ([fbd32ff](https://github.com/ionic-team/ionic/commit/fbd32ffb2633b17d71a34a8760386a319f2e2bca)), closes [#23736](https://github.com/ionic-team/ionic/issues/23736)
* **vue:** custom element internal properties are no longer overridden in vue 3.1.0 ([#23738](https://github.com/ionic-team/ionic/issues/23738)) ([ea39c70](https://github.com/ionic-team/ionic/commit/ea39c70b3ec78b2ea5ef64263e8528b543378784)), closes [#23539](https://github.com/ionic-team/ionic/issues/23539)
* **vue:** modal and popover components now correctly pass properties ([#23761](https://github.com/ionic-team/ionic/issues/23761)) ([578b906](https://github.com/ionic-team/ionic/commit/578b9062dd793c8526b80a769d94aa7aad8fe368)), closes [#23698](https://github.com/ionic-team/ionic/issues/23698)
### Features
* **action-sheet:** add data property to ActionSheetButton ([#23744](https://github.com/ionic-team/ionic/issues/23744)) ([30f8508](https://github.com/ionic-team/ionic/commit/30f8508296cfc8f8b1c03d04b24abfa184624200)), closes [#23700](https://github.com/ionic-team/ionic/issues/23700)
* **datetime:** add firstDayOfWeek property ([#23692](https://github.com/ionic-team/ionic/issues/23692)) ([ea348f0](https://github.com/ionic-team/ionic/commit/ea348f005aef7b2fda581a99338139f6fefcda63)), closes [#23556](https://github.com/ionic-team/ionic/issues/23556)
* **datetime:** add hourCycle property ([#23686](https://github.com/ionic-team/ionic/issues/23686)) ([6342fde](https://github.com/ionic-team/ionic/commit/6342fde56c7687703edd212b8383536c8b9a6400)), closes [#23661](https://github.com/ionic-team/ionic/issues/23661)
# [6.0.0-beta.3](https://github.com/ionic-team/ionic/compare/v5.6.13...v6.0.0-beta.3) (2021-08-04)
### Bug Fixes
* **list:** change inset border radius to match iOS 15 ([#23711](https://github.com/ionic-team/ionic/issues/23711)) ([fe2810b](https://github.com/ionic-team/ionic/commit/fe2810b227abc482e663b210cd89f29b76119ff5))
* **popover:** fix keyboard arrow navigation ([#23709](https://github.com/ionic-team/ionic/issues/23709)) ([f2e7a26](https://github.com/ionic-team/ionic/commit/f2e7a267973a06b50a0f6dcbba0a204930bccf69)), closes [#23512](https://github.com/ionic-team/ionic/issues/23512)
* **vue:** popover positioning is now correct with custom elements build ([#23680](https://github.com/ionic-team/ionic/issues/23680)) ([3a1a9cb](https://github.com/ionic-team/ionic/commit/3a1a9cbce45ad128c9ba87940535dabfa167fb9e))
### Features
* **toast:** add icon property to show icon at start of toast content ([#23596](https://github.com/ionic-team/ionic/issues/23596)) ([df24c8c](https://github.com/ionic-team/ionic/commit/df24c8c5ae0b493841c07c05e0d620fa4a90c05a)), closes [#23524](https://github.com/ionic-team/ionic/issues/23524)
# [6.0.0-beta.2](https://github.com/ionic-team/ionic/compare/v5.6.12...v6.0.0-beta.2) (2021-07-21)
### Bug Fixes
* **accordion:** value can now be set as string when using multiple is true ([#23581](https://github.com/ionic-team/ionic/issues/23581)) ([8f172de](https://github.com/ionic-team/ionic/commit/8f172de355bc7c910d600ce4d8446b04a6212545)), closes [#23550](https://github.com/ionic-team/ionic/issues/23550)
* **angular:** modal and popover now have correct props defined on angular component ([#23565](https://github.com/ionic-team/ionic/issues/23565)) ([e5a7b34](https://github.com/ionic-team/ionic/commit/e5a7b342623b159d41cc83e0a418fb3984ceb3a7))
* **datetime:** add keyboard year navigation ([#23585](https://github.com/ionic-team/ionic/issues/23585)) ([55bd1f7](https://github.com/ionic-team/ionic/commit/55bd1f749bac01cc691e16283728c42e755cc706)), closes [#21553](https://github.com/ionic-team/ionic/issues/21553) [#18122](https://github.com/ionic-team/ionic/issues/18122)
* **datetime:** selecting time now works correctly on firefox ([#23583](https://github.com/ionic-team/ionic/issues/23583)) ([4188964](https://github.com/ionic-team/ionic/commit/4188964dc8da2c46494245b81864ca6e305611f5)), closes [#23545](https://github.com/ionic-team/ionic/issues/23545)
* **datetime:** years displayed now more consistent with v5 datetime, max and min are now accounted for in MD mode ([#23616](https://github.com/ionic-team/ionic/issues/23616)) ([be219a2](https://github.com/ionic-team/ionic/commit/be219a2814800927e6328ff105616713003340b7)), closes [#23615](https://github.com/ionic-team/ionic/issues/23615)
### Features
* **breadcrumbs:** ionCollapsedClick event payload now contains references to collapsed breadcrumb elements ([#23611](https://github.com/ionic-team/ionic/issues/23611)) ([9ce57d2](https://github.com/ionic-team/ionic/commit/9ce57d2efb84130895a37e22e0fd7e5d713a9fa5)), closes [#23552](https://github.com/ionic-team/ionic/issues/23552)
* **datetime:** add showDefaultTimeLabel property and time-label slot ([#23577](https://github.com/ionic-team/ionic/issues/23577)) ([7ac0109](https://github.com/ionic-team/ionic/commit/7ac010943b2c9ad42a1833153ea16ccffd169b91)), closes [#23555](https://github.com/ionic-team/ionic/issues/23555)
* **datetime:** add size property ([#23649](https://github.com/ionic-team/ionic/issues/23649)) ([321341d](https://github.com/ionic-team/ionic/commit/321341d97dff98b76b69a1efce58923a80e92bc4)), closes [#23518](https://github.com/ionic-team/ionic/issues/23518)
* **range:** add support for customizing pin format ([#22972](https://github.com/ionic-team/ionic/issues/22972)) ([8f2c4f7](https://github.com/ionic-team/ionic/commit/8f2c4f73db167503cdf60222f42bcaadf905b401))
* **segment:** add keyboard navigation, add selectOnFocus property to control selection follow focus behavior ([#23590](https://github.com/ionic-team/ionic/issues/23590)) ([b6c53e5](https://github.com/ionic-team/ionic/commit/b6c53e539b0855fa95b0fe02e5fa74ce403b68b8)), closes [#23520](https://github.com/ionic-team/ionic/issues/23520)
* **select:** update popover interface to match MD spec on desktop, allow multiple values in popover interface ([#23474](https://github.com/ionic-team/ionic/issues/23474)) ([2c07a15](https://github.com/ionic-team/ionic/commit/2c07a1566b6f8570f7e12a55ca8f86d8fb8a968e)), closes [#23657](https://github.com/ionic-team/ionic/issues/23657) [#15500](https://github.com/ionic-team/ionic/issues/15500) [#12310](https://github.com/ionic-team/ionic/issues/12310)
### Performance Improvements
* remove shims for legacy browsers no longer supported in v6 ([#23592](https://github.com/ionic-team/ionic/issues/23592)) ([259b135](https://github.com/ionic-team/ionic/commit/259b1359dbd20d4f85036ae46901a051cd8fc98b))
# [6.0.0-beta.1](https://github.com/ionic-team/ionic/compare/v5.6.11...v6.0.0-beta.1) (2021-07-01)
### Bug Fixes
* **accordion:** improved reliability of accordion animations ([#23531](https://github.com/ionic-team/ionic/issues/23531)) ([6fbd60b](https://github.com/ionic-team/ionic/commit/6fbd60b0df56dc927226474a1ffa322d979c563e)), closes [#23504](https://github.com/ionic-team/ionic/issues/23504)
* **content:** add touch-action manipulation for a11y zoom and pan ([#23534](https://github.com/ionic-team/ionic/issues/23534)) ([6ca1780](https://github.com/ionic-team/ionic/commit/6ca17805b8b1ea38d7fc16d091324da16a4193c6)), closes [#22805](https://github.com/ionic-team/ionic/issues/22805)
* **datetime:** scroll position no longer gets reset when using datetime in overlay ([#23543](https://github.com/ionic-team/ionic/issues/23543)) ([b735b58](https://github.com/ionic-team/ionic/commit/b735b587cda777ac481bb580c883d9734145f31e))
* **input, select, textarea:** change type of placeholder prop to string only ([#23500](https://github.com/ionic-team/ionic/issues/23500)) ([f3ae431](https://github.com/ionic-team/ionic/commit/f3ae4319bb64debab304973856a33e422ac910a1)), closes [#22976](https://github.com/ionic-team/ionic/issues/22976)
* **popover:** size property now works when providing only event ([#23532](https://github.com/ionic-team/ionic/issues/23532)) ([bdc1f23](https://github.com/ionic-team/ionic/commit/bdc1f2360d7795472cc242a86eb4376d05fa0bb7)), closes [#23528](https://github.com/ionic-team/ionic/issues/23528)
* **popover:** update animation to better match MD spec ([#23541](https://github.com/ionic-team/ionic/issues/23541)) ([bdb95b7](https://github.com/ionic-team/ionic/commit/bdb95b7b6dd798cbc6d1786ae54fa95ac1dfd096))
* **react:** export accordion and accordion group components ([#23497](https://github.com/ionic-team/ionic/issues/23497)) ([a664d42](https://github.com/ionic-team/ionic/commit/a664d4268dea8e84ab9e3b150043ac8f87fb53c7))
* **vue:** navigating between parameterized pages now results in page transition ([#23525](https://github.com/ionic-team/ionic/issues/23525)) ([e30b17c](https://github.com/ionic-team/ionic/commit/e30b17c5bbd1af6936a8d7a98d1f7a115073e029)), closes [#22662](https://github.com/ionic-team/ionic/issues/22662)
### Features
* **accordion-group:** add animated property to disable animations ([#23530](https://github.com/ionic-team/ionic/issues/23530)) ([9a60dd0](https://github.com/ionic-team/ionic/commit/9a60dd0ea7c55acf0fdd1161433e5b4ed40778f2))
* **action-sheet, alert:** add id to AlertButton and ActionSheetButton ([#18992](https://github.com/ionic-team/ionic/issues/18992)) ([9e24a0b](https://github.com/ionic-team/ionic/commit/9e24a0b49357a3a39ca89f026ff23271a365d935)), closes [#22959](https://github.com/ionic-team/ionic/issues/22959)
* **vue:** extend useIonRouter hook for programmatic navigation with animation control ([#23499](https://github.com/ionic-team/ionic/issues/23499)) ([fc9e1b4](https://github.com/ionic-team/ionic/commit/fc9e1b4b361938e5644683c395a565be2de1eab9)), closes [#23450](https://github.com/ionic-team/ionic/issues/23450)
### BREAKING CHANGES
* **input, select, textarea:** Updated the `placeholder` property on `ion-input`, `ion-textarea`, and `ion-select` to have a type of `string | undefined`.
# [6.0.0-beta.0](https://github.com/ionic-team/ionic/compare/v5.6.10...v6.0.0-beta.0) (2021-06-23)
### Bug Fixes
* **accordion:** toggle icon now shows up in vue and react ([#23426](https://github.com/ionic-team/ionic/issues/23426)) ([c716617](https://github.com/ionic-team/ionic/commit/c7166179457a8e2c7e1702c5761bc6368dbd156f))
* **datetime:** changing time emits ionChange ([#23463](https://github.com/ionic-team/ionic/issues/23463)) ([b0cce36](https://github.com/ionic-team/ionic/commit/b0cce360c83ac564e053523cc31b32d1deaeda0c))
* **modal, popover:** overlays now automatically determine if they are inline ([#23434](https://github.com/ionic-team/ionic/issues/23434)) ([8dbe8ba](https://github.com/ionic-team/ionic/commit/8dbe8ba7bc26792c5024f81cf4752f5b78317492))
* **modal:** add additional padding to toolbars in iOS modal ([#23262](https://github.com/ionic-team/ionic/issues/23262)) ([a037b65](https://github.com/ionic-team/ionic/commit/a037b65aad5cfc0477322a8f36105b9009366ec2)), closes [#22778](https://github.com/ionic-team/ionic/issues/22778)
* **modal:** border radius is correctly set on card style modal ([#23461](https://github.com/ionic-team/ionic/issues/23461)) ([bccb8ad](https://github.com/ionic-team/ionic/commit/bccb8ad5fb5ec7f98a6cbfa62a403ecaca7fbdb6))
* **popover:** shadow parts now correctly added ([#23446](https://github.com/ionic-team/ionic/issues/23446)) ([e1a9613](https://github.com/ionic-team/ionic/commit/e1a96130ebab1e481e880f0f3876f421976f08d5))
* **popover:** update prop defaults, use correct delegate ([#23340](https://github.com/ionic-team/ionic/issues/23340)) ([960778a](https://github.com/ionic-team/ionic/commit/960778a36f6eb6318cc740c4f7a255107723b8fd))
* **searchbar:** showClearButton now defaults to 'always' for improved usability with screen readers ([#23475](https://github.com/ionic-team/ionic/issues/23475)) ([80f181d](https://github.com/ionic-team/ionic/commit/80f181d4846507ee6bd4150bb568fca9b6660428))
* **vue:** ensure webpack does not eliminate core css ([#23465](https://github.com/ionic-team/ionic/issues/23465)) ([ee3a00f](https://github.com/ionic-team/ionic/commit/ee3a00fde61b4d1d3168d34b3d23bb97dd154154))
### Code Refactoring
* **all:** update required browser, framework, and mobile platform versions for v6 ([#23443](https://github.com/ionic-team/ionic/issues/23443)) ([c842dd8](https://github.com/ionic-team/ionic/commit/c842dd88c98888b2afab08ac5e8bc57c2a4c2fbd))
* **angular:** remove Config.set() method ([#22918](https://github.com/ionic-team/ionic/issues/22918)) ([9e05891](https://github.com/ionic-team/ionic/commit/9e0589173607b3c0eff7794079123354c2eeaa1a))
* **header:** removed border from last toolbar when using collapsible large title ([#22891](https://github.com/ionic-team/ionic/issues/22891)) ([c72bc5d](https://github.com/ionic-team/ionic/commit/c72bc5dbd76cd3ce622a4b3cedcb7446a2819384)), closes [#22777](https://github.com/ionic-team/ionic/issues/22777)
* **ios:** update toolbar and tabbar default background colors ([#22852](https://github.com/ionic-team/ionic/issues/22852)) ([3d615cb](https://github.com/ionic-team/ionic/commit/3d615cb3c7b233b08b9da6ac04096e16bbb60bfc)), closes [#22780](https://github.com/ionic-team/ionic/issues/22780)
* **toast:** whitespace variable now defaults to normal ([#22866](https://github.com/ionic-team/ionic/issues/22866)) ([9b78689](https://github.com/ionic-team/ionic/commit/9b786899e550c391b9395c669f9bba8f39ac98aa))
* **vue:** drop support for "on" prefixed overlay events and bump minimum required version of vue to 3.0.6 ([#23229](https://github.com/ionic-team/ionic/issues/23229)) ([6fcb3a6](https://github.com/ionic-team/ionic/commit/6fcb3a62b1b12c5ded11179e83854592d4309bdf))
* **vue:** remove support for child routes nested inside of tabs ([#22919](https://github.com/ionic-team/ionic/issues/22919)) ([75458ac](https://github.com/ionic-team/ionic/commit/75458ac7fb95f56a6ec460f85cf7d7720ce0c070))
### Features
* **accordion:** add accordion and accordion-group components ([#22865](https://github.com/ionic-team/ionic/issues/22865)) ([073883a](https://github.com/ionic-team/ionic/commit/073883a0987149e9f6258ca43c46f5ed4bce0dc5)), closes [#17094](https://github.com/ionic-team/ionic/issues/17094)
* **breadcrumbs:** add breadcrumbs component ([#22701](https://github.com/ionic-team/ionic/issues/22701)) ([2f6b1e4](https://github.com/ionic-team/ionic/commit/2f6b1e4eea307c6f14345704e5824378ef079acb)), closes [#22770](https://github.com/ionic-team/ionic/issues/22770)
* **datetime:** add calendar picker ([#23416](https://github.com/ionic-team/ionic/issues/23416)) ([932d3ca](https://github.com/ionic-team/ionic/commit/932d3ca62f3e3ef08acb065ce6ec46faa3811f96)), closes [#19423](https://github.com/ionic-team/ionic/issues/19423)
* **item:** add helper text, error text, counter, shape, and fill mode ([#23354](https://github.com/ionic-team/ionic/issues/23354)) ([faefe97](https://github.com/ionic-team/ionic/commit/faefe97da6a9d5beff1183d10efd0df9c4e3ebd7)), closes [#19619](https://github.com/ionic-team/ionic/issues/19619)
* **modal:** modals can now be used inline ([#23341](https://github.com/ionic-team/ionic/issues/23341)) ([3be1c3d](https://github.com/ionic-team/ionic/commit/3be1c3dcd73e6039a89b19b409e63877cda37f6e)), closes [#20117](https://github.com/ionic-team/ionic/issues/20117) [#20263](https://github.com/ionic-team/ionic/issues/20263)
* **popover:** account for ionShadowTarget elements ([#23436](https://github.com/ionic-team/ionic/issues/23436)) ([0e38d42](https://github.com/ionic-team/ionic/commit/0e38d4276110dcd94db5adc3b6aee3b5b0befc5c))
* **popover:** add desktop support ([#23258](https://github.com/ionic-team/ionic/issues/23258)) ([a67a0fa](https://github.com/ionic-team/ionic/commit/a67a0fabb8249685bbe93ed862839e2b2e76cd5a)), closes [#21599](https://github.com/ionic-team/ionic/issues/21599)
* **popover:** popover can now be used inline ([#23231](https://github.com/ionic-team/ionic/issues/23231)) ([308fa1c](https://github.com/ionic-team/ionic/commit/308fa1c0dd054cfc2ea54d2edc99e7a4b549f6f0))
* **slides:** add IonicSwiper modules, deprecate ion-slides, and add link to migration ([#23447](https://github.com/ionic-team/ionic/issues/23447)) ([623c84a](https://github.com/ionic-team/ionic/commit/623c84ab082668a996c654e18ffc9768f68b85dd))
* **spinner:** add lines-sharp, lines-sharp-small, update styles for ios 14 ([#22397](https://github.com/ionic-team/ionic/issues/22397)) ([2a5b272](https://github.com/ionic-team/ionic/commit/2a5b272a329bbad1ca07705f84f0fd06e3ef32ad))
* **vue:** add custom elements bundle ([#23458](https://github.com/ionic-team/ionic/issues/23458)) ([dc48a9f](https://github.com/ionic-team/ionic/commit/dc48a9f1a2dff8a2d644112bbe1df8b0b6811848))
### BREAKING CHANGES
* **searchbar:** The `showClearButton` property on `ion-searchbar` now defaults to `'always'`.
* **datetime:** The `ion-datetime` component has been revamped to use a new calendar style. As a result, some APIs have been removed. See https://github.com/ionic-team/ionic-framework/blob/master/BREAKING.md for more details.
* **all:** Browser, JS Framework, and mobile platform minimum required versions have been updated.
* **popover:** Converted `ion-popover` to use the Shadow DOM.
* **vue:** - Dropped support for prefixed overlay events in favor of non prefixed events (I.e. `@onDidDismiss` becomes `@didDismiss`).
- Minimum required version of Vue is now Vue v3.0.6 or newer.
* **vue:** Support for child routes nested inside of tabs has been removed to better conform to Vue Router's best practices. Additional routes should be written as sibling routes with the parent tab as the path prefix.
* **angular:** The `Config.set()` method has been removed. See https://ionicframework.com/docs/angular/config for examples on how to set config globally, per-component, and per-platform.
* **ios:** The tab bar and toolbar default background colors have been updated to better reflect the latest iOS styles.
* **header:** The last toolbar in the header with a collapsible large title no longer has a border.
* **toast:** The `--white-space` CSS Variable now defaults to `normal`.
## [5.9.1](https://github.com/ionic-team/ionic/compare/v5.9.0...v5.9.1) (2021-11-17)
@@ -414,31 +55,6 @@ Please see the [Ionic 6 Upgrade Guide](https://ionicframework.com/docs/next/intr
# [6.0.0-rc.2](https://github.com/ionic-team/ionic/compare/v6.0.0-rc.1...v6.0.0-rc.2) (2021-11-03)
### Bug Fixes
* **all:** resolve compilation issues with Stencil 2.10 ([#24152](https://github.com/ionic-team/ionic/issues/24152)) ([8b940e5](https://github.com/ionic-team/ionic-framework/commit/8b940e505e79bdf5da829850ed949847d5df8b90)), closes [#24153](https://github.com/ionic-team/ionic-framework/issues/24153)
* **datetime:** resolve month and year jumping issue on ios ([#24142](https://github.com/ionic-team/ionic/issues/24142)) ([27aef93](https://github.com/ionic-team/ionic/commit/27aef9343cada9a83adec8fe00e8bc3bafa8e049)), closes [#23910](https://github.com/ionic-team/ionic/issues/23910)
# [6.0.0-rc.1](https://github.com/ionic-team/ionic/compare/v6.0.0-rc.0...v6.0.0-rc.1) (2021-10-27)
### Bug Fixes
* **accordion-group:** ionChange is now fired properly in vue ([#24063](https://github.com/ionic-team/ionic/issues/24063)) ([61b99d1](https://github.com/ionic-team/ionic/commit/61b99d13bfab5c57617cbcdc7e54e43f88885f66)), closes [#23762](https://github.com/ionic-team/ionic/issues/23762)
* **angular:** resolve issues with ng add on angular 12 ([#23970](https://github.com/ionic-team/ionic/issues/23970)) ([3451a34](https://github.com/ionic-team/ionic/commit/3451a34ad0c893be0b6c17dc91ac9a75d2b9b52c))
* **datetime:** clear button is now rendered even if showDefaultButtons is false ([#24075](https://github.com/ionic-team/ionic/issues/24075)) ([e3996cf](https://github.com/ionic-team/ionic/commit/e3996cfbd50f5e9ae54ffcbe2594124e3b9969b0))
* **datetime:** default sizing preserves shape of datetime ([#24104](https://github.com/ionic-team/ionic/issues/24104)) ([71fab0f](https://github.com/ionic-team/ionic/commit/71fab0fa124254f8cdc3b513627aa7b045993f4e))
* **infinite-scroll:** infinite scroll event now fired with custom elements build ([#24043](https://github.com/ionic-team/ionic/issues/24043)) ([8a86cfb](https://github.com/ionic-team/ionic/commit/8a86cfb7050989e914fa85ccc1ea755d73f58c90)), closes [#24034](https://github.com/ionic-team/ionic/issues/24034)
* **modal:** fix backdrop animation for sheets with off-center backdropBreakpoint ([#24061](https://github.com/ionic-team/ionic/issues/24061)) ([49db6d0](https://github.com/ionic-team/ionic/commit/49db6d02883b11b5f179300e2eaa298002a381e8))
* **react:** overlays shown with useIonModal and useIonPopover no longer render outside of main react tree ([f3e492c](https://github.com/ionic-team/ionic/commit/f3e492c897c8cda2b98050156f130654f4d7014a)), closes [#23516](https://github.com/ionic-team/ionic/issues/23516) [#23516](https://github.com/ionic-team/ionic/issues/23516)
## [5.8.5](https://github.com/ionic-team/ionic/compare/v5.8.4...v5.8.5) (2021-10-27)
@@ -460,15 +76,6 @@ Please see the [Ionic 6 Upgrade Guide](https://ionicframework.com/docs/next/intr
# [6.0.0-rc.0](https://github.com/ionic-team/ionic/compare/v6.0.0-beta.7...v6.0.0-rc.0) (2021-10-07)
### Bug Fixes
* **angular:** setup config properly ([#24028](https://github.com/ionic-team/ionic/issues/24028)) ([907996c](https://github.com/ionic-team/ionic/commit/907996ce16446d0dc12939da325b7b5dae09ebd9))
## [5.8.3](https://github.com/ionic-team/ionic/compare/v5.8.2...v5.8.3) (2021-10-07)
@@ -478,33 +85,6 @@ Please see the [Ionic 6 Upgrade Guide](https://ionicframework.com/docs/next/intr
# [6.0.0-beta.7](https://github.com/ionic-team/ionic/compare/v6.0.0-beta.6...v6.0.0-beta.7) (2021-10-06)
### Bug Fixes
* **datetime:** ionBlur and ionFocus now fires correctly ([#23980](https://github.com/ionic-team/ionic/issues/23980)) ([86a77bd](https://github.com/ionic-team/ionic/commit/86a77bd379c6dca57d5feb9694d18afe6d82934d))
* **datetime:** ionChange is no longer called for out of range dates ([#23940](https://github.com/ionic-team/ionic/issues/23940)) ([ea39c6e](https://github.com/ionic-team/ionic/commit/ea39c6e5b3781ceb4c87277cf4a5e0be9c75bc20)), closes [#23939](https://github.com/ionic-team/ionic/issues/23939)
* **datetime:** time picker uses new iOS 15 style ([#23996](https://github.com/ionic-team/ionic/issues/23996)) ([0ab37b5](https://github.com/ionic-team/ionic/commit/0ab37b5061728bd60fd42781645b96add130a79f)), closes [#23768](https://github.com/ionic-team/ionic/issues/23768)
* **modal:** backdropBreakpoint is now an exclusive value ([#23954](https://github.com/ionic-team/ionic/issues/23954)) ([ed455ab](https://github.com/ionic-team/ionic/commit/ed455ab4c6df73f801a3c941da21261c205c9634))
* **react:** ensure inline modal content is visible ([#23968](https://github.com/ionic-team/ionic/issues/23968)) ([285a371](https://github.com/ionic-team/ionic/commit/285a371101e714e74d6df68701cbee9dfe23605e))
* **reorder-group:** wait for content to render before getting scroll position ([#24007](https://github.com/ionic-team/ionic/issues/24007)) ([225a278](https://github.com/ionic-team/ionic/commit/225a2787407c5ce68a953ee3448647d00af26517)), closes [#23875](https://github.com/ionic-team/ionic/issues/23875)
* **select:** ensure popover options with number values are searched for correctly ([#23998](https://github.com/ionic-team/ionic/issues/23998)) ([c204083](https://github.com/ionic-team/ionic/commit/c20408369bd332b5e225a3d50ec94978f6f5ec97))
* **select:** focus selected item in popovers ([#23991](https://github.com/ionic-team/ionic/issues/23991)) ([2497a53](https://github.com/ionic-team/ionic/commit/2497a53255dc43052755bba842dfcf556d930dcd))
### Features
* **all:** add CustomEvents types to components that emit events ([#23956](https://github.com/ionic-team/ionic/issues/23956)) ([8708095](https://github.com/ionic-team/ionic/commit/87080951112a409893a4bac2def1deca06642b16)), closes [#22925](https://github.com/ionic-team/ionic/issues/22925)
* **header, footer:** add ios fading header style ([#24011](https://github.com/ionic-team/ionic/issues/24011)) ([7ce3959](https://github.com/ionic-team/ionic/commit/7ce3959b66a08e980c7dac3bb7d7df6bf0ae874e))
### BREAKING CHANGES
* **radio:** The `RadioChangeEventDetail` interface has been removed in favor of `RadioGroupChangeEventDetail`.
## [5.8.2](https://github.com/ionic-team/ionic/compare/v5.8.1...v5.8.2) (2021-10-06)
@@ -530,30 +110,6 @@ Please see the [Ionic 6 Upgrade Guide](https://ionicframework.com/docs/next/intr
# [6.0.0-beta.6](https://github.com/ionic-team/ionic/compare/v6.0.0-beta.5...v6.0.0-beta.6) (2021-09-15)
### Bug Fixes
* **menu:** add console error for incorrect usage of contentId ([#23871](https://github.com/ionic-team/ionic/issues/23871)) ([879ab8e](https://github.com/ionic-team/ionic/commit/879ab8ebdacc1468ed206701c00b60100dbab9e4)), closes [#23810](https://github.com/ionic-team/ionic/issues/23810)
* **modal:** add sheet modal properties for angular ([#23899](https://github.com/ionic-team/ionic/issues/23899)) ([d1763fc](https://github.com/ionic-team/ionic/commit/d1763fc8b56c8cb5272224ae0faaebfe3e516fdb))
* **modal:** expose breakpoint props in ModalOptions interface ([#23867](https://github.com/ionic-team/ionic/issues/23867)) ([5fd80fd](https://github.com/ionic-team/ionic/commit/5fd80fd43885a5d0cd65f0eef4e0ff15e82c4fe0)), closes [#23866](https://github.com/ionic-team/ionic/issues/23866)
* **modal:** handle on sheet modal can now be turned off ([#23900](https://github.com/ionic-team/ionic/issues/23900)) ([e2d2ad6](https://github.com/ionic-team/ionic/commit/e2d2ad6f8eaf798c6f4b4a69f2b8176f0ac22d32))
* **modal:** modal displays in middle of screen on desktop ([#23911](https://github.com/ionic-team/ionic/issues/23911)) ([9d87028](https://github.com/ionic-team/ionic/commit/9d87028e81723a0f1498c8cf231319676078eda0))
* **modal:** sheet animation works correctly if breakpoints value does not include 1 ([#23927](https://github.com/ionic-team/ionic/issues/23927)) ([414f246](https://github.com/ionic-team/ionic/commit/414f24685cbc67a7fff142b7786d33ce1cd67a0c))
* **modal:** sheet modal handle is now positioned correctly ([#23901](https://github.com/ionic-team/ionic/issues/23901)) ([58a4ba2](https://github.com/ionic-team/ionic/commit/58a4ba285389e45276df49a0b4a3412daa95e92c))
* **modal:** sheet modal now accounts for safe area ([#23884](https://github.com/ionic-team/ionic/issues/23884)) ([195d817](https://github.com/ionic-team/ionic/commit/195d8179676155315f8532636b6371dd2a63e4b9)), closes [#23874](https://github.com/ionic-team/ionic/issues/23874)
### Features
* **datetime:** add ability to select only month, year, or month and year ([#23913](https://github.com/ionic-team/ionic/issues/23913)) ([4ae44b7](https://github.com/ionic-team/ionic/commit/4ae44b7a236004738d593406d7b1236600bc6d95))
* **datetime:** add clear button ([#23920](https://github.com/ionic-team/ionic/issues/23920)) ([18765e7](https://github.com/ionic-team/ionic/commit/18765e7e39b9f205f47f394d26d6ecc4b53e17ef)), closes [#17482](https://github.com/ionic-team/ionic/issues/17482)
* **platform:** add ability to override platform detection methods ([#23915](https://github.com/ionic-team/ionic/issues/23915)) ([45cabae](https://github.com/ionic-team/ionic/commit/45cabae04bf9236cd069793fbf2ac8f68c372cc3)), closes [#19737](https://github.com/ionic-team/ionic/issues/19737)
* **react:** add custom elements bundle ([#23896](https://github.com/ionic-team/ionic/issues/23896)) ([c50d895](https://github.com/ionic-team/ionic/commit/c50d895370a56d0809019dc59fe32ec840b72f03))
# [5.8.0 Calcium](https://github.com/ionic-team/ionic/compare/v5.7.0...v5.8.0) (2021-09-15)
@@ -572,24 +128,6 @@ Please see the [Ionic 6 Upgrade Guide](https://ionicframework.com/docs/next/intr
# [6.0.0-beta.5](https://github.com/ionic-team/ionic/compare/v6.0.0-beta.4...v6.0.0-beta.5) (2021-09-01)
### Bug Fixes
* **angular:** overlay interfaces are now properly exported ([#23847](https://github.com/ionic-team/ionic/issues/23847)) ([c925274](https://github.com/ionic-team/ionic/commit/c925274c3bb22532a323b2a07771d7448f7de542)), closes [#23846](https://github.com/ionic-team/ionic/issues/23846)
* **datetime:** prevent vertical page scroll on interaction ([#23780](https://github.com/ionic-team/ionic/issues/23780)) ([950350a](https://github.com/ionic-team/ionic/commit/950350a948320f889589a0c9d2ec9045637215e5)), closes [#23554](https://github.com/ionic-team/ionic/issues/23554)
* **item:** form validation states are now properly shown ([#23853](https://github.com/ionic-team/ionic/issues/23853)) ([5ca2ce9](https://github.com/ionic-team/ionic/commit/5ca2ce91971408218d7bdc52509ce61a6ebb46aa)), closes [#23733](https://github.com/ionic-team/ionic/issues/23733) [#23850](https://github.com/ionic-team/ionic/issues/23850)
* **overlays:** thrown errors are no longer suppressed ([#23831](https://github.com/ionic-team/ionic/issues/23831)) ([a212eb5](https://github.com/ionic-team/ionic/commit/a212eb52599e35d3706e2d3cef751e490e3a7259)), closes [#22724](https://github.com/ionic-team/ionic/issues/22724)
### Features
* **modal:** add bottom sheet functionality ([#23828](https://github.com/ionic-team/ionic/issues/23828)) ([12216d3](https://github.com/ionic-team/ionic/commit/12216d378df091e16fd77d271b107e819278481c)), closes [#21039](https://github.com/ionic-team/ionic/issues/21039)
* **popover:** add ability to pass event to present method ([#23827](https://github.com/ionic-team/ionic/issues/23827)) ([1d2ee92](https://github.com/ionic-team/ionic/commit/1d2ee92ca01b77bcf87c7783b50d59efcf0a402a)), closes [#23813](https://github.com/ionic-team/ionic/issues/23813)
# [5.7.0 Potassium](https://github.com/ionic-team/ionic/compare/v5.6.14...v5.7.0) (2021-09-01)
@@ -611,27 +149,6 @@ Please see the [Ionic 6 Upgrade Guide](https://ionicframework.com/docs/next/intr
# [6.0.0-beta.4](https://github.com/ionic-team/ionic/compare/v6.0.0-beta.3...v6.0.0-beta.4) (2021-08-18)
### Bug Fixes
* **datetime:** reduce time presentation min height ([#23771](https://github.com/ionic-team/ionic/issues/23771)) ([bc4e826](https://github.com/ionic-team/ionic/commit/bc4e8267aa00e7f162cd01579d8d3adbf3cd7a83)), closes [#23690](https://github.com/ionic-team/ionic/issues/23690)
* **datetime:** text color on ios mode now accounts for color contrast ([#23729](https://github.com/ionic-team/ionic/issues/23729)) ([5980db4](https://github.com/ionic-team/ionic/commit/5980db44e5a765d15e681471325e916d566eca8d)), closes [#23723](https://github.com/ionic-team/ionic/issues/23723)
* **item:** highlight now appears above helper/error text ([#23763](https://github.com/ionic-team/ionic/issues/23763)) ([2995e33](https://github.com/ionic-team/ionic/commit/2995e337c8b4612a87eb7111224ec702494fd1d7)), closes [#23510](https://github.com/ionic-team/ionic/issues/23510)
* **toast:** ToastOptions interface now contains icon prop ([#23737](https://github.com/ionic-team/ionic/issues/23737)) ([fbd32ff](https://github.com/ionic-team/ionic/commit/fbd32ffb2633b17d71a34a8760386a319f2e2bca)), closes [#23736](https://github.com/ionic-team/ionic/issues/23736)
* **vue:** custom element internal properties are no longer overridden in vue 3.1.0 ([#23738](https://github.com/ionic-team/ionic/issues/23738)) ([ea39c70](https://github.com/ionic-team/ionic/commit/ea39c70b3ec78b2ea5ef64263e8528b543378784)), closes [#23539](https://github.com/ionic-team/ionic/issues/23539)
* **vue:** modal and popover components now correctly pass properties ([#23761](https://github.com/ionic-team/ionic/issues/23761)) ([578b906](https://github.com/ionic-team/ionic/commit/578b9062dd793c8526b80a769d94aa7aad8fe368)), closes [#23698](https://github.com/ionic-team/ionic/issues/23698)
### Features
* **action-sheet:** add data property to ActionSheetButton ([#23744](https://github.com/ionic-team/ionic/issues/23744)) ([30f8508](https://github.com/ionic-team/ionic/commit/30f8508296cfc8f8b1c03d04b24abfa184624200)), closes [#23700](https://github.com/ionic-team/ionic/issues/23700)
* **datetime:** add firstDayOfWeek property ([#23692](https://github.com/ionic-team/ionic/issues/23692)) ([ea348f0](https://github.com/ionic-team/ionic/commit/ea348f005aef7b2fda581a99338139f6fefcda63)), closes [#23556](https://github.com/ionic-team/ionic/issues/23556)
* **datetime:** add hourCycle property ([#23686](https://github.com/ionic-team/ionic/issues/23686)) ([6342fde](https://github.com/ionic-team/ionic/commit/6342fde56c7687703edd212b8383536c8b9a6400)), closes [#23661](https://github.com/ionic-team/ionic/issues/23661)
## [5.6.14](https://github.com/ionic-team/ionic/compare/v5.6.13...v5.6.14) (2021-08-18)
@@ -645,22 +162,6 @@ Please see the [Ionic 6 Upgrade Guide](https://ionicframework.com/docs/next/intr
# [6.0.0-beta.3](https://github.com/ionic-team/ionic/compare/v6.0.0-beta.2...v6.0.0-beta.3) (2021-08-04)
### Bug Fixes
* **list:** change inset border radius to match iOS 15 ([#23711](https://github.com/ionic-team/ionic/issues/23711)) ([fe2810b](https://github.com/ionic-team/ionic/commit/fe2810b227abc482e663b210cd89f29b76119ff5))
* **popover:** fix keyboard arrow navigation ([#23709](https://github.com/ionic-team/ionic/issues/23709)) ([f2e7a26](https://github.com/ionic-team/ionic/commit/f2e7a267973a06b50a0f6dcbba0a204930bccf69)), closes [#23512](https://github.com/ionic-team/ionic/issues/23512)
* **vue:** popover positioning is now correct with custom elements build ([#23680](https://github.com/ionic-team/ionic/issues/23680)) ([3a1a9cb](https://github.com/ionic-team/ionic/commit/3a1a9cbce45ad128c9ba87940535dabfa167fb9e))
### Features
* **toast:** add icon property to show icon at start of toast content ([#23596](https://github.com/ionic-team/ionic/issues/23596)) ([df24c8c](https://github.com/ionic-team/ionic/commit/df24c8c5ae0b493841c07c05e0d620fa4a90c05a)), closes [#23524](https://github.com/ionic-team/ionic/issues/23524)
## [5.6.13](https://github.com/ionic-team/ionic/compare/v5.6.12...v5.6.13) (2021-08-04)
@@ -675,34 +176,6 @@ Please see the [Ionic 6 Upgrade Guide](https://ionicframework.com/docs/next/intr
# [6.0.0-beta.2](https://github.com/ionic-team/ionic/compare/v6.0.0-beta.1...v6.0.0-beta.2) (2021-07-21)
### Bug Fixes
* **accordion:** value can now be set as string when using multiple is true ([#23581](https://github.com/ionic-team/ionic/issues/23581)) ([8f172de](https://github.com/ionic-team/ionic/commit/8f172de355bc7c910d600ce4d8446b04a6212545)), closes [#23550](https://github.com/ionic-team/ionic/issues/23550)
* **angular:** modal and popover now have correct props defined on angular component ([#23565](https://github.com/ionic-team/ionic/issues/23565)) ([e5a7b34](https://github.com/ionic-team/ionic/commit/e5a7b342623b159d41cc83e0a418fb3984ceb3a7))
* **datetime:** add keyboard year navigation ([#23585](https://github.com/ionic-team/ionic/issues/23585)) ([55bd1f7](https://github.com/ionic-team/ionic/commit/55bd1f749bac01cc691e16283728c42e755cc706)), closes [#21553](https://github.com/ionic-team/ionic/issues/21553) [#18122](https://github.com/ionic-team/ionic/issues/18122)
* **datetime:** selecting time now works correctly on firefox ([#23583](https://github.com/ionic-team/ionic/issues/23583)) ([4188964](https://github.com/ionic-team/ionic/commit/4188964dc8da2c46494245b81864ca6e305611f5)), closes [#23545](https://github.com/ionic-team/ionic/issues/23545)
* **datetime:** years displayed now more consistent with v5 datetime, max and min are now accounted for in MD mode ([#23616](https://github.com/ionic-team/ionic/issues/23616)) ([be219a2](https://github.com/ionic-team/ionic/commit/be219a2814800927e6328ff105616713003340b7)), closes [#23615](https://github.com/ionic-team/ionic/issues/23615)
### Features
* **breadcrumbs:** ionCollapsedClick event payload now contains references to collapsed breadcrumb elements ([#23611](https://github.com/ionic-team/ionic/issues/23611)) ([9ce57d2](https://github.com/ionic-team/ionic/commit/9ce57d2efb84130895a37e22e0fd7e5d713a9fa5)), closes [#23552](https://github.com/ionic-team/ionic/issues/23552)
* **datetime:** add showDefaultTimeLabel property and time-label slot ([#23577](https://github.com/ionic-team/ionic/issues/23577)) ([7ac0109](https://github.com/ionic-team/ionic/commit/7ac010943b2c9ad42a1833153ea16ccffd169b91)), closes [#23555](https://github.com/ionic-team/ionic/issues/23555)
* **datetime:** add size property ([#23649](https://github.com/ionic-team/ionic/issues/23649)) ([321341d](https://github.com/ionic-team/ionic/commit/321341d97dff98b76b69a1efce58923a80e92bc4)), closes [#23518](https://github.com/ionic-team/ionic/issues/23518)
* **range:** add support for customizing pin format ([#22972](https://github.com/ionic-team/ionic/issues/22972)) ([8f2c4f7](https://github.com/ionic-team/ionic/commit/8f2c4f73db167503cdf60222f42bcaadf905b401))
* **segment:** add keyboard navigation, add selectOnFocus property to control selection follow focus behavior ([#23590](https://github.com/ionic-team/ionic/issues/23590)) ([b6c53e5](https://github.com/ionic-team/ionic/commit/b6c53e539b0855fa95b0fe02e5fa74ce403b68b8)), closes [#23520](https://github.com/ionic-team/ionic/issues/23520)
* **select:** update popover interface to match MD spec on desktop, allow multiple values in popover interface ([#23474](https://github.com/ionic-team/ionic/issues/23474)) ([2c07a15](https://github.com/ionic-team/ionic/commit/2c07a1566b6f8570f7e12a55ca8f86d8fb8a968e)), closes [#23657](https://github.com/ionic-team/ionic/issues/23657) [#15500](https://github.com/ionic-team/ionic/issues/15500) [#12310](https://github.com/ionic-team/ionic/issues/12310)
### Performance Improvements
* remove shims for legacy browsers no longer supported in v6 ([#23592](https://github.com/ionic-team/ionic/issues/23592)) ([259b135](https://github.com/ionic-team/ionic/commit/259b1359dbd20d4f85036ae46901a051cd8fc98b))
## [5.6.12](https://github.com/ionic-team/ionic/compare/v5.6.11...v5.6.12) (2021-07-21)
@@ -717,36 +190,9 @@ Please see the [Ionic 6 Upgrade Guide](https://ionicframework.com/docs/next/intr
# [6.0.0-beta.1](https://github.com/ionic-team/ionic/compare/v6.0.0-beta.0...v6.0.0-beta.1) (2021-07-01)
### Bug Fixes
* **accordion:** improved reliability of accordion animations ([#23531](https://github.com/ionic-team/ionic/issues/23531)) ([6fbd60b](https://github.com/ionic-team/ionic/commit/6fbd60b0df56dc927226474a1ffa322d979c563e)), closes [#23504](https://github.com/ionic-team/ionic/issues/23504)
* **content:** add touch-action manipulation for a11y zoom and pan ([#23534](https://github.com/ionic-team/ionic/issues/23534)) ([6ca1780](https://github.com/ionic-team/ionic/commit/6ca17805b8b1ea38d7fc16d091324da16a4193c6)), closes [#22805](https://github.com/ionic-team/ionic/issues/22805)
* **datetime:** scroll position no longer gets reset when using datetime in overlay ([#23543](https://github.com/ionic-team/ionic/issues/23543)) ([b735b58](https://github.com/ionic-team/ionic/commit/b735b587cda777ac481bb580c883d9734145f31e))
* **input, select, textarea:** change type of placeholder prop to string only ([#23500](https://github.com/ionic-team/ionic/issues/23500)) ([f3ae431](https://github.com/ionic-team/ionic/commit/f3ae4319bb64debab304973856a33e422ac910a1)), closes [#22976](https://github.com/ionic-team/ionic/issues/22976)
* **popover:** size property now works when providing only event ([#23532](https://github.com/ionic-team/ionic/issues/23532)) ([bdc1f23](https://github.com/ionic-team/ionic/commit/bdc1f2360d7795472cc242a86eb4376d05fa0bb7)), closes [#23528](https://github.com/ionic-team/ionic/issues/23528)
* **popover:** update animation to better match MD spec ([#23541](https://github.com/ionic-team/ionic/issues/23541)) ([bdb95b7](https://github.com/ionic-team/ionic/commit/bdb95b7b6dd798cbc6d1786ae54fa95ac1dfd096))
* **react:** export accordion and accordion group components ([#23497](https://github.com/ionic-team/ionic/issues/23497)) ([a664d42](https://github.com/ionic-team/ionic/commit/a664d4268dea8e84ab9e3b150043ac8f87fb53c7))
* **vue:** navigating between parameterized pages now results in page transition ([#23525](https://github.com/ionic-team/ionic/issues/23525)) ([e30b17c](https://github.com/ionic-team/ionic/commit/e30b17c5bbd1af6936a8d7a98d1f7a115073e029)), closes [#22662](https://github.com/ionic-team/ionic/issues/22662)
### Features
* **accordion-group:** add animated property to disable animations ([#23530](https://github.com/ionic-team/ionic/issues/23530)) ([9a60dd0](https://github.com/ionic-team/ionic/commit/9a60dd0ea7c55acf0fdd1161433e5b4ed40778f2))
* **action-sheet, alert:** add id to AlertButton and ActionSheetButton ([#18992](https://github.com/ionic-team/ionic/issues/18992)) ([9e24a0b](https://github.com/ionic-team/ionic/commit/9e24a0b49357a3a39ca89f026ff23271a365d935)), closes [#22959](https://github.com/ionic-team/ionic/issues/22959)
* **vue:** extend useIonRouter hook for programmatic navigation with animation control ([#23499](https://github.com/ionic-team/ionic/issues/23499)) ([fc9e1b4](https://github.com/ionic-team/ionic/commit/fc9e1b4b361938e5644683c395a565be2de1eab9)), closes [#23450](https://github.com/ionic-team/ionic/issues/23450)
### BREAKING CHANGES
* **input, select, textarea:** Updated the `placeholder` property on `ion-input`, `ion-textarea`, and `ion-select` to have a type of `string | undefined`.
## [5.6.11](https://github.com/ionic-team/ionic/compare/v5.6.10...v5.6.11) (2021-07-01)
### Bug Fixes
* **animation:** typescript interface has correct return value for progress methods ([#23536](https://github.com/ionic-team/ionic/issues/23536)) ([f3d6abb](https://github.com/ionic-team/ionic/commit/f3d6abbc1beeafe3b5e7f473d70d0b8ef4c79bc8))
@@ -754,63 +200,6 @@ Please see the [Ionic 6 Upgrade Guide](https://ionicframework.com/docs/next/intr
# [6.0.0-beta.0](https://github.com/ionic-team/ionic/compare/v5.6.10...v6.0.0-beta.0) (2021-06-23)
### Bug Fixes
* **accordion:** toggle icon now shows up in vue and react ([#23426](https://github.com/ionic-team/ionic/issues/23426)) ([c716617](https://github.com/ionic-team/ionic/commit/c7166179457a8e2c7e1702c5761bc6368dbd156f))
* **datetime:** changing time emits ionChange ([#23463](https://github.com/ionic-team/ionic/issues/23463)) ([b0cce36](https://github.com/ionic-team/ionic/commit/b0cce360c83ac564e053523cc31b32d1deaeda0c))
* **modal:** add additional padding to toolbars in iOS modal ([#23262](https://github.com/ionic-team/ionic/issues/23262)) ([a037b65](https://github.com/ionic-team/ionic/commit/a037b65aad5cfc0477322a8f36105b9009366ec2)), closes [#22778](https://github.com/ionic-team/ionic/issues/22778)
* **modal:** border radius is correctly set on card style modal ([#23461](https://github.com/ionic-team/ionic/issues/23461)) ([bccb8ad](https://github.com/ionic-team/ionic/commit/bccb8ad5fb5ec7f98a6cbfa62a403ecaca7fbdb6))
* **modal, popover:** overlays now automatically determine if they are inline ([#23434](https://github.com/ionic-team/ionic/issues/23434)) ([8dbe8ba](https://github.com/ionic-team/ionic/commit/8dbe8ba7bc26792c5024f81cf4752f5b78317492))
* **popover:** shadow parts now correctly added ([#23446](https://github.com/ionic-team/ionic/issues/23446)) ([e1a9613](https://github.com/ionic-team/ionic/commit/e1a96130ebab1e481e880f0f3876f421976f08d5))
* **popover:** update prop defaults, use correct delegate ([#23340](https://github.com/ionic-team/ionic/issues/23340)) ([960778a](https://github.com/ionic-team/ionic/commit/960778a36f6eb6318cc740c4f7a255107723b8fd))
* **searchbar:** showClearButton now defaults to 'always' for improved usability with screen readers ([#23475](https://github.com/ionic-team/ionic/issues/23475)) ([80f181d](https://github.com/ionic-team/ionic/commit/80f181d4846507ee6bd4150bb568fca9b6660428))
* **vue:** ensure webpack does not eliminate core css ([#23465](https://github.com/ionic-team/ionic/issues/23465)) ([ee3a00f](https://github.com/ionic-team/ionic/commit/ee3a00fde61b4d1d3168d34b3d23bb97dd154154))
### Code Refactoring
* **all:** update required browser, framework, and mobile platform versions for v6 ([#23443](https://github.com/ionic-team/ionic/issues/23443)) ([c842dd8](https://github.com/ionic-team/ionic/commit/c842dd88c98888b2afab08ac5e8bc57c2a4c2fbd))
* **angular:** remove Config.set() method ([#22918](https://github.com/ionic-team/ionic/issues/22918)) ([9e05891](https://github.com/ionic-team/ionic/commit/9e0589173607b3c0eff7794079123354c2eeaa1a))
* **header:** removed border from last toolbar when using collapsible large title ([#22891](https://github.com/ionic-team/ionic/issues/22891)) ([c72bc5d](https://github.com/ionic-team/ionic/commit/c72bc5dbd76cd3ce622a4b3cedcb7446a2819384)), closes [#22777](https://github.com/ionic-team/ionic/issues/22777)
* **ios:** update toolbar and tabbar default background colors ([#22852](https://github.com/ionic-team/ionic/issues/22852)) ([3d615cb](https://github.com/ionic-team/ionic/commit/3d615cb3c7b233b08b9da6ac04096e16bbb60bfc)), closes [#22780](https://github.com/ionic-team/ionic/issues/22780)
* **toast:** whitespace variable now defaults to normal ([#22866](https://github.com/ionic-team/ionic/issues/22866)) ([9b78689](https://github.com/ionic-team/ionic/commit/9b786899e550c391b9395c669f9bba8f39ac98aa))
* **vue:** drop support for "on" prefixed overlay events and bump minimum required version of vue to 3.0.6 ([#23229](https://github.com/ionic-team/ionic/issues/23229)) ([6fcb3a6](https://github.com/ionic-team/ionic/commit/6fcb3a62b1b12c5ded11179e83854592d4309bdf))
* **vue:** remove support for child routes nested inside of tabs ([#22919](https://github.com/ionic-team/ionic/issues/22919)) ([75458ac](https://github.com/ionic-team/ionic/commit/75458ac7fb95f56a6ec460f85cf7d7720ce0c070))
### Features
* **accordion:** add accordion and accordion-group components ([#22865](https://github.com/ionic-team/ionic/issues/22865)) ([073883a](https://github.com/ionic-team/ionic/commit/073883a0987149e9f6258ca43c46f5ed4bce0dc5)), closes [#17094](https://github.com/ionic-team/ionic/issues/17094)
* **breadcrumbs:** add breadcrumbs component ([#22701](https://github.com/ionic-team/ionic/issues/22701)) ([2f6b1e4](https://github.com/ionic-team/ionic/commit/2f6b1e4eea307c6f14345704e5824378ef079acb)), closes [#22770](https://github.com/ionic-team/ionic/issues/22770)
* **datetime:** add calendar picker ([#23416](https://github.com/ionic-team/ionic/issues/23416)) ([932d3ca](https://github.com/ionic-team/ionic/commit/932d3ca62f3e3ef08acb065ce6ec46faa3811f96)), closes [#19423](https://github.com/ionic-team/ionic/issues/19423)
* **item:** add helper text, error text, counter, shape, and fill mode ([#23354](https://github.com/ionic-team/ionic/issues/23354)) ([faefe97](https://github.com/ionic-team/ionic/commit/faefe97da6a9d5beff1183d10efd0df9c4e3ebd7)), closes [#19619](https://github.com/ionic-team/ionic/issues/19619)
* **modal:** modals can now be used inline ([#23341](https://github.com/ionic-team/ionic/issues/23341)) ([3be1c3d](https://github.com/ionic-team/ionic/commit/3be1c3dcd73e6039a89b19b409e63877cda37f6e)), closes [#20117](https://github.com/ionic-team/ionic/issues/20117) [#20263](https://github.com/ionic-team/ionic/issues/20263)
* **popover:** account for ionShadowTarget elements ([#23436](https://github.com/ionic-team/ionic/issues/23436)) ([0e38d42](https://github.com/ionic-team/ionic/commit/0e38d4276110dcd94db5adc3b6aee3b5b0befc5c))
* **popover:** add desktop support ([#23258](https://github.com/ionic-team/ionic/issues/23258)) ([a67a0fa](https://github.com/ionic-team/ionic/commit/a67a0fabb8249685bbe93ed862839e2b2e76cd5a)), closes [#21599](https://github.com/ionic-team/ionic/issues/21599)
* **popover:** popover can now be used inline ([#23231](https://github.com/ionic-team/ionic/issues/23231)) ([308fa1c](https://github.com/ionic-team/ionic/commit/308fa1c0dd054cfc2ea54d2edc99e7a4b549f6f0))
* **slides:** add IonicSwiper modules, deprecate ion-slides, and add link to migration ([#23447](https://github.com/ionic-team/ionic/issues/23447)) ([623c84a](https://github.com/ionic-team/ionic/commit/623c84ab082668a996c654e18ffc9768f68b85dd))
* **spinner:** add lines-sharp, lines-sharp-small, update styles for ios 14 ([#22397](https://github.com/ionic-team/ionic/issues/22397)) ([2a5b272](https://github.com/ionic-team/ionic/commit/2a5b272a329bbad1ca07705f84f0fd06e3ef32ad))
* **vue:** add custom elements bundle ([#23458](https://github.com/ionic-team/ionic/issues/23458)) ([dc48a9f](https://github.com/ionic-team/ionic/commit/dc48a9f1a2dff8a2d644112bbe1df8b0b6811848))
### BREAKING CHANGES
* **searchbar:** The `showClearButton` property on `ion-searchbar` now defaults to `'always'`.
* **datetime:** The `ion-datetime` component has been revamped to use a new calendar style. As a result, some APIs have been removed. See https://github.com/ionic-team/ionic-framework/blob/master/BREAKING.md for more details.
* **all:** Browser, JS Framework, and mobile platform minimum required versions have been updated.
* **popover:** Converted `ion-popover` to use the Shadow DOM.
* **vue:** - Dropped support for prefixed overlay events in favor of non prefixed events (I.e. `@onDidDismiss` becomes `@didDismiss`).
- Minimum required version of Vue is now Vue v3.0.6 or newer.
* **vue:** Support for child routes nested inside of tabs has been removed to better conform to Vue Router's best practices. Additional routes should be written as sibling routes with the parent tab as the path prefix.
* **angular:** The `Config.set()` method has been removed. See https://ionicframework.com/docs/angular/config for examples on how to set config globally, per-component, and per-platform.
* **ios:** The tab bar and toolbar default background colors have been updated to better reflect the latest iOS styles.
* **header:** The last toolbar in the header with a collapsible large title no longer has a border.
* **toast:** The `--white-space` CSS Variable now defaults to `normal`.
## [5.6.10](https://github.com/ionic-team/ionic/compare/v5.6.9...v5.6.10) (2021-06-22)
@@ -4737,4 +4126,4 @@ The following dependencies need to be updated to resolve build errors
<a name="0.1.0"></a>
## [0.1.0](https://github.com/ionic-team/ionic/commit/43a8c4c7a719169336a84964fc1c737562d764a6) (2018-03-01)
## [0.1.0](https://github.com/ionic-team/ionic/commit/43a8c4c7a719169336a84964fc1c737562d764a6) (2018-03-01)

View File

@@ -1,63 +1,22 @@
<p align="center">
<a href="#">
<img alt="Ionic" src="https://github.com/ionic-team/ionic-framework/blob/main/.github/assets/logo.png?raw=true" width="60" />
</a>
</p>
# Ionic Framework
<h1 align="center">
Ionic
</h1>
[Ionic Framework](https://ionicframework.com/) is the open-source mobile app development framework that makes it easy to
build top quality native and progressive web apps with web technologies.
<p align="center">
Ionic is an open source app development toolkit for building modern, fast, top-quality cross-platform native and Progressive Web Apps from a single codebase with JavaScript and the Web.
</p>
<p align="center">
Ionic is based on <a href="https://www.webcomponents.org/introduction">Web Components</a>, which enables significant performance, usability, and feature improvements alongside support for popular web frameworks like <a href="https://angular.io/">Angular</a>, <a href="https://reactjs.com/">React</a>, and <a href="https://vuejs.org/">Vue</a>.
Ionic Framework is based on [Web Components](https://www.webcomponents.org/introduction) and comes with many significant performance, usability, and feature improvements over the past versions.
</p>
## Looking for the Ionic Framework v6 beta?
<p align="center">
<a href="https://github.com/ionic-team/ionic-framework/blob/main/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="Ionic Framework is released under the MIT license." />
</a>
<a href="https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md">
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs welcome!" />
</a>
<a href="https://twitter.com/Ionicframework">
<img src="https://img.shields.io/twitter/follow/ionicframework.svg?label=Follow%20@IonicFramework" alt="Follow @IonicFramework">
</a>
<a href="https://ionic.link/discord">
<img src="https://img.shields.io/discord/520266681499779082?color=7289DA&label=%23ionic&logo=discord&logoColor=white" alt="Official Ionic Discord" />
</a>
</p>
<h2 align="center">
<a href="https://ionicframework.com/getting-started/">Quickstart</a>
<span> · </span>
<a href="https://ionicframework.com/docs/">
Documentation
</a>
<span> · </span>
<a href="https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md">Contribute</a>
<span> · </span>
<a href="https://blog.ionicframework.com/">Blog</a>
<br />
Community:
<a href="https://ionic.link/discord">Discord</a>
<span> · </span>
<a href="https://forum.ionicframework.com/">Forums</a>
<span> · </span>
<a href="https://twitter.com/Ionicframework">Twitter</a>
</h2>
[Click here to get started!](https://github.com/ionic-team/ionic-framework/blob/next/BETA.md)
### Packages
| Project | Package | Version | Downloads| Links |
| ------- | ------- | ------- | -------- |:-----:|
| **Core** | [`@ionic/core`](https://www.npmjs.com/package/@ionic/core) | [![version](https://img.shields.io/npm/v/@ionic/core/latest.svg)](https://www.npmjs.com/package/@ionic/core) | <a href="https://www.npmjs.com/package/@ionic/core" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/core.svg" alt="NPM Downloads" /></a> | [`README.md`](core/README.md)
| **Angular** | [`@ionic/angular`](https://www.npmjs.com/package/@ionic/angular) | [![version](https://img.shields.io/npm/v/@ionic/angular/latest.svg)](https://www.npmjs.com/package/@ionic/angular) | <a href="https://www.npmjs.com/package/@ionic/angular" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/angular.svg" alt="NPM Downloads" /></a> | [`README.md`](angular/README.md)
| **Vue** | [`@ionic/vue`](https://www.npmjs.com/package/@ionic/vue) | [![version](https://img.shields.io/npm/v/@ionic/vue/latest.svg)](https://www.npmjs.com/package/@ionic/vue) | <a href="https://www.npmjs.com/package/@ionic/vue" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/vue.svg" alt="NPM Downloads" /></a> | [`README.md`](packages/vue/README.md)
| **React** | [`@ionic/react`](https://www.npmjs.com/package/@ionic/react) | [![version](https://img.shields.io/npm/v/@ionic/react/latest.svg)](https://www.npmjs.com/package/@ionic/react) | <a href="https://www.npmjs.com/package/@ionic/react" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/react.svg" alt="NPM Downloads" /></a> |[`README.md`](packages/react/README.md)
| Project | Package | Version | Links |
| ------- | ------- | ------- |:-----:|
| **Core** | [`@ionic/core`](https://www.npmjs.com/package/@ionic/core) | [![version](https://img.shields.io/npm/v/@ionic/core/latest.svg)](https://www.npmjs.com/package/@ionic/core) | [`README.md`](core/README.md)
| **Angular** | [`@ionic/angular`](https://www.npmjs.com/package/@ionic/angular) | [![version](https://img.shields.io/npm/v/@ionic/angular/latest.svg)](https://www.npmjs.com/package/@ionic/angular) | [`README.md`](angular/README.md)
| **Vue** | [`@ionic/vue`](https://www.npmjs.com/package/@ionic/vue) | [![version](https://img.shields.io/npm/v/@ionic/vue/latest.svg)](https://www.npmjs.com/package/@ionic/vue) | [`README.md`](packages/vue/README.md)
| **React** | [`@ionic/react`](https://www.npmjs.com/package/@ionic/react) | [![version](https://img.shields.io/npm/v/@ionic/react/latest.svg)](https://www.npmjs.com/package/@ionic/react) | [`README.md`](packages/react/README.md)
Looking for the `ionic-angular` package? Ionic 3 has been moved to the [`ionic-v3`](https://github.com/ionic-team/ionic-v3) repo. See [Earlier Versions](#earlier-versions).
@@ -67,18 +26,6 @@ Start a new project by following our quick [Getting Started guide](https://ionic
We would love to hear from you! If you have any feedback or run into issues using our framework, please file
an [issue](https://github.com/ionic-team/ionic/issues/new) on this repository.
### Migration Guides
Already have an Ionic app? These guides will help you migrate to the latest versions.
* [Migrate from v5 to v6](https://ionicframework.com/docs/reference/migration#migrating-from-ionic-5x-to-ionic-6x)
* [Migrate from v4 to v5](https://ionicframework.com/docs/reference/migration#migrating-from-ionic-4x-to-ionic-5x)
* [Migrate from v3 to v4](https://ionicframework.com/docs/reference/migration#migrating-from-ionic-30-to-ionic-40)
### Examples
The [Ionic Conference App](https://github.com/ionic-team/ionic-conference-app) is a full featured Ionic app.
It is the perfect starting point for learning and building your own app.
### Contributing
@@ -89,6 +36,13 @@ label.
Please note that this project is released with a [Contributor Code of Conduct](https://github.com/ionic-team/ionic/blob/main/CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
### Examples
The [Ionic Conference App](https://github.com/ionic-team/ionic-conference-app) is a full featured Ionic app.
It is the perfect starting point for learning and building your own app.
### Future Goals
As Ionic Framework components migrate to the web component standard, a goal of ours is to have Ionic Framework easily work within all of the popular frameworks.

View File

@@ -1,4 +0,0 @@
dist
virtual-scroll
scripts
proxies.ts

View File

@@ -1,30 +0,0 @@
{
"root": true,
"ignorePatterns": ["test/**/*", "src/directives/virtual-scroll/**/*"],
"overrides": [
{
"files": ["*.ts"],
"parserOptions": {
"project": ["tsconfig.json"],
"createDefaultProgram": true
},
"extends": [
"@ionic/eslint-config/recommended",
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@typescript-eslint/consistent-type-imports": "off",
"@angular-eslint/component-class-suffix": "off",
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "ion",
"style": "kebab-case"
}
]
}
}
]
}

View File

@@ -1,6 +0,0 @@
dist
virtual-scroll
scripts
test
src/directives/proxies.ts
src/directives/angular-component-lib/utils.ts

View File

@@ -1,12 +0,0 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [6.0.2](https://github.com/ionic-team/ionic/compare/v6.0.1...v6.0.2) (2022-01-11)
### Bug Fixes
* **angular:** attach change detector ref for inline overlays ([#24521](https://github.com/ionic-team/ionic/issues/24521)) ([5c54593](https://github.com/ionic-team/ionic/commit/5c54593dde64ae61347568405ebf74502cfff370)), closes [#24502](https://github.com/ionic-team/ionic/issues/24502)
* **angular:** popover will respect side attribute value ([#24470](https://github.com/ionic-team/ionic/issues/24470)) ([e6955a2](https://github.com/ionic-team/ionic/commit/e6955a26b92fc536c5c73b60b5943881c7d58ee1)), closes [#24466](https://github.com/ionic-team/ionic/issues/24466)

15800
angular/package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "6.0.2",
"version": "5.9.3",
"description": "Angular specific wrappers for @ionic/core",
"keywords": [
"ionic",
@@ -22,76 +22,65 @@
"bugs": {
"url": "https://github.com/ionic-team/ionic/issues"
},
"publishConfig": {
"directory": "dist"
},
"homepage": "https://ionicframework.com/",
"scripts": {
"prepublishOnly": "npm run build",
"build": "npm run clean && npm run build.ng && npm run build.core && npm run clean-generated",
"build.core": "node scripts/build-core.js",
"build.fesm": "rollup --config ./scripts/rollup.config.js",
"build.link": "npm run build && node scripts/link-copy.js",
"build.ng": "ng-packagr -p package.json -c tsconfig.json",
"build.ng": "ng-packagr -p package.json",
"build.es2015": "ngc -p tsconfig.json && rollup --config ./scripts/rollup.config.js",
"build.es5": "ngc -p tsconfig.legacy.json && rollup --config ./scripts/rollup.config.legacy.js",
"clean": "node scripts/clean.js",
"clean-generated": "node ./scripts/clean-generated.js",
"lint": "npm run eslint && npm run prettier -- --check",
"fmt": "npm run eslint -- --fix && npm run prettier -- --write",
"prettier": "prettier \"**/*.ts\"",
"eslint": "eslint . --ext .ts",
"lint": "npm run lint.ts",
"lint.ts": "tslint --project .",
"lint.fix": "tslint --project . --fix",
"prerelease": "npm run validate && np prerelease --yolo --any-branch --tag next",
"test": "echo 'angular no tests yet'",
"tsc": "tsc -p .",
"validate": "npm i && npm run lint && npm run test && npm run build"
},
"dependencies": {
"@ionic/core": "^6.0.2",
"jsonc-parser": "^3.0.0",
"tslib": "^2.0.0"
"@ionic/core": "5.9.3",
"tslib": "^1.9.3"
},
"peerDependencies": {
"@angular/core": ">=12.0.0",
"@angular/forms": ">=12.0.0",
"@angular/router": ">=12.0.0",
"rxjs": ">=6.6.0",
"zone.js": ">=0.11.0"
"@angular/core": ">=8.2.7",
"@angular/forms": ">=8.2.7",
"@angular/router": ">=8.2.7",
"rxjs": ">=6.2.0",
"zone.js": ">=0.8.26"
},
"devDependencies": {
"@angular-devkit/core": "^12.0.0",
"@angular-devkit/schematics": "^12.0.0",
"@angular-eslint/eslint-plugin": "^12.5.0",
"@angular-eslint/eslint-plugin-template": "^12.5.0",
"@angular-eslint/template-parser": "^12.5.0",
"@angular/common": "^12.0.0",
"@angular/compiler": "^12.0.0",
"@angular/compiler-cli": "^12.0.0",
"@angular/core": "^12.0.0",
"@angular/forms": "^12.0.0",
"@angular/router": "^12.0.0",
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@schematics/angular": "^12.2.9",
"@angular-devkit/core": "8.3.17",
"@angular-devkit/schematics": "8.3.17",
"@angular/common": "8.2.13",
"@angular/compiler": "8.2.13",
"@angular/compiler-cli": "8.2.13",
"@angular/core": "8.2.13",
"@angular/forms": "8.2.13",
"@angular/router": "8.2.13",
"@types/node": "12.12.5",
"@typescript-eslint/eslint-plugin": "^4.32.0",
"@typescript-eslint/parser": "^4.32.0",
"eslint": "^7.32.0",
"eslint-plugin-import": "^2.25.2",
"fs-extra": "^7.0.0",
"ng-packagr": "^12.0.0",
"prettier": "^2.4.1",
"glob": "^7.1.4",
"ng-packagr": "^9.1.5",
"rollup": "~1.17.0",
"rollup-plugin-node-resolve": "~5.2.0",
"rxjs": "^6.6.2",
"typescript": "4.2.4",
"typescript-eslint-language-service": "^4.1.5",
"zone.js": "~0.11.4"
"tsickle": "^0.39.1",
"tslint": "^5.12.1",
"tslint-ionic-rules": "0.0.21",
"typescript": "3.4.5",
"zone.js": "^0.11.1"
},
"prettier": "@ionic/prettier-config",
"schematics": "./schematics/collection.json",
"ngPackage": {
"lib": {
"entryFile": "src/index.ts"
},
"allowedNonPeerDependencies": [
"@ionic/core",
"jsonc-parser"
"whitelistedNonPeerDependencies": [
"@ionic/core"
]
}
}

View File

@@ -10,13 +10,15 @@ export const appInitialize = (config: Config, doc: Document, zone: NgZone) => {
return (): any => {
const win: IonicWindow | undefined = doc.defaultView as any;
if (win && typeof (window as any) !== 'undefined') {
setupConfig({
...config,
_zoneGate: (h: any) => zone.run(h),
_zoneGate: (h: any) => zone.run(h)
});
const aelFn =
'__zone_symbol__addEventListener' in (doc.body as any) ? '__zone_symbol__addEventListener' : 'addEventListener';
const aelFn = '__zone_symbol__addEventListener' in (doc.body as any)
? '__zone_symbol__addEventListener'
: 'addEventListener';
return applyPolyfills().then(() => {
return defineCustomElements(win, {
@@ -29,7 +31,7 @@ export const appInitialize = (config: Config, doc: Document, zone: NgZone) => {
},
rel(elm, eventName, cb, opts) {
elm.removeEventListener(eventName, cb, opts);
},
}
});
});
}

View File

@@ -1,63 +0,0 @@
/* eslint-disable */
/* tslint:disable */
import { fromEvent } from 'rxjs';
export const proxyInputs = (Cmp: any, inputs: string[]) => {
const Prototype = Cmp.prototype;
inputs.forEach(item => {
Object.defineProperty(Prototype, item, {
get() {
return this.el[item];
},
set(val: any) {
this.z.runOutsideAngular(() => (this.el[item] = val));
}
});
});
};
export const proxyMethods = (Cmp: any, methods: string[]) => {
const Prototype = Cmp.prototype;
methods.forEach(methodName => {
Prototype[methodName] = function () {
const args = arguments;
return this.z.runOutsideAngular(() =>
this.el[methodName].apply(this.el, args)
);
};
});
};
export const proxyOutputs = (instance: any, el: any, events: string[]) => {
events.forEach(eventName => instance[eventName] = fromEvent(el, eventName));
}
export const defineCustomElement = (tagName: string, customElement: any) => {
if (
customElement !== undefined &&
typeof customElements !== 'undefined' &&
!customElements.get(tagName)
) {
customElements.define(tagName, customElement);
}
}
// tslint:disable-next-line: only-arrow-functions
export function ProxyCmp(opts: { defineCustomElementFn?: () => void, inputs?: any; methods?: any }) {
const decorator = function (cls: any) {
const { defineCustomElementFn, inputs, methods } = opts;
if (defineCustomElementFn !== undefined) {
defineCustomElementFn();
}
if (inputs) {
proxyInputs(cls, inputs);
}
if (methods) {
proxyMethods(cls, methods);
}
return cls;
};
return decorator;
}

View File

@@ -1,30 +1,32 @@
import { Directive, HostListener, ElementRef, Injector } from '@angular/core';
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor, setIonicClasses } from './value-accessor';
@Directive({
/* tslint:disable-next-line:directive-selector */
selector: 'ion-checkbox,ion-toggle',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: BooleanValueAccessorDirective,
multi: true,
},
],
useExisting: BooleanValueAccessor,
multi: true
}
]
})
export class BooleanValueAccessorDirective extends ValueAccessor {
export class BooleanValueAccessor extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
}
writeValue(value: any): void {
writeValue(value: any) {
this.el.nativeElement.checked = this.lastValue = value == null ? false : value;
setIonicClasses(this.el);
}
@HostListener('ionChange', ['$event.target'])
_handleIonChange(el: any): void {
_handleIonChange(el: any) {
this.handleChangeEvent(el, el.checked);
}
}

View File

@@ -1,30 +1,32 @@
import { Directive, HostListener, ElementRef, Injector } from '@angular/core';
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from './value-accessor';
@Directive({
/* tslint:disable-next-line:directive-selector */
selector: 'ion-input[type=number]',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: NumericValueAccessorDirective,
multi: true,
},
],
useExisting: NumericValueAccessor,
multi: true
}
]
})
export class NumericValueAccessorDirective extends ValueAccessor {
export class NumericValueAccessor extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
}
@HostListener('ionChange', ['$event.target'])
_handleIonChange(el: any): void {
_handleIonChange(el: any) {
this.handleChangeEvent(el, el.value);
}
registerOnChange(fn: (_: number | null) => void): void {
super.registerOnChange((value) => {
registerOnChange(fn: (_: number | null) => void) {
super.registerOnChange(value => {
fn(value === '' ? null : parseFloat(value));
});
}

View File

@@ -1,4 +1,4 @@
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from './value-accessor';
@@ -9,18 +9,19 @@ import { ValueAccessor } from './value-accessor';
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: RadioValueAccessorDirective,
multi: true,
},
],
useExisting: RadioValueAccessor,
multi: true
}
]
})
export class RadioValueAccessorDirective extends ValueAccessor {
export class RadioValueAccessor extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
}
@HostListener('ionSelect', ['$event.target'])
_handleIonSelect(el: any): void {
_handleIonSelect(el: any) {
this.handleChangeEvent(el, el.checked);
}
}

View File

@@ -1,4 +1,4 @@
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from './value-accessor';
@@ -9,18 +9,19 @@ import { ValueAccessor } from './value-accessor';
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: SelectValueAccessorDirective,
multi: true,
},
],
useExisting: SelectValueAccessor,
multi: true
}
]
})
export class SelectValueAccessorDirective extends ValueAccessor {
export class SelectValueAccessor extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
}
@HostListener('ionChange', ['$event.target'])
_handleChangeEvent(el: any): void {
_handleChangeEvent(el: any) {
this.handleChangeEvent(el, el.value);
}
}

View File

@@ -1,4 +1,4 @@
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
import { Directive, ElementRef, HostListener, Injector } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { ValueAccessor } from './value-accessor';
@@ -9,18 +9,19 @@ import { ValueAccessor } from './value-accessor';
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: TextValueAccessorDirective,
multi: true,
},
],
useExisting: TextValueAccessor,
multi: true
}
]
})
export class TextValueAccessorDirective extends ValueAccessor {
export class TextValueAccessor extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
}
@HostListener('ionChange', ['$event.target'])
_handleInputEvent(el: any): void {
_handleInputEvent(el: any) {
this.handleChangeEvent(el, el.value);
}
}

View File

@@ -1,23 +1,19 @@
import { AfterViewInit, ElementRef, Injector, OnDestroy, Directive, HostListener } from '@angular/core';
import { AfterViewInit, ElementRef, HostListener, Injector, OnDestroy, Type } from '@angular/core';
import { ControlValueAccessor, NgControl } from '@angular/forms';
import { Subscription } from 'rxjs';
import { raf } from '../../util/util';
@Directive()
export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDestroy {
private onChange: (value: any) => void = () => {
/**/
};
private onTouched: () => void = () => {
/**/
};
private onChange: (value: any) => void = () => {/**/};
private onTouched: () => void = () => {/**/};
protected lastValue: any;
private statusChanges?: Subscription;
constructor(protected injector: Injector, protected el: ElementRef) {}
writeValue(value: any): void {
writeValue(value: any) {
/**
* TODO for Ionic 6:
* Change `value == null ? '' : value;`
@@ -29,7 +25,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
setIonicClasses(this.el);
}
handleChangeEvent(el: HTMLElement, value: any): void {
handleChangeEvent(el: HTMLElement, value: any) {
if (el === this.el.nativeElement) {
if (value !== this.lastValue) {
this.lastValue = value;
@@ -40,42 +36,38 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
}
@HostListener('ionBlur', ['$event.target'])
_handleBlurEvent(el: any): void {
_handleBlurEvent(el: any) {
if (el === this.el.nativeElement) {
this.onTouched();
setIonicClasses(this.el);
}
}
registerOnChange(fn: (value: any) => void): void {
registerOnChange(fn: (value: any) => void) {
this.onChange = fn;
}
registerOnTouched(fn: () => void): void {
registerOnTouched(fn: () => void) {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
setDisabledState(isDisabled: boolean) {
this.el.nativeElement.disabled = isDisabled;
}
ngOnDestroy(): void {
ngOnDestroy() {
if (this.statusChanges) {
this.statusChanges.unsubscribe();
}
}
ngAfterViewInit(): void {
ngAfterViewInit() {
let ngControl;
try {
ngControl = this.injector.get<NgControl>(NgControl);
} catch {
/* No FormControl or ngModel binding */
}
ngControl = this.injector.get<NgControl>(NgControl as Type<NgControl>);
} catch { /* No FormControl or ngModel binding */ }
if (!ngControl) {
return;
}
if (!ngControl) { return; }
// Listen for changes in validity, disabled, or pending states
if (ngControl.statusChanges) {
@@ -92,15 +84,15 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
* This patches the methods to manually sync
* the classes until this feature is implemented in Angular.
*/
const formControl = ngControl.control as any;
const formControl = ngControl.control;
if (formControl) {
const methodsToPatch = ['markAsTouched', 'markAllAsTouched', 'markAsUntouched', 'markAsDirty', 'markAsPristine'];
methodsToPatch.forEach((method) => {
if (formControl.get(method)) {
const oldFn = formControl[method].bind(formControl);
formControl[method] = (...params: any[]) => {
oldFn(...params);
setIonicClasses(this.el);
const methodsToPatch = ['markAsTouched', 'markAllAsTouched', 'markAsUntouched', 'markAsDirty', 'markAsPristine'] as const;
methodsToPatch.forEach(method => {
if (formControl[method]) {
const oldFn = formControl[method].bind(formControl);
formControl[method] = (...params: any[]) => {
oldFn(...params);
setIonicClasses(this.el);
};
}
});
@@ -108,7 +100,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
}
}
export const setIonicClasses = (element: ElementRef): void => {
export const setIonicClasses = (element: ElementRef) => {
raf(() => {
const input = element.nativeElement as HTMLElement;
const classes = getClasses(input);
@@ -135,11 +127,16 @@ const getClasses = (element: HTMLElement) => {
const setClasses = (element: HTMLElement, classes: string[]) => {
const classList = element.classList;
['ion-valid', 'ion-invalid', 'ion-touched', 'ion-untouched', 'ion-dirty', 'ion-pristine'].forEach((c) =>
classList.remove(c)
);
[
'ion-valid',
'ion-invalid',
'ion-touched',
'ion-untouched',
'ion-dirty',
'ion-pristine'
].forEach(c => classList.remove(c));
classes.forEach((c) => classList.add(c));
classes.forEach(c => classList.add(c));
};
const startsWith = (input: string, search: string): boolean => {

View File

@@ -1,4 +1,4 @@
import { Directive, HostListener, Input, Optional } from '@angular/core';
import { Directive, HostListener, Optional } from '@angular/core';
import { AnimationBuilder } from '@ionic/core';
import { Config } from '../../providers/config';
@@ -8,12 +8,11 @@ import { IonRouterOutlet } from './ion-router-outlet';
@Directive({
selector: 'ion-back-button',
inputs: ['defaultHref', 'routerAnimation'],
})
export class IonBackButtonDelegateDirective {
@Input()
defaultHref: string | undefined | null;
export class IonBackButtonDelegate {
@Input()
defaultHref: string | undefined | null;
routerAnimation?: AnimationBuilder;
constructor(
@@ -26,10 +25,10 @@ export class IonBackButtonDelegateDirective {
* @internal
*/
@HostListener('click', ['$event'])
onClick(ev: Event): void {
onClick(ev: Event) {
const defaultHref = this.defaultHref || this.config.get('backButtonDefaultHref');
if (this.routerOutlet?.canGoBack()) {
if (this.routerOutlet && this.routerOutlet.canGoBack()) {
this.navCtrl.setDirection('back', undefined, undefined, this.routerAnimation);
this.routerOutlet.pop();
ev.preventDefault();

View File

@@ -1,26 +1,11 @@
import { Location } from '@angular/common';
import {
ComponentFactoryResolver,
ComponentRef,
ElementRef,
Injector,
NgZone,
OnDestroy,
OnInit,
ViewContainerRef,
Attribute,
Directive,
EventEmitter,
Optional,
Output,
SkipSelf,
} from '@angular/core';
import { OutletContext, Router, ActivatedRoute, ChildrenOutletContexts, PRIMARY_OUTLET } from '@angular/router';
import { Attribute, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, NgZone, OnDestroy, OnInit, Optional, Output, SkipSelf, ViewContainerRef } from '@angular/core';
import { ActivatedRoute, ChildrenOutletContexts, OutletContext, PRIMARY_OUTLET, Router } from '@angular/router';
import { componentOnReady } from '@ionic/core';
import { Observable, BehaviorSubject } from 'rxjs';
import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, filter, switchMap } from 'rxjs/operators';
import { AnimationBuilder } from '../../ionic-core';
import { AnimationBuilder } from '../../';
import { Config } from '../../providers/config';
import { NavController } from '../../providers/nav-controller';
@@ -30,10 +15,8 @@ import { RouteView, getUrl } from './stack-utils';
@Directive({
selector: 'ion-router-outlet',
exportAs: 'outlet',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['animated', 'animation', 'swipeGesture'],
inputs: ['animated', 'animation', 'swipeGesture']
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class IonRouterOutlet implements OnDestroy, OnInit {
nativeEl: HTMLIonRouterOutletElement;
@@ -54,9 +37,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
tabsPrefix: string | undefined;
@Output() stackEvents = new EventEmitter<any>();
// eslint-disable-next-line @angular-eslint/no-output-rename
@Output('activate') activateEvents = new EventEmitter<any>();
// eslint-disable-next-line @angular-eslint/no-output-rename
@Output('deactivate') deactivateEvents = new EventEmitter<any>();
set animation(animation: AnimationBuilder) {
@@ -70,13 +51,11 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
set swipeGesture(swipe: boolean) {
this._swipeGesture = swipe;
this.nativeEl.swipeHandler = swipe
? {
canStart: () => this.stackCtrl.canGoBack(1) && !this.stackCtrl.hasRunningTask(),
onStart: () => this.stackCtrl.startBackTransition(),
onEnd: (shouldContinue) => this.stackCtrl.endBackTransition(shouldContinue),
}
: undefined;
this.nativeEl.swipeHandler = swipe ? {
canStart: () => this.stackCtrl.canGoBack(1) && !this.stackCtrl.hasRunningTask(),
onStart: () => this.stackCtrl.startBackTransition(),
onEnd: shouldContinue => this.stackCtrl.endBackTransition(shouldContinue)
} : undefined;
}
constructor(
@@ -114,12 +93,12 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
// If the outlet was not instantiated at the time the route got activated we need to populate
// the outlet when it is initialized (ie inside a NgIf)
const context = this.getContext();
if (context?.route) {
if (context && context.route) {
this.activateWith(context.route, context.resolver || null);
}
}
new Promise((resolve) => componentOnReady(this.nativeEl, resolve)).then(() => {
new Promise(resolve => componentOnReady(this.nativeEl, resolve)).then(() => {
if (this._swipeGesture === undefined) {
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
}
@@ -130,7 +109,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
return !!this.activated;
}
get component(): Record<string, unknown> {
get component(): object {
if (!this.activated) {
throw new Error('Outlet is not activated');
}
@@ -161,15 +140,13 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
/**
* Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
attach(_ref: ComponentRef<any>, _activatedRoute: ActivatedRoute): void {
attach(_ref: ComponentRef<any>, _activatedRoute: ActivatedRoute) {
throw new Error('incompatible reuse strategy');
}
deactivate(): void {
if (this.activated) {
if (this.activatedView) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const context = this.getContext()!;
this.activatedView.savedData = new Map(context.children['contexts']);
@@ -195,7 +172,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
const contextSnapshot = context.route.snapshot;
this.activatedView.savedExtras.queryParams = contextSnapshot.queryParams;
(this.activatedView.savedExtras.fragment as string | null) = contextSnapshot.fragment;
this.activatedView.savedExtras.fragment = contextSnapshot.fragment;
}
}
const c = this.component;
@@ -206,7 +183,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
}
}
activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver | null): void {
activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver | null) {
if (this.isActivated) {
throw new Error('Cannot activate an already activated outlet');
}
@@ -219,7 +196,6 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
const saved = enteringView.savedData;
if (saved) {
// self-restore
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const context = this.getContext()!;
context.children['contexts'] = saved;
}
@@ -227,7 +203,6 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
this.updateActivatedRouteProxy(cmpRef.instance, activatedRoute);
} else {
const snapshot = (activatedRoute as any)._futureSnapshot;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const component = snapshot.routeConfig!.component as any;
resolver = resolver || this.resolver;
@@ -255,7 +230,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
}
this.activatedView = enteringView;
this.stackCtrl.setActive(enteringView).then((data) => {
this.stackCtrl.setActive(enteringView).then(data => {
this.navCtrl.setTopOutlet(this);
this.activateEvents.emit(cmpRef.instance);
this.stackEvents.emit(data);
@@ -338,11 +313,11 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
private proxyObservable(component$: Observable<any>, path: string): Observable<any> {
return component$.pipe(
// First wait until the component instance is pushed
filter((component) => !!component),
switchMap((component) =>
filter(component => !!component),
switchMap(component =>
this.currentActivatedRoute$.pipe(
filter((current) => current !== null && current.component === component),
switchMap((current) => current && (current.activatedRoute as any)[path]),
filter(current => current !== null && current.component === component),
switchMap(current => current && (current.activatedRoute as any)[path]),
distinctUntilChanged()
)
)
@@ -369,7 +344,11 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
}
class OutletInjector implements Injector {
constructor(private route: ActivatedRoute, private childContexts: ChildrenOutletContexts, private parent: Injector) {}
constructor(
private route: ActivatedRoute,
private childContexts: ChildrenOutletContexts,
private parent: Injector
) { }
get(token: any, notFoundValue?: any): any {
if (token === ActivatedRoute) {
@@ -380,6 +359,7 @@ class OutletInjector implements Injector {
return this.childContexts;
}
// tslint:disable-next-line
return this.parent.get(token, notFoundValue);
}
}

View File

@@ -8,53 +8,54 @@ import { StackEvent } from './stack-utils';
@Component({
selector: 'ion-tabs',
template: ` <ng-content select="[slot=top]"></ng-content>
template: `
<ng-content select="[slot=top]"></ng-content>
<div class="tabs-inner">
<ion-router-outlet #outlet tabs="true" (stackEvents)="onPageSelected($event)"></ion-router-outlet>
</div>
<ng-content></ng-content>`,
styles: [
`
:host {
display: flex;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
styles: [`
:host {
display: flex;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
flex-direction: column;
flex-direction: column;
width: 100%;
height: 100%;
width: 100%;
height: 100%;
contain: layout size style;
z-index: $z-index-page-container;
}
.tabs-inner {
position: relative;
contain: layout size style;
z-index: $z-index-page-container;
}
.tabs-inner {
position: relative;
flex: 1;
flex: 1;
contain: layout size style;
}
`,
],
contain: layout size style;
}`
]
})
// eslint-disable-next-line @angular-eslint/component-class-suffix
export class IonTabs {
@ViewChild('outlet', { read: IonRouterOutlet, static: false }) outlet: IonRouterOutlet;
@ContentChild(IonTabBar, { static: false }) tabBar: IonTabBar | undefined;
@Output() ionTabsWillChange = new EventEmitter<{ tab: string }>();
@Output() ionTabsDidChange = new EventEmitter<{ tab: string }>();
constructor(private navCtrl: NavController) {}
constructor(
private navCtrl: NavController,
) { }
/**
* @internal
*/
onPageSelected(detail: StackEvent): void {
onPageSelected(detail: StackEvent) {
const stackId = detail.enteringView.stackId;
if (detail.tabSwitch && stackId !== undefined) {
if (this.tabBar) {
@@ -86,9 +87,9 @@ export class IonTabs {
* to the default tabRootUrl
*/
@HostListener('ionTabButtonClick', ['$event'])
select(tabOrEvent: string | CustomEvent): Promise<boolean> | undefined {
select(tabOrEvent: string | CustomEvent) {
const isTabString = typeof tabOrEvent === 'string';
const tab = isTabString ? tabOrEvent : (tabOrEvent as CustomEvent).detail.tab;
const tab = (isTabString) ? tabOrEvent : (tabOrEvent as CustomEvent).detail.tab;
const alreadySelected = this.outlet.getActiveStackId() === tab;
const tabRootUrl = `${this.outlet.tabsPrefix}/${tab}`;
@@ -107,14 +108,12 @@ export class IonTabs {
const activeView = this.outlet.getLastRouteView(activeStackId);
// If on root tab, do not navigate to root tab again
if (activeView?.url === tabRootUrl) {
return;
}
if (activeView.url === tabRootUrl) { return; }
const rootView = this.outlet.getRootView(tab);
const navigationExtras = rootView && tabRootUrl === rootView.url && rootView.savedExtras;
return this.navCtrl.navigateRoot(tabRootUrl, {
...navigationExtras,
...(navigationExtras),
animated: true,
animationDirection: 'back',
});
@@ -124,11 +123,11 @@ export class IonTabs {
* If there is a lastRoute, goto that, otherwise goto the fallback url of the
* selected tab
*/
const url = lastRoute?.url || tabRootUrl;
const navigationExtras = lastRoute?.savedExtras;
const url = lastRoute && lastRoute.url || tabRootUrl;
const navigationExtras = lastRoute && lastRoute.savedExtras;
return this.navCtrl.navigateRoot(url, {
...navigationExtras,
...(navigationExtras),
animated: true,
animationDirection: 'back',
});

View File

@@ -1,30 +1,15 @@
import { ComponentFactoryResolver, ElementRef, Injector, ViewContainerRef, Directive } from '@angular/core';
import { ComponentFactoryResolver, Directive, ElementRef, Injector, ViewContainerRef } from '@angular/core';
import { AngularDelegate } from '../../providers/angular-delegate';
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
import { ProxyCmp, proxyOutputs } from '../proxies-utils';
@ProxyCmp({
inputs: ['animated', 'animation', 'root', 'rootParams', 'swipeGesture'],
methods: [
'push',
'insert',
'insertPages',
'pop',
'popTo',
'popToRoot',
'removeIndex',
'setRoot',
'setPages',
'getActive',
'getByIndex',
'canGoBack',
'getPrevious',
],
methods: ['push', 'insert', 'insertPages', 'pop', 'popTo', 'popToRoot', 'removeIndex', 'setRoot', 'setPages', 'getActive', 'getByIndex', 'canGoBack', 'getPrevious']
})
@Directive({
selector: 'ion-nav',
selector: 'ion-nav'
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class NavDelegate {
protected el: HTMLElement;
constructor(
@@ -36,6 +21,6 @@ export class NavDelegate {
) {
this.el = ref.nativeElement;
ref.nativeElement.delegate = angularDelegate.create(resolver, injector, location);
proxyOutputs(this, this.el, ['ionNavDidChange', 'ionNavWillChange']);
proxyOutputs(this, this.el, ['ionNavDidChange' , 'ionNavWillChange' ]);
}
}

View File

@@ -19,7 +19,8 @@
* ```
*/
export class NavParams {
constructor(public data: { [key: string]: any } = {}) {}
constructor(public data: {[key: string]: any} = {}) {}
/**
* Get the value of a nav-parameter for the current view

View File

@@ -1,5 +1,5 @@
import { LocationStrategy } from '@angular/common';
import { ElementRef, OnChanges, OnDestroy, OnInit, Directive, HostListener, Input, Optional } from '@angular/core';
import { Directive, ElementRef, HostListener, Optional } from '@angular/core';
import { Router, RouterLink } from '@angular/router';
import { AnimationBuilder, RouterDirection } from '@ionic/core';
import { Subscription } from 'rxjs';
@@ -8,14 +8,13 @@ import { NavController } from '../../providers/nav-controller';
@Directive({
selector: '[routerLink]',
inputs: ['routerDirection', 'routerAnimation']
})
export class RouterLinkDelegateDirective implements OnInit, OnChanges, OnDestroy {
export class RouterLinkDelegate {
private subscription?: Subscription;
@Input()
routerDirection: RouterDirection = 'forward';
@Input()
routerAnimation?: AnimationBuilder;
constructor(
@@ -23,18 +22,18 @@ export class RouterLinkDelegateDirective implements OnInit, OnChanges, OnDestroy
private navCtrl: NavController,
private elementRef: ElementRef,
private router: Router,
@Optional() private routerLink?: RouterLink
) {}
@Optional() private routerLink?: RouterLink,
) { }
ngOnInit(): void {
ngOnInit() {
this.updateTargetUrlAndHref();
}
ngOnChanges(): void {
ngOnChanges(): any {
this.updateTargetUrlAndHref();
}
ngOnDestroy(): void {
ngOnDestroy(): any {
if (this.subscription) {
this.subscription.unsubscribe();
}
@@ -51,7 +50,7 @@ export class RouterLinkDelegateDirective implements OnInit, OnChanges, OnDestroy
* @internal
*/
@HostListener('click', ['$event'])
onClick(ev: UIEvent): void {
onClick(ev: UIEvent) {
this.navCtrl.setDirection(this.routerDirection, undefined, undefined, this.routerAnimation);
ev.preventDefault();
}

View File

@@ -6,18 +6,10 @@ import { AnimationBuilder, RouterDirection } from '@ionic/core';
import { bindLifecycleEvents } from '../../providers/angular-delegate';
import { NavController } from '../../providers/nav-controller';
import {
RouteView,
StackEvent,
computeStackId,
destroyView,
getUrl,
insertView,
isTabSwitch,
toSegments,
} from './stack-utils';
import { RouteView, StackEvent, computeStackId, destroyView, getUrl, insertView, isTabSwitch, toSegments } from './stack-utils';
export class StackController {
private views: RouteView[] = [];
private runningTask?: Promise<any>;
private skipTransition = false;
@@ -38,7 +30,7 @@ export class StackController {
createView(ref: ComponentRef<any>, activatedRoute: ActivatedRoute): RouteView {
const url = getUrl(this.router, activatedRoute);
const element = ref?.location?.nativeElement as HTMLElement;
const element = (ref && ref.location && ref.location.nativeElement) as HTMLElement;
const unlistenEvents = bindLifecycleEvents(this.zone, ref.instance, element);
return {
id: this.nextId++,
@@ -52,7 +44,7 @@ export class StackController {
getExistingView(activatedRoute: ActivatedRoute): RouteView | undefined {
const activatedUrlKey = getUrl(this.router, activatedRoute);
const view = this.views.find((vw) => vw.url === activatedUrlKey);
const view = this.views.find(vw => vw.url === activatedUrlKey);
if (view) {
view.ref.changeDetectorRef.reattach();
}
@@ -73,14 +65,17 @@ export class StackController {
let currentNavigation;
const router = this.router as any;
const router = (this.router as any);
// Angular >= 7.2.0
if (router.getCurrentNavigation) {
currentNavigation = router.getCurrentNavigation();
// Angular < 7.2.0
} else if (router.navigations?.value) {
} else if (
router.navigations &&
router.navigations.value
) {
currentNavigation = router.navigations.value;
}
@@ -91,7 +86,11 @@ export class StackController {
* we remove the last item
* from our views stack
*/
if (currentNavigation?.extras?.replaceUrl) {
if (
currentNavigation &&
currentNavigation.extras &&
currentNavigation.extras.replaceUrl
) {
if (this.views.length > 0) {
this.views.splice(-1, 1);
}
@@ -115,7 +114,12 @@ export class StackController {
* provided another animation.
*/
const customAnimation = enteringView.animationBuilder;
if (animationBuilder === undefined && direction === 'back' && !tabSwitch && customAnimation !== undefined) {
if (
animationBuilder === undefined &&
direction === 'back' &&
!tabSwitch &&
customAnimation !== undefined
) {
animationBuilder = customAnimation;
}
@@ -144,7 +148,7 @@ export class StackController {
enteringView,
direction,
animation,
tabSwitch,
tabSwitch
}));
});
});
@@ -154,7 +158,7 @@ export class StackController {
return this.getStack(stackId).length > deep;
}
pop(deep: number, stackId = this.getActiveStackId()): Promise<boolean> {
pop(deep: number, stackId = this.getActiveStackId()) {
return this.zone.run(() => {
const views = this.getStack(stackId);
if (views.length <= deep) {
@@ -166,7 +170,13 @@ export class StackController {
const viewSavedData = view.savedData;
if (viewSavedData) {
const primaryOutlet = viewSavedData.get('primary');
if (primaryOutlet?.route?._routerState?.snapshot.url) {
if (
primaryOutlet &&
primaryOutlet.route &&
primaryOutlet.route._routerState &&
primaryOutlet.route._routerState.snapshot &&
primaryOutlet.route._routerState.snapshot.url
) {
url = primaryOutlet.route._routerState.snapshot.url;
}
}
@@ -175,7 +185,7 @@ export class StackController {
});
}
startBackTransition(): Promise<boolean> | Promise<void> {
startBackTransition() {
const leavingView = this.activeView;
if (leavingView) {
const views = this.getStack(leavingView.stackId);
@@ -196,7 +206,7 @@ export class StackController {
return Promise.resolve();
}
endBackTransition(shouldComplete: boolean): void {
endBackTransition(shouldComplete: boolean) {
if (shouldComplete) {
this.skipTransition = true;
this.pop(1);
@@ -205,7 +215,7 @@ export class StackController {
}
}
getLastUrl(stackId?: string): RouteView | undefined {
getLastUrl(stackId?: string) {
const views = this.getStack(stackId);
return views.length > 0 ? views[views.length - 1] : undefined;
}
@@ -213,7 +223,7 @@ export class StackController {
/**
* @internal
*/
getRootUrl(stackId?: string): RouteView | undefined {
getRootUrl(stackId?: string) {
const views = this.getStack(stackId);
return views.length > 0 ? views[0] : undefined;
}
@@ -226,8 +236,7 @@ export class StackController {
return this.runningTask !== undefined;
}
destroy(): void {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
destroy() {
this.containerEl = undefined!;
this.views.forEach(destroyView);
this.activeView = undefined;
@@ -235,7 +244,7 @@ export class StackController {
}
private getStack(stackId: string | undefined) {
return this.views.filter((v) => v.stackId === stackId);
return this.views.filter(v => v.stackId === stackId);
}
private insertView(enteringView: RouteView, direction: RouterDirection) {
@@ -276,7 +285,7 @@ export class StackController {
direction,
showGoBack,
progressAnimation,
animationBuilder,
animationBuilder
});
}
}
@@ -288,15 +297,15 @@ export class StackController {
await this.runningTask;
this.runningTask = undefined;
}
const promise = (this.runningTask = task());
promise.finally(() => (this.runningTask = undefined));
const promise = this.runningTask = task();
promise.finally(() => this.runningTask = undefined);
return promise;
}
}
const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
if (typeof (requestAnimationFrame as any) === 'function') {
return new Promise<void>((resolve) => {
return new Promise<void>(resolve => {
requestAnimationFrame(() => {
cleanup(activeRoute, views, viewsSnapshot, location);
resolve();
@@ -307,9 +316,11 @@ const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot:
};
const cleanup = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
viewsSnapshot.filter((view) => !views.includes(view)).forEach(destroyView);
viewsSnapshot
.filter(view => !views.includes(view))
.forEach(destroyView);
views.forEach((view) => {
views.forEach(view => {
/**
* In the event that a user navigated multiple
* times in rapid succession, we want to make sure

View File

@@ -2,7 +2,7 @@ import { ComponentRef } from '@angular/core';
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
import { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core';
export const insertView = (views: RouteView[], view: RouteView, direction: RouterDirection): RouteView[] => {
export const insertView = (views: RouteView[], view: RouteView, direction: RouterDirection) => {
if (direction === 'root') {
return setRoot(views, view);
} else if (direction === 'forward') {
@@ -13,7 +13,7 @@ export const insertView = (views: RouteView[], view: RouteView, direction: Route
};
const setRoot = (views: RouteView[], view: RouteView) => {
views = views.filter((v) => v.stackId !== view.stackId);
views = views.filter(v => v.stackId !== view.stackId);
views.push(view);
return views;
};
@@ -21,7 +21,7 @@ const setRoot = (views: RouteView[], view: RouteView) => {
const setForward = (views: RouteView[], view: RouteView) => {
const index = views.indexOf(view);
if (index >= 0) {
views = views.filter((v) => v.stackId !== view.stackId || v.id <= view.id);
views = views.filter(v => v.stackId !== view.stackId || v.id <= view.id);
} else {
views.push(view);
}
@@ -31,25 +31,25 @@ const setForward = (views: RouteView[], view: RouteView) => {
const setBack = (views: RouteView[], view: RouteView) => {
const index = views.indexOf(view);
if (index >= 0) {
return views.filter((v) => v.stackId !== view.stackId || v.id <= view.id);
return views.filter(v => v.stackId !== view.stackId || v.id <= view.id);
} else {
return setRoot(views, view);
}
};
export const getUrl = (router: Router, activatedRoute: ActivatedRoute): string => {
export const getUrl = (router: Router, activatedRoute: ActivatedRoute) => {
const urlTree = router.createUrlTree(['.'], { relativeTo: activatedRoute });
return router.serializeUrl(urlTree);
};
export const isTabSwitch = (enteringView: RouteView, leavingView: RouteView | undefined): boolean => {
export const isTabSwitch = (enteringView: RouteView, leavingView: RouteView | undefined) => {
if (!leavingView) {
return true;
}
return enteringView.stackId !== leavingView.stackId;
};
export const computeStackId = (prefixUrl: string[] | undefined, url: string): string | undefined => {
export const computeStackId = (prefixUrl: string[] | undefined, url: string) => {
if (!prefixUrl) {
return undefined;
}
@@ -65,14 +65,14 @@ export const computeStackId = (prefixUrl: string[] | undefined, url: string): st
return undefined;
};
export const toSegments = (path: string): string[] => {
export const toSegments = (path: string) => {
return path
.split('/')
.map((s) => s.trim())
.filter((s) => s !== '');
.map(s => s.trim())
.filter(s => s !== '');
};
export const destroyView = (view: RouteView | undefined): void => {
export const destroyView = (view: RouteView | undefined) => {
if (view) {
// TODO lifecycle event
view.ref.destroy();

View File

@@ -1,128 +0,0 @@
/* eslint-disable */
/* tslint:disable */
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ContentChild,
ElementRef,
EventEmitter,
NgZone,
TemplateRef,
} from '@angular/core';
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
import { Components } from '@ionic/core';
export declare interface IonModal extends Components.IonModal {
/**
* Emitted after the modal has presented.
**/
ionModalDidPresent: EventEmitter<CustomEvent>;
/**
* Emitted before the modal has presented.
*/
ionModalWillPresent: EventEmitter<CustomEvent>;
/**
* Emitted before the modal has dismissed.
*/
ionModalWillDismiss: EventEmitter<CustomEvent>;
/**
* Emitted after the modal has dismissed.
*/
ionModalDidDismiss: EventEmitter<CustomEvent>;
/**
* Emitted after the modal has presented. Shorthand for ionModalWillDismiss.
*/
didPresent: EventEmitter<CustomEvent>;
/**
* Emitted before the modal has presented. Shorthand for ionModalWillPresent.
*/
willPresent: EventEmitter<CustomEvent>;
/**
* Emitted before the modal has dismissed. Shorthand for ionModalWillDismiss.
*/
willDismiss: EventEmitter<CustomEvent>;
/**
* Emitted after the modal has dismissed. Shorthand for ionModalDidDismiss.
*/
didDismiss: EventEmitter<CustomEvent>;
}
@ProxyCmp({
inputs: [
'animated',
'backdropBreakpoint',
'backdropDismiss',
'breakpoints',
'cssClass',
'enterAnimation',
'event',
'handle',
'initialBreakpoint',
'isOpen',
'keyboardClose',
'leaveAnimation',
'mode',
'presentingElement',
'showBackdrop',
'swipeToClose',
'translucent',
'trigger',
],
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss'],
})
@Component({
selector: 'ion-modal',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<ng-container [ngTemplateOutlet]="template" *ngIf="isCmpOpen"></ng-container>`,
inputs: [
'animated',
'backdropBreakpoint',
'backdropDismiss',
'breakpoints',
'cssClass',
'enterAnimation',
'event',
'handle',
'initialBreakpoint',
'isOpen',
'keyboardClose',
'leaveAnimation',
'mode',
'presentingElement',
'showBackdrop',
'swipeToClose',
'translucent',
'trigger',
],
})
export class IonModal {
@ContentChild(TemplateRef, { static: false }) template: TemplateRef<any>;
isCmpOpen: boolean = false;
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
this.el = r.nativeElement;
this.el.addEventListener('willPresent', () => {
this.isCmpOpen = true;
c.detectChanges();
});
this.el.addEventListener('didDismiss', () => {
this.isCmpOpen = false;
c.detectChanges();
});
proxyOutputs(this, this.el, [
'ionModalDidPresent',
'ionModalWillPresent',
'ionModalWillDismiss',
'ionModalDidDismiss',
'didPresent',
'willPresent',
'willDismiss',
'didDismiss',
]);
}
}

View File

@@ -1,128 +0,0 @@
/* eslint-disable */
/* tslint:disable */
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ContentChild,
ElementRef,
EventEmitter,
NgZone,
TemplateRef,
} from '@angular/core';
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
import { Components } from '@ionic/core';
export declare interface IonPopover extends Components.IonPopover {
/**
* Emitted after the popover has presented.
*/
ionPopoverDidPresent: EventEmitter<CustomEvent>;
/**
* Emitted before the popover has presented.
*/
ionPopoverWillPresent: EventEmitter<CustomEvent>;
/**
* Emitted after the popover has dismissed.
*/
ionPopoverWillDismiss: EventEmitter<CustomEvent>;
/**
* Emitted after the popover has dismissed.
*/
ionPopoverDidDismiss: EventEmitter<CustomEvent>;
/**
* Emitted after the popover has presented. Shorthand for ionPopoverWillDismiss.
*/
didPresent: EventEmitter<CustomEvent>;
/**
* Emitted before the popover has presented. Shorthand for ionPopoverWillPresent.
*/
willPresent: EventEmitter<CustomEvent>;
/**
* Emitted after the popover has presented. Shorthand for ionPopoverWillDismiss.
*/
willDismiss: EventEmitter<CustomEvent>;
/**
* Emitted after the popover has dismissed. Shorthand for ionPopoverDidDismiss.
*/
didDismiss: EventEmitter<CustomEvent>;
}
@ProxyCmp({
inputs: [
'alignment',
'animated',
'arrow',
'backdropDismiss',
'cssClass',
'dismissOnSelect',
'enterAnimation',
'event',
'isOpen',
'keyboardClose',
'leaveAnimation',
'mode',
'showBackdrop',
'translucent',
'trigger',
'triggerAction',
'reference',
'size',
'side',
],
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss'],
})
@Component({
selector: 'ion-popover',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<ng-container [ngTemplateOutlet]="template" *ngIf="isCmpOpen"></ng-container>`,
inputs: [
'alignment',
'animated',
'arrow',
'backdropDismiss',
'cssClass',
'dismissOnSelect',
'enterAnimation',
'event',
'isOpen',
'keyboardClose',
'leaveAnimation',
'mode',
'showBackdrop',
'translucent',
'trigger',
'triggerAction',
'reference',
'size',
'side',
],
})
export class IonPopover {
@ContentChild(TemplateRef, { static: false }) template: TemplateRef<any>;
isCmpOpen: boolean = false;
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
this.el = r.nativeElement;
this.el.addEventListener('willPresent', () => {
this.isCmpOpen = true;
c.detectChanges();
});
this.el.addEventListener('didDismiss', () => {
this.isCmpOpen = false;
c.detectChanges();
});
proxyOutputs(this, this.el, [
'ionPopoverDidPresent',
'ionPopoverWillPresent',
'ionPopoverWillDismiss',
'ionPopoverDidDismiss',
'didPresent',
'willPresent',
'willDismiss',
'didDismiss',
]);
}
}

View File

@@ -1,16 +1,12 @@
import * as d from './proxies';
import type * as d from './proxies';
export const DIRECTIVES = [
d.IonAccordion,
d.IonAccordionGroup,
d.IonApp,
d.IonAvatar,
d.IonBackButton,
d.IonBackdrop,
d.IonBadge,
d.IonBreadcrumb,
d.IonBreadcrumbs,
d.IonButton,
d.IonButtons,
d.IonCard,

View File

@@ -0,0 +1,46 @@
/* eslint-disable */
/* tslint:disable */
import { fromEvent } from 'rxjs';
export const proxyInputs = (Cmp: any, inputs: string[]) => {
const Prototype = Cmp.prototype;
inputs.forEach(item => {
Object.defineProperty(Prototype, item, {
get() {
return this.el[item];
},
set(val: any) {
this.z.runOutsideAngular(() => (this.el[item] = val));
}
});
});
};
export const proxyMethods = (Cmp: any, methods: string[]) => {
const Prototype = Cmp.prototype;
methods.forEach(methodName => {
Prototype[methodName] = function () {
const args = arguments;
return this.z.runOutsideAngular(() =>
this.el[methodName].apply(this.el, args)
);
};
});
};
export const proxyOutputs = (instance: any, el: any, events: string[]) => {
events.forEach(eventName => instance[eventName] = fromEvent(el, eventName));
}
export function ProxyCmp(opts: { inputs?: any; methods?: any }) {
const decorator = function(cls: any){
if (opts.inputs) {
proxyInputs(cls, opts.inputs);
}
if (opts.methods) {
proxyMethods(cls, opts.methods);
}
return cls;
};
return decorator;
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, EmbeddedViewRef, IterableDiffer, IterableDiffers, NgZone, SimpleChanges, TrackByFunction } from '@angular/core';
import { Cell, CellType, FooterHeightFn, HeaderFn, HeaderHeightFn, ItemHeightFn } from '@ionic/core';
import { ProxyCmp } from '../angular-component-lib/utils';
import { ProxyCmp } from '../proxies-utils';
import { VirtualFooter } from './virtual-footer';
import { VirtualHeader } from './virtual-header';
@@ -205,8 +205,8 @@ export class IonVirtualScroll {
case 'item': return this.itmTmp.templateRef;
case 'header': return this.hdrTmp.templateRef;
case 'footer': return this.ftrTmp.templateRef;
default: throw new Error('template for virtual item was not provided');
}
throw new Error('template for virtual item was not provided');
}
}

View File

@@ -1,21 +1,19 @@
// DIRECTIVES
export { BooleanValueAccessorDirective as BooleanValueAccessor } from './directives/control-value-accessors/boolean-value-accessor';
export { NumericValueAccessorDirective as NumericValueAccessor } from './directives/control-value-accessors/numeric-value-accesssor';
export { RadioValueAccessorDirective as RadioValueAccessor } from './directives/control-value-accessors/radio-value-accessor';
export { SelectValueAccessorDirective as SelectValueAccessor } from './directives/control-value-accessors/select-value-accessor';
export { TextValueAccessorDirective as TextValueAccessor } from './directives/control-value-accessors/text-value-accessor';
export { BooleanValueAccessor } from './directives/control-value-accessors/boolean-value-accessor';
export { NumericValueAccessor } from './directives/control-value-accessors/numeric-value-accesssor';
export { RadioValueAccessor } from './directives/control-value-accessors/radio-value-accessor';
export { SelectValueAccessor } from './directives/control-value-accessors/select-value-accessor';
export { TextValueAccessor } from './directives/control-value-accessors/text-value-accessor';
export { IonTabs } from './directives/navigation/ion-tabs';
export { IonBackButtonDelegateDirective as IonBackButtonDelegate } from './directives/navigation/ion-back-button';
export { IonBackButtonDelegate } from './directives/navigation/ion-back-button';
export { NavDelegate } from './directives/navigation/nav-delegate';
export { IonRouterOutlet } from './directives/navigation/ion-router-outlet';
export { RouterLinkDelegateDirective as RouterLinkDelegate } from './directives/navigation/router-link-delegate';
export { RouterLinkDelegate } from './directives/navigation/router-link-delegate';
export { NavParams } from './directives/navigation/nav-params';
export { IonVirtualScroll } from './directives/virtual-scroll/virtual-scroll';
export { VirtualItem } from './directives/virtual-scroll/virtual-item';
export { VirtualHeader } from './directives/virtual-scroll/virtual-header';
export { VirtualFooter } from './directives/virtual-scroll/virtual-footer';
export { IonModal } from './directives/overlays/modal';
export { IonPopover } from './directives/overlays/popover';
export * from './directives/proxies';
// PROVIDERS
@@ -44,18 +42,11 @@ export * from './types/ionic-lifecycle-hooks';
// PACKAGE MODULE
export { IonicModule } from './ionic-module';
// UTILS
export { IonicSafeString, getPlatforms, isPlatform, createAnimation, IonicSwiper, IonicSlides } from '@ionic/core';
// CORE TYPES
export {
// UTILS
createAnimation,
createGesture,
iosTransitionAnimation,
mdTransitionAnimation,
IonicSwiper,
IonicSlides,
getPlatforms,
isPlatform,
getTimeGivenProgression,
// TYPES
Animation,
AnimationBuilder,
AnimationCallbackOptions,
@@ -66,62 +57,33 @@ export {
Gesture,
GestureConfig,
GestureDetail,
mdTransitionAnimation,
iosTransitionAnimation,
NavComponentWithProps,
SpinnerTypes,
AccordionGroupCustomEvent,
AccordionGroupChangeEventDetail,
BreadcrumbCustomEvent,
BreadcrumbCollapsedClickEventDetail,
ActionSheetOptions,
ActionSheetButton,
AlertOptions,
AlertInput,
AlertTextareaAttributes,
AlertInputAttributes,
AlertButton,
BackButtonEvent,
CheckboxCustomEvent,
CheckboxChangeEventDetail,
DatetimeCustomEvent,
DatetimeChangeEventDetail,
InfiniteScrollCustomEvent,
InputCustomEvent,
InputChangeEventDetail,
ItemReorderEventDetail,
ItemReorderCustomEvent,
ItemSlidingCustomEvent,
IonicSafeString,
LoadingOptions,
MenuCustomEvent,
ModalOptions,
NavCustomEvent,
PickerOptions,
PickerButton,
PickerColumn,
PickerColumnOption,
PlatformConfig,
PopoverOptions,
RadioGroupCustomEvent,
RadioGroupChangeEventDetail,
RefresherCustomEvent,
RefresherEventDetail,
RouterEventDetail,
RouterCustomEvent,
ScrollBaseCustomEvent,
ScrollBaseDetail,
ScrollDetail,
ScrollCustomEvent,
SearchbarCustomEvent,
SearchbarChangeEventDetail,
SegmentChangeEventDetail,
SegmentCustomEvent,
SelectChangeEventDetail,
SelectCustomEvent,
TabsCustomEvent,
TextareaChangeEventDetail,
TextareaCustomEvent,
ToastOptions,
ToastButton,
ToggleChangeEventDetail,
ToggleCustomEvent,
ToastButton
} from '@ionic/core';

View File

@@ -1,21 +0,0 @@
// Re-exports from ionic/core
// UTILS
export { IonicSafeString, getPlatforms, isPlatform, createAnimation } from '@ionic/core';
// CORE TYPES
export {
Animation,
AnimationBuilder,
AnimationCallbackOptions,
AnimationDirection,
AnimationFill,
AnimationKeyFrames,
AnimationLifecycle,
Gesture,
GestureConfig,
GestureDetail,
mdTransitionAnimation,
iosTransitionAnimation,
NavComponentWithProps,
} from '@ionic/core';

View File

@@ -1,97 +1,19 @@
import { CommonModule, DOCUMENT } from '@angular/common';
import { ModuleWithProviders, APP_INITIALIZER, NgModule, NgZone } from '@angular/core';
import { APP_INITIALIZER, ModuleWithProviders, NgModule, NgZone } from '@angular/core';
import { IonicConfig } from '@ionic/core';
import { appInitialize } from './app-initialize';
import { BooleanValueAccessorDirective } from './directives/control-value-accessors/boolean-value-accessor';
import { NumericValueAccessorDirective } from './directives/control-value-accessors/numeric-value-accesssor';
import { RadioValueAccessorDirective } from './directives/control-value-accessors/radio-value-accessor';
import { SelectValueAccessorDirective } from './directives/control-value-accessors/select-value-accessor';
import { TextValueAccessorDirective } from './directives/control-value-accessors/text-value-accessor';
import { IonBackButtonDelegateDirective } from './directives/navigation/ion-back-button';
import { BooleanValueAccessor } from './directives/control-value-accessors/boolean-value-accessor';
import { NumericValueAccessor } from './directives/control-value-accessors/numeric-value-accesssor';
import { RadioValueAccessor } from './directives/control-value-accessors/radio-value-accessor';
import { SelectValueAccessor } from './directives/control-value-accessors/select-value-accessor';
import { TextValueAccessor } from './directives/control-value-accessors/text-value-accessor';
import { IonBackButtonDelegate } from './directives/navigation/ion-back-button';
import { IonRouterOutlet } from './directives/navigation/ion-router-outlet';
import { IonTabs } from './directives/navigation/ion-tabs';
import { NavDelegate } from './directives/navigation/nav-delegate';
import { RouterLinkDelegateDirective } from './directives/navigation/router-link-delegate';
import { IonModal } from './directives/overlays/modal';
import { IonPopover } from './directives/overlays/popover';
import {
IonAccordion,
IonAccordionGroup,
IonApp,
IonAvatar,
IonBackButton,
IonBackdrop,
IonBadge,
IonBreadcrumb,
IonBreadcrumbs,
IonButton,
IonButtons,
IonCard,
IonCardContent,
IonCardHeader,
IonCardSubtitle,
IonCardTitle,
IonCheckbox,
IonChip,
IonCol,
IonContent,
IonDatetime,
IonFab,
IonFabButton,
IonFabList,
IonFooter,
IonGrid,
IonHeader,
IonIcon,
IonImg,
IonInfiniteScroll,
IonInfiniteScrollContent,
IonInput,
IonItem,
IonItemDivider,
IonItemGroup,
IonItemOption,
IonItemOptions,
IonItemSliding,
IonLabel,
IonList,
IonListHeader,
IonMenu,
IonMenuButton,
IonMenuToggle,
IonNav,
IonNavLink,
IonNote,
IonProgressBar,
IonRadio,
IonRadioGroup,
IonRange,
IonRefresher,
IonRefresherContent,
IonReorder,
IonReorderGroup,
IonRippleEffect,
IonRow,
IonSearchbar,
IonSegment,
IonSegmentButton,
IonSelect,
IonSelectOption,
IonSkeletonText,
IonSlide,
IonSlides,
IonSpinner,
IonSplitPane,
IonTabBar,
IonTabButton,
IonText,
IonTextarea,
IonThumbnail,
IonTitle,
IonToggle,
IonToolbar,
} from './directives/proxies';
import { RouterLinkDelegate } from './directives/navigation/router-link-delegate';
import { IonApp, IonAvatar, IonBackButton, IonBackdrop, IonBadge, IonButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonCheckbox, IonChip, IonCol, IonContent, IonDatetime, IonFab, IonFabButton, IonFabList, IonFooter, IonGrid, IonHeader, IonIcon, IonImg, IonInfiniteScroll, IonInfiniteScrollContent, IonInput, IonItem, IonItemDivider, IonItemGroup, IonItemOption, IonItemOptions, IonItemSliding, IonLabel, IonList, IonListHeader, IonMenu, IonMenuButton, IonMenuToggle, IonNav, IonNavLink, IonNote, IonProgressBar, IonRadio, IonRadioGroup, IonRange, IonRefresher, IonRefresherContent, IonReorder, IonReorderGroup, IonRippleEffect, IonRow, IonSearchbar, IonSegment, IonSegmentButton, IonSelect, IonSelectOption, IonSkeletonText, IonSlide, IonSlides, IonSpinner, IonSplitPane, IonTabBar, IonTabButton, IonText, IonTextarea, IonThumbnail, IonTitle, IonToggle, IonToolbar } from './directives/proxies';
import { VirtualFooter } from './directives/virtual-scroll/virtual-footer';
import { VirtualHeader } from './directives/virtual-scroll/virtual-header';
import { VirtualItem } from './directives/virtual-scroll/virtual-item';
@@ -103,15 +25,11 @@ import { PopoverController } from './providers/popover-controller';
const DECLARATIONS = [
// proxies
IonAccordion,
IonAccordionGroup,
IonApp,
IonAvatar,
IonBackButton,
IonBackdrop,
IonBadge,
IonBreadcrumb,
IonBreadcrumbs,
IonButton,
IonButtons,
IonCard,
@@ -147,11 +65,9 @@ const DECLARATIONS = [
IonMenu,
IonMenuButton,
IonMenuToggle,
IonModal,
IonNav,
IonNavLink,
IonNote,
IonPopover,
IonProgressBar,
IonRadio,
IonRadioGroup,
@@ -184,30 +100,30 @@ const DECLARATIONS = [
IonTabs,
// ngModel accessors
BooleanValueAccessorDirective,
NumericValueAccessorDirective,
RadioValueAccessorDirective,
SelectValueAccessorDirective,
TextValueAccessorDirective,
BooleanValueAccessor,
NumericValueAccessor,
RadioValueAccessor,
SelectValueAccessor,
TextValueAccessor,
// navigation
IonRouterOutlet,
IonBackButtonDelegateDirective,
IonBackButtonDelegate,
NavDelegate,
RouterLinkDelegateDirective,
RouterLinkDelegate,
// virtual scroll
VirtualFooter,
VirtualHeader,
VirtualItem,
IonVirtualScroll,
IonVirtualScroll
];
@NgModule({
declarations: DECLARATIONS,
exports: DECLARATIONS,
providers: [AngularDelegate, ModalController, PopoverController],
imports: [CommonModule],
imports: [CommonModule]
})
export class IonicModule {
static forRoot(config?: IonicConfig): ModuleWithProviders<IonicModule> {
@@ -216,15 +132,19 @@ export class IonicModule {
providers: [
{
provide: ConfigToken,
useValue: config,
useValue: config
},
{
provide: APP_INITIALIZER,
useFactory: appInitialize,
multi: true,
deps: [ConfigToken, DOCUMENT, NgZone],
},
],
deps: [
ConfigToken,
DOCUMENT,
NgZone
]
}
]
};
}
}

View File

@@ -1,37 +1,27 @@
import {
ApplicationRef,
ComponentFactoryResolver,
NgZone,
ViewContainerRef,
Injectable,
InjectionToken,
Injector,
} from '@angular/core';
import {
FrameworkDelegate,
LIFECYCLE_DID_ENTER,
LIFECYCLE_DID_LEAVE,
LIFECYCLE_WILL_ENTER,
LIFECYCLE_WILL_LEAVE,
LIFECYCLE_WILL_UNLOAD,
} from '@ionic/core';
import { ApplicationRef, ComponentFactoryResolver, Injectable, InjectionToken, Injector, NgZone, ViewContainerRef } from '@angular/core';
import { FrameworkDelegate, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE, LIFECYCLE_WILL_UNLOAD } from '@ionic/core';
import { NavParams } from '../directives/navigation/nav-params';
@Injectable()
export class AngularDelegate {
constructor(private zone: NgZone, private appRef: ApplicationRef) {}
constructor(
private zone: NgZone,
private appRef: ApplicationRef
) {}
create(
resolver: ComponentFactoryResolver,
injector: Injector,
location?: ViewContainerRef
): AngularFrameworkDelegate {
location?: ViewContainerRef,
) {
return new AngularFrameworkDelegate(resolver, injector, location, this.appRef, this.zone);
}
}
export class AngularFrameworkDelegate implements FrameworkDelegate {
private elRefMap = new WeakMap<HTMLElement, any>();
private elEventsMap = new WeakMap<HTMLElement, () => void>();
@@ -40,24 +30,16 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
private injector: Injector,
private location: ViewContainerRef | undefined,
private appRef: ApplicationRef,
private zone: NgZone
private zone: NgZone,
) {}
attachViewToDom(container: any, component: any, params?: any, cssClasses?: string[]): Promise<any> {
return this.zone.run(() => {
return new Promise((resolve) => {
return new Promise(resolve => {
const el = attachView(
this.zone,
this.resolver,
this.injector,
this.location,
this.appRef,
this.elRefMap,
this.elEventsMap,
container,
component,
params,
cssClasses
this.zone, this.resolver, this.injector, this.location, this.appRef,
this.elRefMap, this.elEventsMap,
container, component, params, cssClasses
);
resolve(el);
});
@@ -66,7 +48,7 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
removeViewFromDom(_container: any, component: any): Promise<void> {
return this.zone.run(() => {
return new Promise((resolve) => {
return new Promise(resolve => {
const componentRef = this.elRefMap.get(component);
if (componentRef) {
componentRef.destroy();
@@ -91,17 +73,14 @@ export const attachView = (
appRef: ApplicationRef,
elRefMap: WeakMap<HTMLElement, any>,
elEventsMap: WeakMap<HTMLElement, () => void>,
container: any,
component: any,
params: any,
cssClasses: string[] | undefined
): any => {
container: any, component: any, params: any, cssClasses: string[] | undefined
) => {
const factory = resolver.resolveComponentFactory(component);
const childInjector = Injector.create({
providers: getProviders(params),
parent: injector,
parent: injector
});
const componentRef = location
const componentRef = (location)
? location.createComponent(factory, location.length, childInjector)
: factory.create(childInjector);
@@ -132,36 +111,35 @@ const LIFECYCLES = [
LIFECYCLE_DID_ENTER,
LIFECYCLE_WILL_LEAVE,
LIFECYCLE_DID_LEAVE,
LIFECYCLE_WILL_UNLOAD,
LIFECYCLE_WILL_UNLOAD
];
export const bindLifecycleEvents = (zone: NgZone, instance: any, element: HTMLElement): (() => void) => {
export const bindLifecycleEvents = (zone: NgZone, instance: any, element: HTMLElement) => {
return zone.run(() => {
const unregisters = LIFECYCLES.filter((eventName) => typeof instance[eventName] === 'function').map((eventName) => {
const handler = (ev: any) => instance[eventName](ev.detail);
element.addEventListener(eventName, handler);
return () => element.removeEventListener(eventName, handler);
});
return () => unregisters.forEach((fn) => fn());
const unregisters = LIFECYCLES
.filter(eventName => typeof instance[eventName] === 'function')
.map(eventName => {
const handler = (ev: any) => instance[eventName](ev.detail);
element.addEventListener(eventName, handler);
return () => element.removeEventListener(eventName, handler);
});
return () => unregisters.forEach(fn => fn());
});
};
const NavParamsToken = new InjectionToken<any>('NavParamsToken');
const getProviders = (params: { [key: string]: any }) => {
const getProviders = (params: {[key: string]: any}) => {
return [
{
provide: NavParamsToken,
useValue: params,
provide: NavParamsToken, useValue: params
},
{
provide: NavParams,
useFactory: provideNavParamsInjectable,
deps: [NavParamsToken],
},
provide: NavParams, useFactory: provideNavParamsInjectable, deps: [NavParamsToken]
}
];
};
const provideNavParamsInjectable = (params: { [key: string]: any }) => {
const provideNavParamsInjectable = (params: {[key: string]: any}) => {
return new NavParams(params);
};

View File

@@ -4,9 +4,10 @@ import { Config as CoreConfig, IonicConfig } from '@ionic/core';
import { IonicWindow } from '../types/interfaces';
@Injectable({
providedIn: 'root',
providedIn: 'root'
})
export class Config {
get(key: keyof IonicConfig, fallback?: any): any {
const c = getConfig();
if (c) {
@@ -30,6 +31,14 @@ export class Config {
}
return 0;
}
set(key: keyof IonicConfig, value?: any) {
console.warn(`[DEPRECATION][Config]: The Config.set() method is deprecated and will be removed in Ionic Framework 6.0. Please see https://ionicframework.com/docs/angular/config for alternatives.`);
const c = getConfig();
if (c) {
c.set(key, value);
}
}
}
export const ConfigToken = new InjectionToken<any>('USERCONFIG');
@@ -37,7 +46,7 @@ export const ConfigToken = new InjectionToken<any>('USERCONFIG');
const getConfig = (): CoreConfig | null => {
if (typeof (window as any) !== 'undefined') {
const Ionic = (window as any as IonicWindow).Ionic;
if (Ionic?.config) {
if (Ionic && Ionic.config) {
return Ionic.config;
}
}

View File

@@ -4,11 +4,12 @@ import { Injectable } from '@angular/core';
providedIn: 'root',
})
export class DomController {
/**
* Schedules a task to run during the READ phase of the next frame.
* This task should only read the DOM, but never modify it.
*/
read(cb: RafCallback): void {
read(cb: RafCallback) {
getQueue().read(cb);
}
@@ -16,29 +17,29 @@ export class DomController {
* Schedules a task to run during the WRITE phase of the next frame.
* This task should write the DOM, but never READ it.
*/
write(cb: RafCallback): void {
write(cb: RafCallback) {
getQueue().write(cb);
}
}
const getQueue = () => {
const win = typeof (window as any) !== 'undefined' ? window : (null as any);
const win = typeof (window as any) !== 'undefined' ? window : null as any;
if (win != null) {
const Ionic = win.Ionic;
if (Ionic?.queue) {
if (Ionic && Ionic.queue) {
return Ionic.queue;
}
return {
read: (cb: any) => win.requestAnimationFrame(cb),
write: (cb: any) => win.requestAnimationFrame(cb),
write: (cb: any) => win.requestAnimationFrame(cb)
};
}
return {
read: (cb: any) => cb(),
write: (cb: any) => cb(),
write: (cb: any) => cb()
};
};

View File

@@ -1,4 +1,4 @@
import { NgZone, Injectable } from '@angular/core';
import { Injectable, NgZone } from '@angular/core';
import { Gesture, GestureConfig, createGesture } from '@ionic/core';
@Injectable({
@@ -11,10 +11,10 @@ export class GestureController {
*/
create(opts: GestureConfig, runInsideAngularZone = false): Gesture {
if (runInsideAngularZone) {
Object.getOwnPropertyNames(opts).forEach((key) => {
Object.getOwnPropertyNames(opts).forEach(key => {
if (typeof opts[key] === 'function') {
const fn = opts[key];
opts[key] = (...props: any[]) => this.zone.run(() => fn(...props));
opts[key] = (...props) => this.zone.run(() => fn(...props));
}
});
}

View File

@@ -5,12 +5,13 @@ import { menuController } from '@ionic/core';
providedIn: 'root',
})
export class MenuController {
/**
* Programmatically open the Menu.
* @param [menuId] Optionally get the menu by its id, or side.
* @return returns a promise when the menu is fully opened
*/
open(menuId?: string): Promise<boolean> {
open(menuId?: string) {
return menuController.open(menuId);
}
@@ -21,7 +22,7 @@ export class MenuController {
* @param [menuId] Optionally get the menu by its id, or side.
* @return returns a promise when the menu is fully closed
*/
close(menuId?: string): Promise<boolean> {
close(menuId?: string) {
return menuController.close(menuId);
}
@@ -31,7 +32,7 @@ export class MenuController {
* @param [menuId] Optionally get the menu by its id, or side.
* @return returns a promise when the menu has been toggled
*/
toggle(menuId?: string): Promise<boolean> {
toggle(menuId?: string) {
return menuController.toggle(menuId);
}
@@ -43,7 +44,7 @@ export class MenuController {
* @param [menuId] Optionally get the menu by its id, or side.
* @return Returns the instance of the menu, which is useful for chaining.
*/
enable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement | undefined> {
enable(shouldEnable: boolean, menuId?: string) {
return menuController.enable(shouldEnable, menuId);
}
@@ -53,7 +54,7 @@ export class MenuController {
* @param [menuId] Optionally get the menu by its id, or side.
* @return Returns the instance of the menu, which is useful for chaining.
*/
swipeGesture(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement | undefined> {
swipeGesture(shouldEnable: boolean, menuId?: string) {
return menuController.swipeGesture(shouldEnable, menuId);
}
@@ -62,7 +63,7 @@ export class MenuController {
* @return Returns true if the specified menu is currently open, otherwise false.
* If the menuId is not specified, it returns true if ANY menu is currenly open.
*/
isOpen(menuId?: string): Promise<boolean> {
isOpen(menuId?: string) {
return menuController.isOpen(menuId);
}
@@ -70,7 +71,7 @@ export class MenuController {
* @param [menuId] Optionally get the menu by its id, or side.
* @return Returns true if the menu is currently enabled, otherwise false.
*/
isEnabled(menuId?: string): Promise<boolean> {
isEnabled(menuId?: string) {
return menuController.isEnabled(menuId);
}
@@ -83,21 +84,21 @@ export class MenuController {
* @param [menuId] Optionally get the menu by its id, or side.
* @return Returns the instance of the menu if found, otherwise `null`.
*/
get(menuId?: string): Promise<HTMLIonMenuElement | undefined> {
get(menuId?: string) {
return menuController.get(menuId);
}
/**
* @return Returns the instance of the menu already opened, otherwise `null`.
*/
getOpen(): Promise<HTMLIonMenuElement | undefined> {
getOpen() {
return menuController.getOpen();
}
/**
* @return Returns an array of all menu instances.
*/
getMenus(): Promise<HTMLIonMenuElement[]> {
getMenus() {
return menuController.getMenus();
}
}

View File

@@ -1,4 +1,4 @@
import { ComponentFactoryResolver, Injector, Injectable } from '@angular/core';
import { ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
import { ModalOptions, modalController } from '@ionic/core';
import { OverlayBaseController } from '../util/overlay';
@@ -7,10 +7,11 @@ import { AngularDelegate } from './angular-delegate';
@Injectable()
export class ModalController extends OverlayBaseController<ModalOptions, HTMLIonModalElement> {
constructor(
private angularDelegate: AngularDelegate,
private resolver: ComponentFactoryResolver,
private injector: Injector
private injector: Injector,
) {
super(modalController);
}
@@ -18,7 +19,7 @@ export class ModalController extends OverlayBaseController<ModalOptions, HTMLIon
create(opts: ModalOptions): Promise<HTMLIonModalElement> {
return super.create({
...opts,
delegate: this.angularDelegate.create(this.resolver, this.injector),
delegate: this.angularDelegate.create(this.resolver, this.injector)
});
}
}

View File

@@ -1,6 +1,6 @@
import { Location } from '@angular/common';
import { Injectable, Optional } from '@angular/core';
import { NavigationExtras, Router, UrlSerializer, UrlTree, NavigationStart } from '@angular/router';
import { NavigationExtras, NavigationStart, Router, UrlSerializer, UrlTree } from '@angular/router';
import { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core';
import { IonRouterOutlet } from '../directives/navigation/ion-router-outlet';
@@ -19,6 +19,7 @@ export interface NavigationOptions extends NavigationExtras, AnimationOptions {}
providedIn: 'root',
})
export class NavController {
private topOutlet?: IonRouterOutlet;
private direction: 'forward' | 'back' | 'root' | 'auto' = DEFAULT_DIRECTION;
private animated?: NavDirection = DEFAULT_ANIMATED;
@@ -31,13 +32,13 @@ export class NavController {
platform: Platform,
private location: Location,
private serializer: UrlSerializer,
@Optional() private router?: Router
@Optional() private router?: Router,
) {
// Subscribe to router events to detect direction
if (router) {
router.events.subscribe((ev) => {
router.events.subscribe(ev => {
if (ev instanceof NavigationStart) {
const id = ev.restoredState ? ev.restoredState.navigationId : ev.id;
const id = (ev.restoredState) ? ev.restoredState.navigationId : ev.id;
this.guessDirection = id < this.lastNavId ? 'back' : 'forward';
this.guessAnimation = !ev.restoredState ? this.guessDirection : undefined;
this.lastNavId = this.guessDirection === 'forward' ? ev.id : id;
@@ -46,7 +47,7 @@ export class NavController {
}
// Subscribe to backButton events
platform.backButton.subscribeWithPriority(0, (processNextHandler) => {
platform.backButton.subscribeWithPriority(0, processNextHandler => {
this.pop();
processNextHandler();
});
@@ -121,7 +122,7 @@ export class NavController {
* It will use the standard `window.history.back()` under the hood, but featuring a `back` animation
* by default.
*/
back(options: AnimationOptions = { animated: true, animationDirection: 'back' }): void {
back(options: AnimationOptions = { animated: true, animationDirection: 'back' }) {
this.setDirection('back', options.animated, options.animationDirection, options.animation);
return this.location.back();
}
@@ -132,7 +133,7 @@ export class NavController {
* It recursively finds the top active `ion-router-outlet` and calls `pop()`.
* This is the recommended way to go back when you are using `ion-router-outlet`.
*/
async pop(): Promise<void> {
async pop() {
let outlet = this.topOutlet;
while (outlet) {
@@ -151,12 +152,7 @@ export class NavController {
*
* It's recommended to use `navigateForward()`, `navigateBack()` and `navigateRoot()` instead of `setDirection()`.
*/
setDirection(
direction: RouterDirection,
animated?: boolean,
animationDirection?: 'forward' | 'back',
animationBuilder?: AnimationBuilder
): void {
setDirection(direction: RouterDirection, animated?: boolean, animationDirection?: 'forward' | 'back', animationBuilder?: AnimationBuilder) {
this.direction = direction;
this.animated = getAnimation(direction, animated, animationDirection);
this.animationBuilder = animationBuilder;
@@ -165,18 +161,14 @@ export class NavController {
/**
* @internal
*/
setTopOutlet(outlet: IonRouterOutlet): void {
setTopOutlet(outlet: IonRouterOutlet) {
this.topOutlet = outlet;
}
/**
* @internal
*/
consumeTransition(): {
direction: RouterDirection;
animation: NavDirection | undefined;
animationBuilder: AnimationBuilder | undefined;
} {
consumeTransition() {
let direction: RouterDirection = 'root';
let animation: NavDirection | undefined;
const animationBuilder = this.animationBuilder;
@@ -195,15 +187,15 @@ export class NavController {
return {
direction,
animation,
animationBuilder,
animationBuilder
};
}
private navigate(url: string | UrlTree | any[], options: NavigationOptions) {
if (Array.isArray(url)) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.router!.navigate(url, options);
} else {
/**
* navigateByUrl ignores any properties that
* would change the url, so things like queryParams
@@ -225,17 +217,12 @@ export class NavController {
* that do not modify the url, such as `replaceUrl` which is why
* `options` is passed in here.
*/
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.router!.navigateByUrl(urlTree, options);
}
}
}
const getAnimation = (
direction: RouterDirection,
animated: boolean | undefined,
animationDirection: 'forward' | 'back' | undefined
): NavDirection | undefined => {
const getAnimation = (direction: RouterDirection, animated: boolean | undefined, animationDirection: 'forward' | 'back' | undefined): NavDirection | undefined => {
if (animated === false) {
return undefined;
}

View File

@@ -1,19 +1,17 @@
import { DOCUMENT } from '@angular/common';
import { NgZone, Inject, Injectable } from '@angular/core';
import { Inject, Injectable, NgZone } from '@angular/core';
import { BackButtonEventDetail, KeyboardEventDetail, Platforms, getPlatforms, isPlatform } from '@ionic/core';
import { Subscription, Subject } from 'rxjs';
import { Subject, Subscription } from 'rxjs';
export interface BackButtonEmitter extends Subject<BackButtonEventDetail> {
subscribeWithPriority(
priority: number,
callback: (processNextHandler: () => void) => Promise<any> | void
): Subscription;
subscribeWithPriority(priority: number, callback: (processNextHandler: () => void) => Promise<any> | void): Subscription;
}
@Injectable({
providedIn: 'root',
})
export class Platform {
private _readyPromise: Promise<string>;
private win: any;
@@ -59,9 +57,9 @@ export class Platform {
constructor(@Inject(DOCUMENT) private doc: any, zone: NgZone) {
zone.run(() => {
this.win = doc.defaultView;
this.backButton.subscribeWithPriority = function (priority, callback) {
return this.subscribe((ev) => {
return ev.register(priority, (processNextHandler) => zone.run(() => callback(processNextHandler)));
this.backButton.subscribeWithPriority = function(priority, callback) {
return this.subscribe(ev => {
return ev.register(priority, processNextHandler => zone.run(() => callback(processNextHandler)));
});
};
@@ -73,19 +71,12 @@ export class Platform {
proxyEvent(this.keyboardDidHide, this.win, 'ionKeyboardDidHide');
let readyResolve: (value: string) => void;
this._readyPromise = new Promise((res) => {
readyResolve = res;
});
if (this.win?.['cordova']) {
doc.addEventListener(
'deviceready',
() => {
readyResolve('cordova');
},
{ once: true }
);
this._readyPromise = new Promise(res => { readyResolve = res; });
if (this.win && this.win['cordova']) {
doc.addEventListener('deviceready', () => {
readyResolve('cordova');
}, { once: true });
} else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
readyResolve!('dom');
}
});
@@ -222,25 +213,25 @@ export class Platform {
* Returns `true` if the app is in portrait mode.
*/
isPortrait(): boolean {
return this.win.matchMedia?.('(orientation: portrait)').matches;
return this.win.matchMedia && this.win.matchMedia('(orientation: portrait)').matches;
}
testUserAgent(expression: string): boolean {
const nav = this.win.navigator;
return !!(nav?.userAgent && nav.userAgent.indexOf(expression) >= 0);
return !!(nav && nav.userAgent && nav.userAgent.indexOf(expression) >= 0);
}
/**
* Get the current url.
*/
url(): string {
url() {
return this.win.location.href;
}
/**
* Gets the width of the platform's viewport using `window.innerWidth`.
*/
width(): number {
width() {
return this.win.innerWidth;
}
@@ -253,17 +244,17 @@ export class Platform {
}
const readQueryParam = (url: string, key: string) => {
key = key.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
key = key.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
const regex = new RegExp('[\\?&]' + key + '=([^&#]*)');
const results = regex.exec(url);
return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null;
};
const proxyEvent = <T>(emitter: Subject<T>, el: EventTarget, eventName: string) => {
if (el as any) {
if ((el as any)) {
el.addEventListener(eventName, (ev: Event | undefined | null) => {
// ?? cordova might emit "null" events
emitter.next(ev != null ? ((ev as any).detail as T) : undefined);
emitter.next(ev != null ? (ev as any).detail as T : undefined);
});
}
};

View File

@@ -1,4 +1,4 @@
import { ComponentFactoryResolver, Injector, Injectable } from '@angular/core';
import { ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
import { PopoverOptions, popoverController } from '@ionic/core';
import { OverlayBaseController } from '../util/overlay';
@@ -7,10 +7,11 @@ import { AngularDelegate } from './angular-delegate';
@Injectable()
export class PopoverController extends OverlayBaseController<PopoverOptions, HTMLIonPopoverElement> {
constructor(
private angularDelegate: AngularDelegate,
private resolver: ComponentFactoryResolver,
private injector: Injector
private injector: Injector,
) {
super(popoverController);
}
@@ -18,7 +19,7 @@ export class PopoverController extends OverlayBaseController<PopoverOptions, HTM
create(opts: PopoverOptions): Promise<HTMLIonPopoverElement> {
return super.create({
...opts,
delegate: this.angularDelegate.create(this.resolver, this.injector),
delegate: this.angularDelegate.create(this.resolver, this.injector)
});
}
}

View File

@@ -1,21 +1,8 @@
import { Path, join } from '@angular-devkit/core';
import {
Rule,
SchematicContext,
Tree,
apply,
chain,
mergeWith,
move,
SchematicsException,
template,
url,
} from '@angular-devkit/schematics';
import { join, Path } from '@angular-devkit/core';
import { apply, chain, mergeWith, move, Rule, SchematicContext, SchematicsException, template, Tree, url } from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { getWorkspace } from '@schematics/angular/utility/workspace';
import { addModuleImportToRootModule } from './../utils/ast';
import { addArchitectBuilder, addAsset, addStyle, getDefaultAngularAppName } from './../utils/config';
import { addArchitectBuilder, addAsset, addStyle, getDefaultAngularAppName, getWorkspace, WorkspaceProject, WorkspaceSchema } from './../utils/config';
import { addPackageToPackageJson } from './../utils/package';
import { Schema as IonAddOptions } from './schema';
@@ -28,14 +15,24 @@ function addIonicAngularToPackageJson(): Rule {
function addIonicAngularToolkitToPackageJson(): Rule {
return (host: Tree) => {
addPackageToPackageJson(host, 'devDependencies', '@ionic/angular-toolkit', 'latest');
addPackageToPackageJson(
host,
'devDependencies',
'@ionic/angular-toolkit',
'latest'
);
return host;
};
}
function addIonicAngularModuleToAppModule(projectSourceRoot: Path): Rule {
return (host: Tree) => {
addModuleImportToRootModule(host, projectSourceRoot, 'IonicModule.forRoot()', '@ionic/angular');
addModuleImportToRootModule(
host,
projectSourceRoot,
'IonicModule.forRoot()',
'@ionic/angular'
);
return host;
};
}
@@ -53,13 +50,13 @@ function addIonicStyles(projectName: string, projectSourceRoot: Path): Rule {
'node_modules/@ionic/angular/css/text-alignment.css',
'node_modules/@ionic/angular/css/text-transformation.css',
'node_modules/@ionic/angular/css/flex-utils.css',
`${projectSourceRoot}/theme/variables.css`,
];
`${projectSourceRoot}/theme/variables.css`
]
ionicStyles.forEach((entry) => {
ionicStyles.forEach(entry => {
addStyle(host, projectName, entry);
});
return host;
return host;
};
}
@@ -68,7 +65,7 @@ function addIonicons(projectName: string): Rule {
const ioniconsGlob = {
glob: '**/*.svg',
input: 'node_modules/ionicons/dist/ionicons/svg',
output: './svg',
output: './svg'
};
addAsset(host, projectName, 'build', ioniconsGlob);
addAsset(host, projectName, 'test', ioniconsGlob);
@@ -82,25 +79,25 @@ function addIonicBuilder(projectName: string): Rule {
builder: '@ionic/angular-toolkit:cordova-serve',
options: {
cordovaBuildTarget: `${projectName}:ionic-cordova-build`,
devServerTarget: `${projectName}:serve`,
devServerTarget: `${projectName}:serve`
},
configurations: {
production: {
cordovaBuildTarget: `${projectName}:ionic-cordova-build:production`,
devServerTarget: `${projectName}:serve:production`,
},
},
devServerTarget: `${projectName}:serve:production`
}
}
});
addArchitectBuilder(host, projectName, 'ionic-cordova-build', {
builder: '@ionic/angular-toolkit:cordova-build',
options: {
browserTarget: `${projectName}:build`,
browserTarget: `${projectName}:build`
},
configurations: {
production: {
browserTarget: `${projectName}:build:production`,
},
},
browserTarget: `${projectName}:build:production`
}
}
});
return host;
};
@@ -113,18 +110,22 @@ function installNodeDeps() {
}
export default function ngAdd(options: IonAddOptions): Rule {
return async (host: Tree) => {
const workspace = await getWorkspace(host);
return (host: Tree) => {
const workspace: WorkspaceSchema = getWorkspace(host);
if (!options.project) {
options.project = getDefaultAngularAppName(workspace);
}
const project = workspace.projects.get(options.project);
if (!project || project.extensions.projectType !== 'application') {
throw new SchematicsException(`Ionic Add requires a project type of "application".`);
const project: WorkspaceProject = workspace.projects[options.project];
if (project.projectType !== 'application') {
throw new SchematicsException(
`Ionic Add requires a project type of "application".`
);
}
const sourcePath: Path = join(project.sourceRoot as Path);
const rootTemplateSource = apply(url('./files/root'), [template({ ...options }), move(sourcePath)]);
const rootTemplateSource = apply(url('./files/root'), [
template({ ...options }),
move(sourcePath)
]);
return chain([
// @ionic/angular
addIonicAngularToPackageJson(),
@@ -135,7 +136,7 @@ export default function ngAdd(options: IonAddOptions): Rule {
addIonicons(options.project),
mergeWith(rootTemplateSource),
// install freshly added dependencies
installNodeDeps(),
installNodeDeps()
]);
};
}

View File

@@ -1,6 +1,6 @@
{
"$schema": "http://json-schema.org/schema",
"$id": "ionicNgAdd",
"id": "ionicNgAdd",
"title": "Ionic Add options",
"type": "object",
"properties": {

View File

@@ -1,7 +1,6 @@
import { SchematicsException, Tree } from '@angular-devkit/schematics';
import { normalize } from '@angular-devkit/core';
import { Tree, SchematicsException } from '@angular-devkit/schematics';
import * as ts from 'typescript';
import { addImportToModule } from './devkit-utils/ast-utils';
import { InsertChange } from './devkit-utils/change';
@@ -14,7 +13,12 @@ export function getSourceFile(host: Tree, path: string): ts.SourceFile {
throw new SchematicsException(`Could not find file for path: ${path}`);
}
const content = buffer.toString();
const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);
const source = ts.createSourceFile(
path,
content,
ts.ScriptTarget.Latest,
true
);
return source;
}
@@ -26,8 +30,13 @@ export function addModuleImportToRootModule(
projectSourceRoot: string,
moduleName: string,
importSrc: string
): void {
addModuleImportToModule(host, normalize(`${projectSourceRoot}/app/app.module.ts`), moduleName, importSrc);
) {
addModuleImportToModule(
host,
normalize(`${projectSourceRoot}/app/app.module.ts`),
moduleName,
importSrc
);
}
/**
@@ -37,12 +46,17 @@ export function addModuleImportToRootModule(
* @param moduleName name of module to import
* @param src src location to import
*/
export function addModuleImportToModule(host: Tree, modulePath: string, moduleName: string, src: string): void {
export function addModuleImportToModule(
host: Tree,
modulePath: string,
moduleName: string,
src: string
) {
const moduleSource = getSourceFile(host, modulePath);
const changes = addImportToModule(moduleSource, modulePath, moduleName, src);
const recorder = host.beginUpdate(modulePath);
changes.forEach((change) => {
changes.forEach(change => {
if (change instanceof InsertChange) {
recorder.insertLeft(change.pos, change.toAdd);
}

View File

@@ -1,19 +1,18 @@
import { WorkspaceDefinition } from '@angular-devkit/core/src/workspace';
import { Tree, SchematicsException } from '@angular-devkit/schematics';
import { parse } from 'jsonc-parser';
import { SchematicsException, Tree } from '@angular-devkit/schematics';
import { experimental, parseJson, JsonParseMode } from '@angular-devkit/core';
const CONFIG_PATH = 'angular.json';
export function readConfig(host: Tree): any {
const sourceText = host.read(CONFIG_PATH)?.toString('utf-8');
export function readConfig(host: Tree) {
const sourceText = host.read(CONFIG_PATH)!.toString('utf-8');
return JSON.parse(sourceText);
}
export function writeConfig(host: Tree, config: JSON): void {
export function writeConfig(host: Tree, config: JSON) {
host.overwrite(CONFIG_PATH, JSON.stringify(config, null, 2));
}
function isAngularBrowserProject(projectConfig: any): boolean {
function isAngularBrowserProject(projectConfig: any) {
if (projectConfig.projectType === 'application') {
const buildConfig = projectConfig.architect.build;
return buildConfig.builder === '@angular-devkit/build-angular:browser';
@@ -37,7 +36,6 @@ export function getDefaultAngularAppName(config: any): string {
}
export function getAngularAppConfig(config: any, projectName: string): any | never {
// eslint-disable-next-line no-prototype-builtins
if (!config.projects.hasOwnProperty(projectName)) {
throw new SchematicsException(`Could not find project: ${projectName}`);
}
@@ -55,50 +53,40 @@ export function getAngularAppConfig(config: any, projectName: string): any | nev
}
}
export function addStyle(host: Tree, projectName: string, stylePath: string): void {
export function addStyle(host: Tree, projectName: string, stylePath: string) {
const config = readConfig(host);
const appConfig = getAngularAppConfig(config, projectName);
appConfig.architect.build.options.styles.push({
input: stylePath,
input: stylePath
});
writeConfig(host, config);
}
export function addAsset(
host: Tree,
projectName: string,
architect: string,
asset: string | { glob: string; input: string; output: string }
): void {
export function addAsset(host: Tree, projectName: string, architect: string, asset: string | {glob: string; input: string; output: string}) {
const config = readConfig(host);
const appConfig = getAngularAppConfig(config, projectName);
const target = appConfig.architect[architect];
if (target) {
target.options.assets.push(asset);
writeConfig(host, config);
}
appConfig.architect[architect].options.assets.push(asset);
writeConfig(host, config);
}
export function addArchitectBuilder(
host: Tree,
projectName: string,
builderName: string,
builderOpts: any
): void | never {
export function addArchitectBuilder(host: Tree, projectName: string, builderName: string, builderOpts: any): void | never {
const config = readConfig(host);
const appConfig = getAngularAppConfig(config, projectName);
appConfig.architect[builderName] = builderOpts;
writeConfig(host, config);
}
export type WorkspaceSchema = experimental.workspace.WorkspaceSchema;
export type WorkspaceProject = experimental.workspace.WorkspaceProject;
export function getWorkspacePath(host: Tree): string {
const possibleFiles = ['/angular.json', '/.angular.json'];
const path = possibleFiles.filter((path) => host.exists(path))[0];
const path = possibleFiles.filter(path => host.exists(path))[0];
return path;
}
export function getWorkspace(host: Tree): WorkspaceDefinition {
export function getWorkspace(host: Tree): WorkspaceSchema {
const path = getWorkspacePath(host);
const configBuffer = host.read(path);
if (configBuffer === null) {
@@ -106,5 +94,5 @@ export function getWorkspace(host: Tree): WorkspaceDefinition {
}
const content = configBuffer.toString();
return parse(content) as WorkspaceDefinition;
return (parseJson(content, JsonParseMode.Loose) as {}) as WorkspaceSchema;
}

View File

@@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import { Change, InsertChange, NoopChange } from './change';
/**
* Add Import `import { symbolName } from fileName` if the import doesn't exit
* already. Assumes fileToEdit can be resolved and accessed.
@@ -18,32 +18,26 @@ import { Change, InsertChange, NoopChange } from './change';
* @param isDefault (if true, import follows style for importing default exports)
* @return Change
*/
export function insertImport(
source: ts.SourceFile,
fileToEdit: string,
symbolName: string,
fileName: string,
isDefault = false
): Change {
export function insertImport(source: ts.SourceFile, fileToEdit: string, symbolName: string,
fileName: string, isDefault = false): Change {
const rootNode = source;
const allImports = findNodes(rootNode, ts.SyntaxKind.ImportDeclaration);
// get nodes that map to import statements from the file fileName
const relevantImports = allImports.filter((node) => {
const relevantImports = allImports.filter(node => {
// StringLiteral of the ImportDeclaration is the import file (fileName in this case).
const importFiles = node
.getChildren()
.filter((child) => child.kind === ts.SyntaxKind.StringLiteral)
.map((n) => (n as ts.StringLiteral).text);
const importFiles = node.getChildren()
.filter(child => child.kind === ts.SyntaxKind.StringLiteral)
.map(n => (n as ts.StringLiteral).text);
return importFiles.filter((file) => file === fileName).length === 1;
return importFiles.filter(file => file === fileName).length === 1;
});
if (relevantImports.length > 0) {
let importsAsterisk = false;
// imports from import file
const imports: ts.Node[] = [];
relevantImports.forEach((n) => {
relevantImports.forEach(n => {
Array.prototype.push.apply(imports, findNodes(n, ts.SyntaxKind.Identifier));
if (findNodes(n, ts.SyntaxKind.AsteriskToken).length > 0) {
importsAsterisk = true;
@@ -55,7 +49,7 @@ export function insertImport(
return new NoopChange();
}
const importTextNodes = imports.filter((n) => (n as ts.Identifier).text === symbolName);
const importTextNodes = imports.filter(n => (n as ts.Identifier).text === symbolName);
// insert import if it's not there
if (importTextNodes.length === 0) {
@@ -70,9 +64,8 @@ export function insertImport(
}
// no such import declaration exists
const useStrict = findNodes(rootNode, ts.SyntaxKind.StringLiteral).filter(
(n: ts.StringLiteral) => n.text === 'use strict'
);
const useStrict = findNodes(rootNode, ts.SyntaxKind.StringLiteral)
.filter((n: ts.StringLiteral) => n.text === 'use strict');
let fallbackPos = 0;
if (useStrict.length > 0) {
fallbackPos = useStrict[0].end;
@@ -82,12 +75,19 @@ export function insertImport(
// if there are no imports or 'use strict' statement, insert import at beginning of file
const insertAtBeginning = allImports.length === 0 && useStrict.length === 0;
const separator = insertAtBeginning ? '' : ';\n';
const toInsert =
`${separator}import ${open}${symbolName}${close}` + ` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
const toInsert = `${separator}import ${open}${symbolName}${close}` +
` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
return insertAfterLastOccurrence(allImports, toInsert, fileToEdit, fallbackPos, ts.SyntaxKind.StringLiteral);
return insertAfterLastOccurrence(
allImports,
toInsert,
fileToEdit,
fallbackPos,
ts.SyntaxKind.StringLiteral,
);
}
/**
* Find all nodes from the AST in the subtree of node of SyntaxKind kind.
* @param node
@@ -107,7 +107,7 @@ export function findNodes(node: ts.Node, kind: ts.SyntaxKind, max = Infinity): t
}
if (max > 0) {
for (const child of node.getChildren()) {
findNodes(child, kind, max).forEach((node) => {
findNodes(child, kind, max).forEach(node => {
if (max > 0) {
arr.push(node);
}
@@ -123,6 +123,7 @@ export function findNodes(node: ts.Node, kind: ts.SyntaxKind, max = Infinity): t
return arr;
}
/**
* Get all the nodes from a source.
* @param sourceFile The source file object.
@@ -153,13 +154,14 @@ export function findNode(node: ts.Node, kind: ts.SyntaxKind, text: string): ts.N
}
let foundNode: ts.Node | null = null;
ts.forEachChild(node, (childNode) => {
ts.forEachChild(node, childNode => {
foundNode = foundNode || findNode(childNode, kind, text);
});
return foundNode;
}
/**
* Helper for sorting nodes.
* @return function to sort nodes in increasing order of position in sourceFile
@@ -168,6 +170,7 @@ function nodesByPosition(first: ts.Node, second: ts.Node): number {
return first.getStart() - second.getStart();
}
/**
* Insert `toInsert` after the last occurence of `ts.SyntaxKind[nodes[i].kind]`
* or after the last of occurence of `syntaxKind` if the last occurence is a sub child
@@ -181,13 +184,11 @@ function nodesByPosition(first: ts.Node, second: ts.Node): number {
* @return Change instance
* @throw Error if toInsert is first occurence but fall back is not set
*/
export function insertAfterLastOccurrence(
nodes: ts.Node[],
toInsert: string,
file: string,
fallbackPos: number,
syntaxKind?: ts.SyntaxKind
): Change {
export function insertAfterLastOccurrence(nodes: ts.Node[],
toInsert: string,
file: string,
fallbackPos: number,
syntaxKind?: ts.SyntaxKind): Change {
// sort() has a side effect, so make a copy so that we won't overwrite the parent's object.
let lastItem = [...nodes].sort(nodesByPosition).pop();
if (!lastItem) {
@@ -204,6 +205,7 @@ export function insertAfterLastOccurrence(
return new InsertChange(file, lastItemPosition, toInsert);
}
export function getContentOfKeyLiteral(_source: ts.SourceFile, node: ts.Node): string | null {
if (node.kind == ts.SyntaxKind.Identifier) {
return (node as ts.Identifier).text;
@@ -214,11 +216,9 @@ export function getContentOfKeyLiteral(_source: ts.SourceFile, node: ts.Node): s
}
}
function _angularImportsFromNode(
node: ts.ImportDeclaration,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_sourceFile: ts.SourceFile
): { [name: string]: string } {
function _angularImportsFromNode(node: ts.ImportDeclaration,
_sourceFile: ts.SourceFile): {[name: string]: string} {
const ms = node.moduleSpecifier;
let modulePath: string;
switch (ms.kind) {
@@ -249,8 +249,8 @@ function _angularImportsFromNode(
const namedImports = nb as ts.NamedImports;
return namedImports.elements
.map((is: ts.ImportSpecifier) => (is.propertyName ? is.propertyName.text : is.name.text))
.reduce((acc: { [name: string]: string }, curr: string) => {
.map((is: ts.ImportSpecifier) => is.propertyName ? is.propertyName.text : is.name.text)
.reduce((acc: {[name: string]: string}, curr: string) => {
acc[curr] = modulePath;
return acc;
@@ -265,10 +265,13 @@ function _angularImportsFromNode(
}
}
export function getDecoratorMetadata(source: ts.SourceFile, identifier: string, module: string): ts.Node[] {
const angularImports: { [name: string]: string } = findNodes(source, ts.SyntaxKind.ImportDeclaration)
export function getDecoratorMetadata(source: ts.SourceFile, identifier: string,
module: string): ts.Node[] {
const angularImports: {[name: string]: string}
= findNodes(source, ts.SyntaxKind.ImportDeclaration)
.map((node: ts.ImportDeclaration) => _angularImportsFromNode(node, source))
.reduce((acc: { [name: string]: string }, current: { [name: string]: string }) => {
.reduce((acc: {[name: string]: string}, current: {[name: string]: string}) => {
for (const key of Object.keys(current)) {
acc[key] = current[key];
}
@@ -277,17 +280,17 @@ export function getDecoratorMetadata(source: ts.SourceFile, identifier: string,
}, {});
return getSourceNodes(source)
.filter((node) => {
return (
node.kind == ts.SyntaxKind.Decorator && (node as ts.Decorator).expression.kind == ts.SyntaxKind.CallExpression
);
.filter(node => {
return node.kind == ts.SyntaxKind.Decorator
&& (node as ts.Decorator).expression.kind == ts.SyntaxKind.CallExpression;
})
.map((node) => (node as ts.Decorator).expression as ts.CallExpression)
.filter((expr) => {
.map(node => (node as ts.Decorator).expression as ts.CallExpression)
.filter(expr => {
if (expr.expression.kind == ts.SyntaxKind.Identifier) {
const id = expr.expression as ts.Identifier;
return id.getFullText(source) == identifier && angularImports[id.getFullText(source)] === module;
return id.getFullText(source) == identifier
&& angularImports[id.getFullText(source)] === module;
} else if (expr.expression.kind == ts.SyntaxKind.PropertyAccessExpression) {
// This covers foo.NgModule when importing * as foo.
const paExpr = expr.expression as ts.PropertyAccessExpression;
@@ -299,16 +302,17 @@ export function getDecoratorMetadata(source: ts.SourceFile, identifier: string,
const id = paExpr.name.text;
const moduleId = (paExpr.expression as ts.Identifier).getText(source);
return id === identifier && angularImports[moduleId + '.'] === module;
return id === identifier && (angularImports[moduleId + '.'] === module);
}
return false;
})
.filter((expr) => expr.arguments[0] && expr.arguments[0].kind == ts.SyntaxKind.ObjectLiteralExpression)
.map((expr) => expr.arguments[0] as ts.ObjectLiteralExpression);
.filter(expr => expr.arguments[0]
&& expr.arguments[0].kind == ts.SyntaxKind.ObjectLiteralExpression)
.map(expr => expr.arguments[0] as ts.ObjectLiteralExpression);
}
function findClassDeclarationParent(node: ts.Node): ts.ClassDeclaration | undefined {
function findClassDeclarationParent(node: ts.Node): ts.ClassDeclaration|undefined {
if (ts.isClassDeclaration(node)) {
return node;
}
@@ -322,7 +326,7 @@ function findClassDeclarationParent(node: ts.Node): ts.ClassDeclaration | undefi
* @param source source file containing one or more @NgModule
* @returns the name of the first @NgModule, or `undefined` if none is found
*/
export function getFirstNgModuleName(source: ts.SourceFile): string | undefined {
export function getFirstNgModuleName(source: ts.SourceFile): string|undefined {
// First, find the @NgModule decorators.
const ngModulesMetadata = getDecoratorMetadata(source, 'NgModule', '@angular/core');
if (ngModulesMetadata.length === 0) {
@@ -345,10 +349,10 @@ export function addSymbolToNgModuleMetadata(
ngModulePath: string,
metadataField: string,
symbolName: string,
importPath: string | null = null
importPath: string | null = null,
): Change[] {
const nodes = getDecoratorMetadata(source, 'NgModule', '@angular/core');
let node: any = nodes[0]; // tslint:disable-line:no-any
let node: any = nodes[0]; // tslint:disable-line:no-any
// Find the decorator declaration.
if (!node) {
@@ -356,8 +360,9 @@ export function addSymbolToNgModuleMetadata(
}
// Get all the children property assignment of object literals.
const matchingProperties: ts.ObjectLiteralElement[] = (node as ts.ObjectLiteralExpression).properties
.filter((prop) => prop.kind == ts.SyntaxKind.PropertyAssignment)
const matchingProperties: ts.ObjectLiteralElement[] =
(node as ts.ObjectLiteralExpression).properties
.filter(prop => prop.kind == ts.SyntaxKind.PropertyAssignment)
// Filter out every fields that's not "metadataField". Also handles string literals
// (but not expressions).
.filter((prop: ts.PropertyAssignment) => {
@@ -427,9 +432,8 @@ export function addSymbolToNgModuleMetadata(
}
if (Array.isArray(node)) {
// eslint-disable-next-line @typescript-eslint/ban-types
const nodeArray = node as {} as ts.Node[];
const symbolsArray = nodeArray.map((node) => node.getText());
const nodeArray = node as {} as Array<ts.Node>;
const symbolsArray = nodeArray.map(node => node.getText());
if (symbolsArray.includes(symbolName)) {
return [];
}
@@ -484,93 +488,81 @@ export function addSymbolToNgModuleMetadata(
* Custom function to insert a declaration (component, pipe, directive)
* into NgModule declarations. It also imports the component.
*/
export function addDeclarationToModule(
source: ts.SourceFile,
modulePath: string,
classifiedName: string,
importPath: string
): Change[] {
return addSymbolToNgModuleMetadata(source, modulePath, 'declarations', classifiedName, importPath);
export function addDeclarationToModule(source: ts.SourceFile,
modulePath: string, classifiedName: string,
importPath: string): Change[] {
return addSymbolToNgModuleMetadata(
source, modulePath, 'declarations', classifiedName, importPath);
}
/**
* Custom function to insert an NgModule into NgModule imports. It also imports the module.
*/
export function addImportToModule(
source: ts.SourceFile,
modulePath: string,
classifiedName: string,
importPath: string
): Change[] {
export function addImportToModule(source: ts.SourceFile,
modulePath: string, classifiedName: string,
importPath: string): Change[] {
return addSymbolToNgModuleMetadata(source, modulePath, 'imports', classifiedName, importPath);
}
/**
* Custom function to insert a provider into NgModule. It also imports it.
*/
export function addProviderToModule(
source: ts.SourceFile,
modulePath: string,
classifiedName: string,
importPath: string
): Change[] {
export function addProviderToModule(source: ts.SourceFile,
modulePath: string, classifiedName: string,
importPath: string): Change[] {
return addSymbolToNgModuleMetadata(source, modulePath, 'providers', classifiedName, importPath);
}
/**
* Custom function to insert an export into NgModule. It also imports it.
*/
export function addExportToModule(
source: ts.SourceFile,
modulePath: string,
classifiedName: string,
importPath: string
): Change[] {
export function addExportToModule(source: ts.SourceFile,
modulePath: string, classifiedName: string,
importPath: string): Change[] {
return addSymbolToNgModuleMetadata(source, modulePath, 'exports', classifiedName, importPath);
}
/**
* Custom function to insert an export into NgModule. It also imports it.
*/
export function addBootstrapToModule(
source: ts.SourceFile,
modulePath: string,
classifiedName: string,
importPath: string
): Change[] {
export function addBootstrapToModule(source: ts.SourceFile,
modulePath: string, classifiedName: string,
importPath: string): Change[] {
return addSymbolToNgModuleMetadata(source, modulePath, 'bootstrap', classifiedName, importPath);
}
/**
* Custom function to insert an entryComponent into NgModule. It also imports it.
*/
export function addEntryComponentToModule(
source: ts.SourceFile,
modulePath: string,
classifiedName: string,
importPath: string
): Change[] {
return addSymbolToNgModuleMetadata(source, modulePath, 'entryComponents', classifiedName, importPath);
export function addEntryComponentToModule(source: ts.SourceFile,
modulePath: string, classifiedName: string,
importPath: string): Change[] {
return addSymbolToNgModuleMetadata(
source, modulePath,
'entryComponents', classifiedName, importPath,
);
}
/**
* Determine if an import already exists.
*/
export function isImported(source: ts.SourceFile, classifiedName: string, importPath: string): boolean {
export function isImported(source: ts.SourceFile,
classifiedName: string,
importPath: string): boolean {
const allNodes = getSourceNodes(source);
const matchingNodes = allNodes
.filter((node) => node.kind === ts.SyntaxKind.ImportDeclaration)
.filter(node => node.kind === ts.SyntaxKind.ImportDeclaration)
.filter((imp: ts.ImportDeclaration) => imp.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral)
.filter((imp: ts.ImportDeclaration) => {
return (imp.moduleSpecifier as ts.StringLiteral).text === importPath;
return (<ts.StringLiteral> imp.moduleSpecifier).text === importPath;
})
.filter((imp: ts.ImportDeclaration) => {
if (!imp.importClause) {
return false;
}
const nodes = findNodes(imp.importClause, ts.SyntaxKind.ImportSpecifier).filter(
(n) => n.getText() === classifiedName
);
const nodes = findNodes(imp.importClause, ts.SyntaxKind.ImportSpecifier)
.filter(n => n.getText() === classifiedName);
return nodes.length > 0;
});

View File

@@ -10,6 +10,7 @@ export interface Host {
read(path: string): Promise<string>;
}
export interface Change {
apply(host: Host): Promise<void>;
@@ -25,6 +26,7 @@ export interface Change {
readonly description: string;
}
/**
* An operation that does nothing.
*/
@@ -32,15 +34,15 @@ export class NoopChange implements Change {
description = 'No operation.';
order = Infinity;
path = null;
apply(): Promise<void> {
return Promise.resolve();
}
apply() { return Promise.resolve(); }
}
/**
* Will add text to the source code.
*/
export class InsertChange implements Change {
order: number;
description: string;
@@ -55,8 +57,8 @@ export class InsertChange implements Change {
/**
* This method does not insert spaces if there is none in the original string.
*/
apply(host: Host): Promise<void> {
return host.read(this.path).then((content) => {
apply(host: Host) {
return host.read(this.path).then(content => {
const prefix = content.substring(0, this.pos);
const suffix = content.substring(this.pos);
@@ -69,6 +71,7 @@ export class InsertChange implements Change {
* Will remove text from the source code.
*/
export class RemoveChange implements Change {
order: number;
description: string;
@@ -81,7 +84,7 @@ export class RemoveChange implements Change {
}
apply(host: Host): Promise<void> {
return host.read(this.path).then((content) => {
return host.read(this.path).then(content => {
const prefix = content.substring(0, this.pos);
const suffix = content.substring(this.pos + this.toRemove.length);
@@ -98,7 +101,8 @@ export class ReplaceChange implements Change {
order: number;
description: string;
constructor(public path: string, private pos: number, private oldText: string, private newText: string) {
constructor(public path: string, private pos: number, private oldText: string,
private newText: string) {
if (pos < 0) {
throw new Error('Negative positions are invalid');
}
@@ -107,7 +111,7 @@ export class ReplaceChange implements Change {
}
apply(host: Host): Promise<void> {
return host.read(this.path).then((content) => {
return host.read(this.path).then(content => {
const prefix = content.substring(0, this.pos);
const suffix = content.substring(this.pos + this.oldText.length);
const text = content.substring(this.pos, this.pos + this.oldText.length);

View File

@@ -3,3 +3,5 @@
These are utility files copied over from `@angular-devkit`.
They are not exported so they need to be manually copied over.
Please do not edit directly.

View File

@@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import * as ts from 'typescript';
import { findNodes, insertAfterLastOccurrence } from './ast-utils';
import { Change, NoopChange } from './change';
@@ -31,22 +30,25 @@ export function insertImport(
const allImports = findNodes(rootNode, ts.SyntaxKind.ImportDeclaration);
// get nodes that map to import statements from the file fileName
const relevantImports = allImports.filter((node) => {
const relevantImports = allImports.filter(node => {
// StringLiteral of the ImportDeclaration is the import file (fileName in this case).
const importFiles = node
.getChildren()
.filter((child) => child.kind === ts.SyntaxKind.StringLiteral)
.map((n) => (n as ts.StringLiteral).text);
.filter(child => child.kind === ts.SyntaxKind.StringLiteral)
.map(n => (n as ts.StringLiteral).text);
return importFiles.filter((file) => file === fileName).length === 1;
return importFiles.filter(file => file === fileName).length === 1;
});
if (relevantImports.length > 0) {
let importsAsterisk = false;
// imports from import file
const imports: ts.Node[] = [];
relevantImports.forEach((n) => {
Array.prototype.push.apply(imports, findNodes(n, ts.SyntaxKind.Identifier));
relevantImports.forEach(n => {
Array.prototype.push.apply(
imports,
findNodes(n, ts.SyntaxKind.Identifier)
);
if (findNodes(n, ts.SyntaxKind.AsteriskToken).length > 0) {
importsAsterisk = true;
}
@@ -57,15 +59,25 @@ export function insertImport(
return new NoopChange();
}
const importTextNodes = imports.filter((n) => (n as ts.Identifier).text === symbolName);
const importTextNodes = imports.filter(
n => (n as ts.Identifier).text === symbolName
);
// insert import if it's not there
if (importTextNodes.length === 0) {
const fallbackPos =
findNodes(relevantImports[0], ts.SyntaxKind.CloseBraceToken)[0].getStart() ||
findNodes(
relevantImports[0],
ts.SyntaxKind.CloseBraceToken
)[0].getStart() ||
findNodes(relevantImports[0], ts.SyntaxKind.FromKeyword)[0].getStart();
return insertAfterLastOccurrence(imports, `, ${symbolName}`, fileToEdit, fallbackPos);
return insertAfterLastOccurrence(
imports,
`, ${symbolName}`,
fileToEdit,
fallbackPos
);
}
return new NoopChange();
@@ -85,7 +97,14 @@ export function insertImport(
const insertAtBeginning = allImports.length === 0 && useStrict.length === 0;
const separator = insertAtBeginning ? '' : ';\n';
const toInsert =
`${separator}import ${open}${symbolName}${close}` + ` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
`${separator}import ${open}${symbolName}${close}` +
` from '${fileName}'${insertAtBeginning ? ';\n' : ''}`;
return insertAfterLastOccurrence(allImports, toInsert, fileToEdit, fallbackPos, ts.SyntaxKind.StringLiteral);
return insertAfterLastOccurrence(
allImports,
toInsert,
fileToEdit,
fallbackPos,
ts.SyntaxKind.StringLiteral
);
}

View File

@@ -1,11 +1,11 @@
import { Tree } from '@angular-devkit/schematics';
import {Tree} from '@angular-devkit/schematics';
/**
* Adds a package to the package.json
*/
export function addPackageToPackageJson(host: Tree, type: string, pkg: string, version: string): Tree {
export function addPackageToPackageJson(host: Tree, type: string, pkg: string, version: string) {
if (host.exists('package.json')) {
const sourceText = host.read('package.json')?.toString('utf-8');
const sourceText = host.read('package.json')!.toString('utf-8');
const json = JSON.parse(sourceText);
if (!json[type]) {
json[type] = {};

View File

@@ -1,3 +1,4 @@
export interface IonicGlobal {
config?: any;
asyncQueue?: boolean;

View File

@@ -1,31 +1,27 @@
import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';
export class IonicRouteStrategy implements RouteReuseStrategy {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
shouldDetach(_route: ActivatedRouteSnapshot): boolean {
return false;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
shouldAttach(_route: ActivatedRouteSnapshot): boolean {
return false;
}
store(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_route: ActivatedRouteSnapshot,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_detachedTree: DetachedRouteHandle
): void {
store(_route: ActivatedRouteSnapshot, _detachedTree: DetachedRouteHandle): void {
return;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
retrieve(_route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
return null;
}
shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
shouldReuseRoute(
future: ActivatedRouteSnapshot,
curr: ActivatedRouteSnapshot
): boolean {
if (future.routeConfig !== curr.routeConfig) {
return false;
}

View File

@@ -1,3 +1,4 @@
interface ControllerShape<Opts, HTMLElm> {
create(options: Opts): Promise<HTMLElm>;
dismiss(data?: any, role?: string, id?: string): Promise<boolean>;
@@ -10,7 +11,7 @@ export class OverlayBaseController<Opts, Overlay> implements ControllerShape<Opt
/**
* Creates a new overlay
*/
create(opts?: Opts): Promise<Overlay> {
create(opts?: Opts) {
// TODO: next major release opts is not optional
return this.ctrl.create((opts || {}) as any);
}
@@ -18,14 +19,14 @@ export class OverlayBaseController<Opts, Overlay> implements ControllerShape<Opt
/**
* When `id` is not provided, it dismisses the top overlay.
*/
dismiss(data?: any, role?: string, id?: string): Promise<boolean> {
dismiss(data?: any, role?: string, id?: string) {
return this.ctrl.dismiss(data, role, id);
}
/**
* Returns the top overlay.
*/
getTop(): Promise<Overlay | undefined> {
getTop() {
return this.ctrl.getTop();
}
}

View File

@@ -1,7 +1,8 @@
declare const __zone_symbol__requestAnimationFrame: any;
declare const requestAnimationFrame: any;
export const raf = (h: any): any => {
export const raf = (h: any) => {
if (typeof __zone_symbol__requestAnimationFrame === 'function') {
return __zone_symbol__requestAnimationFrame(h);
}

View File

@@ -1,51 +0,0 @@
{
"root": true,
"ignorePatterns": [
"projects/**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"tsconfig.json",
"e2e/tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/component-selector": [
"error",
{
"prefix": "app",
"style": "kebab-case",
"type": "element"
}
],
"@angular-eslint/directive-selector": [
"error",
{
"prefix": "app",
"style": "camelCase",
"type": "attribute"
}
]
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {}
}
]
}

View File

@@ -33,9 +33,7 @@
"output": "./svg"
}
],
"styles": [
"src/styles.css"
],
"styles": ["src/styles.css"],
"scripts": []
},
"configurations": {
@@ -49,6 +47,7 @@
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"progress": false,
@@ -87,12 +86,10 @@
}
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
"tsConfig": ["tsconfig.app.json", "tsconfig.spec.json"],
"exclude": ["**/node_modules/**"]
}
},
"server": {
@@ -140,8 +137,5 @@
}
}
},
"defaultProject": "test-app",
"cli": {
"defaultCollection": "@angular-eslint/schematics"
}
"defaultProject": "test-app"
}

View File

@@ -41,23 +41,3 @@ describe('Modals', () => {
});
});
describe('Modals: Inline', () => {
beforeEach(() => {
cy.visit('/modal-inline');
});
it('should initially have no items', () => {
cy.get('ion-list ion-item').should('not.exist');
});
it('should have items after 1500ms', () => {
cy.wait(1500);
cy.get('ion-list ion-item:nth-child(1)').should('have.text', 'A');
cy.get('ion-list ion-item:nth-child(2)').should('have.text', 'B');
cy.get('ion-list ion-item:nth-child(3)').should('have.text', 'C');
cy.get('ion-list ion-item:nth-child(4)').should('have.text', 'D');
});
});

View File

@@ -1,18 +0,0 @@
describe('Popovers: Inline', () => {
beforeEach(() => {
cy.visit('/popover-inline');
});
it('should initially have no items', () => {
cy.get('ion-list ion-item').should('not.exist');
});
it('should have items after 1500ms', () => {
cy.wait(1500);
cy.get('ion-list ion-item:nth-child(1)').should('have.text', 'A');
cy.get('ion-list ion-item:nth-child(2)').should('have.text', 'B');
cy.get('ion-list ion-item:nth-child(3)').should('have.text', 'C');
cy.get('ion-list ion-item:nth-child(4)').should('have.text', 'D');
});
});

View File

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
"start": "npm run sync && ng serve",
"sync:build": "sh scripts/build-ionic.sh",
"sync": "sh scripts/sync.sh",
"build": "npm run sync && ng build --configuration production --no-progress",
"build": "npm run sync && ng build --prod --no-progress",
"lint": "ng lint",
"postinstall": "npm run sync && ngcc",
"serve:ssr": "node dist/test-app/server/main.js",
@@ -20,49 +20,41 @@
"test.watch": "concurrently \"npm run start\" \"wait-on http-get://localhost:4200 && npm run cy.open\" --kill-others --success first"
},
"dependencies": {
"@angular/animations": "^12.2.8",
"@angular/common": "^12.2.8",
"@angular/compiler": "^12.2.8",
"@angular/core": "^12.2.8",
"@angular/forms": "^12.2.8",
"@angular/platform-browser": "^12.2.8",
"@angular/platform-browser-dynamic": "^12.2.8",
"@angular/platform-server": "^12.2.8",
"@angular/router": "^12.2.8",
"@angular/animations": "^11.2.11",
"@angular/common": "^11.2.11",
"@angular/compiler": "^11.2.11",
"@angular/core": "^11.2.11",
"@angular/forms": "^11.2.11",
"@angular/platform-browser": "^11.2.11",
"@angular/platform-browser-dynamic": "^11.2.11",
"@angular/platform-server": "^11.2.11",
"@angular/router": "^11.2.11",
"@ionic/angular": "^5.3.1",
"@ionic/angular-server": "^5.3.1",
"@nguniversal/express-engine": "^12.1.1",
"@nguniversal/express-engine": "^11.2.1",
"angular-in-memory-web-api": "^0.11.0",
"core-js": "^2.6.11",
"express": "^4.15.2",
"rxjs": "^6.5.5",
"tslib": "^2.0.0",
"typescript-eslint-language-service": "^4.1.5",
"zone.js": "^0.11.4"
"zone.js": "^0.10.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "^12.2.8",
"@angular-eslint/builder": "12.5.0",
"@angular-eslint/eslint-plugin": "12.5.0",
"@angular-eslint/eslint-plugin-template": "12.5.0",
"@angular-eslint/schematics": "12.5.0",
"@angular-eslint/template-parser": "12.5.0",
"@angular/cli": "^12.2.8",
"@angular/compiler-cli": "^12.2.8",
"@angular/language-service": "^12.2.8",
"@nguniversal/builders": "^12.1.1",
"@angular-devkit/build-angular": "^0.1102.10",
"@angular/cli": "^11.2.10",
"@angular/compiler-cli": "^11.2.11",
"@angular/language-service": "^11.2.11",
"@nguniversal/builders": "^11.2.1",
"@types/express": "^4.17.7",
"@types/node": "^12.12.54",
"@typescript-eslint/eslint-plugin": "4.28.2",
"@typescript-eslint/parser": "4.28.2",
"codelyzer": "^6.0.1",
"concurrently": "^6.0.0",
"cypress": "^6.7.1",
"eslint": "^7.26.0",
"ts-loader": "^6.2.2",
"ts-node": "^8.3.0",
"typescript": "^4.3.5",
"tslint": "~6.1.0",
"typescript": "^4.0.7",
"wait-on": "^5.2.1",
"webpack": "^5.61.0",
"webpack-cli": "^3.3.12"
}
}

View File

@@ -27,9 +27,7 @@ const routes: Routes = [
{ path: 'inputs', component: InputsComponent },
{ path: 'form', component: FormComponent },
{ path: 'modals', component: ModalComponent },
{ path: 'modal-inline', loadChildren: () => import('./modal-inline').then(m => m.ModalInlineModule) },
{ path: 'view-child', component: ViewChildComponent },
{ path: 'popover-inline', loadChildren: () => import('./popover-inline').then(m => m.PopoverInlineModule) },
{ path: 'providers', component: ProvidersComponent },
{ path: 'router-link', component: RouterLinkComponent },
{ path: 'router-link-page', component: RouterLinkPageComponent },

View File

@@ -63,7 +63,7 @@ import { AlertComponent } from './alert/alert.component';
AppRoutingModule,
FormsModule,
ReactiveFormsModule,
IonicModule.forRoot({ keyboardHeight: 12345 }),
IonicModule.forRoot(),
],
entryComponents: [
ModalExampleComponent,

View File

@@ -1,2 +0,0 @@
export * from './modal-inline.component';
export * from './modal-inline.module';

View File

@@ -1,16 +0,0 @@
import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router";
import { ModalInlineComponent } from ".";
@NgModule({
imports: [
RouterModule.forChild([
{
path: '',
component: ModalInlineComponent
}
])
],
exports: [RouterModule]
})
export class ModalInlineRoutingModule { }

View File

@@ -1,11 +0,0 @@
<ion-modal [isOpen]="true" [breakpoints]="[0.1, 0.5, 1]" [initialBreakpoint]="0.5">
<ng-template>
<ion-content>
<ion-list>
<ion-item *ngFor="let item of items">
<ion-label>{{ item }}</ion-label>
</ion-item>
</ion-list>
</ion-content>
</ng-template>
</ion-modal>

View File

@@ -1,21 +0,0 @@
import { AfterViewInit, Component } from "@angular/core";
/**
* Validates that inline modals will correctly display
* dynamic contents that are updated after the modal is
* display.
*/
@Component({
selector: 'app-modal-inline',
templateUrl: 'modal-inline.component.html'
})
export class ModalInlineComponent implements AfterViewInit {
items: string[] = [];
ngAfterViewInit(): void {
setTimeout(() => {
this.items = ['A', 'B', 'C', 'D'];
}, 1000);
}
}

View File

@@ -1,12 +0,0 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { IonicModule } from "@ionic/angular";
import { ModalInlineRoutingModule } from "./modal-inline-routing.module";
import { ModalInlineComponent } from "./modal-inline.component";
@NgModule({
imports: [CommonModule, IonicModule, ModalInlineRoutingModule],
declarations: [ModalInlineComponent],
exports: [ModalInlineComponent]
})
export class ModalInlineModule { }

View File

@@ -1 +0,0 @@
export * from './popover-inline.module';

View File

@@ -1,14 +0,0 @@
import { NgModule } from "@angular/core";
import { RouterModule } from "@angular/router";
import { PopoverInlineComponent } from "./popover-inline.component";
@NgModule({
imports: [RouterModule.forChild([
{
path: '',
component: PopoverInlineComponent
}
])],
exports: [RouterModule]
})
export class PopoverInlineRoutingModule { }

View File

@@ -1,11 +0,0 @@
<ion-popover [isOpen]="true">
<ng-template>
<ion-content>
<ion-list>
<ion-item *ngFor="let item of items">
<ion-label>{{ item }}</ion-label>
</ion-item>
</ion-list>
</ion-content>
</ng-template>
</ion-popover>

View File

@@ -1,22 +0,0 @@
import { AfterViewInit, Component } from "@angular/core";
/**
* Validates that inline popovers will correctly display
* dynamic contents that are updated after the modal is
* display.
*/
@Component({
selector: 'app-popover-inline',
templateUrl: 'popover-inline.component.html'
})
export class PopoverInlineComponent implements AfterViewInit {
items: string[] = [];
ngAfterViewInit(): void {
setTimeout(() => {
this.items = ['A', 'B', 'C', 'D'];
}, 1000);
}
}

View File

@@ -1,11 +0,0 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { IonicModule } from "@ionic/angular";
import { PopoverInlineRoutingModule } from "./popover-inline-routing.module";
import { PopoverInlineComponent } from "./popover-inline.component";
@NgModule({
imports: [CommonModule, IonicModule, PopoverInlineRoutingModule],
declarations: [PopoverInlineComponent],
})
export class PopoverInlineModule { }

View File

@@ -69,6 +69,7 @@ export class ProvidersComponent {
// test config
this.isTesting = config.getBoolean('_testing');
config.set('keyboardHeight', 12345);
this.keyboardHeight = config.getNumber('keyboardHeight');
zone.runOutsideAngular(() => {

Some files were not shown because too many files have changed in this diff Show More