mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65c94b16d5 | ||
|
|
fb0b0ae46e | ||
|
|
a3bc83a1e6 |
33
.github/CODEOWNERS
vendored
33
.github/CODEOWNERS
vendored
@@ -11,15 +11,13 @@
|
||||
# In each subsection folders are ordered first by depth, then alphabetically.
|
||||
# This should make it easy to add new rules without breaking existing ones.
|
||||
|
||||
* @ionic-team/framework
|
||||
|
||||
# Frameworks
|
||||
|
||||
## Angular
|
||||
|
||||
/packages/angular/ @sean-perkins @thetaPC
|
||||
/packages/angular-server @sean-perkins @thetaPC
|
||||
/packages/angular/test @thetaPC
|
||||
/angular/ @sean-perkins
|
||||
/packages/angular-server @sean-perkins
|
||||
/angular/test
|
||||
|
||||
## React
|
||||
|
||||
@@ -30,23 +28,19 @@
|
||||
|
||||
## Vue
|
||||
|
||||
/packages/vue/ @liamdebeasi @thetaPC
|
||||
/packages/vue-router/ @liamdebeasi @thetaPC
|
||||
/packages/vue/test/ @thetaPC
|
||||
/packages/vue-router/__tests__ @thetaPC
|
||||
/packages/vue/ @liamdebeasi
|
||||
/packages/vue-router/ @liamdebeasi
|
||||
/packages/vue/test/
|
||||
/packages/vue-router/__tests__
|
||||
|
||||
# Components
|
||||
|
||||
/core/src/components/accordion/ @liamdebeasi
|
||||
/core/src/components/accordion-group/ @liamdebeasi
|
||||
|
||||
/core/src/components/checkbox/ @amandaejohnston
|
||||
|
||||
/core/src/components/datetime/ @liamdebeasi @amandaejohnston @sean-perkins
|
||||
/core/src/components/datetime-button/ @liamdebeasi
|
||||
|
||||
/core/src/components/item/ @brandyscarney
|
||||
|
||||
/core/src/components/menu/ @amandaejohnston
|
||||
/core/src/components/menu-toggle/ @amandaejohnston
|
||||
|
||||
@@ -56,19 +50,9 @@
|
||||
/core/src/components/picker-internal/ @liamdebeasi
|
||||
/core/src/components/picker-column-internal/ @liamdebeasi
|
||||
|
||||
/core/src/components/radio/ @amandaejohnston
|
||||
/core/src/components/radio-group/ @amandaejohnston
|
||||
|
||||
/core/src/components/refresher/ @liamdebeasi
|
||||
/core/src/components/refresher-content/ @liamdebeasi
|
||||
|
||||
/core/src/components/searchbar/ @brandyscarney
|
||||
|
||||
/core/src/components/segment/ @brandyscarney
|
||||
/core/src/components/segment-button/ @brandyscarney
|
||||
|
||||
/core/src/components/skeleton-text/ @brandyscarney
|
||||
|
||||
# Utilities
|
||||
|
||||
/core/src/utils/animation/ @liamdebeasi
|
||||
@@ -80,6 +64,3 @@
|
||||
/core/src/utils/sanitization/ @liamdebeasi
|
||||
/core/src/utils/tap-click/ @liamdebeasi
|
||||
/core/src/utils/transition/ @liamdebeasi
|
||||
|
||||
/core/src/css/ @brandyscarney
|
||||
/core/src/themes/ @brandyscarney
|
||||
|
||||
10
.github/CONTRIBUTING.md
vendored
10
.github/CONTRIBUTING.md
vendored
@@ -163,7 +163,7 @@ npm i
|
||||
npm run build
|
||||
npm pack --pack-destination ~
|
||||
|
||||
cd ../packages/angular
|
||||
cd ../angular
|
||||
npm i
|
||||
npm run sync
|
||||
npm run build
|
||||
@@ -295,7 +295,7 @@ See [Ionic's E2E testing guide](../core/src/utils/test/playwright/docs/README.md
|
||||
#### Modifying Files
|
||||
|
||||
1. Locate the files inside the relevant root directory:
|
||||
- Angular: [`/packages/angular/src`](/packages/angular/src)
|
||||
- Angular: [`/angular/src`](/angular/src)
|
||||
- React: [`/packages/react/src`](/packages/react/src)
|
||||
- Vue: [`/packages/vue/src`](/packages/vue/src)
|
||||
2. Make your changes to the files. If the change is overly complex or out of the ordinary, add comments so we can understand the changes.
|
||||
@@ -311,7 +311,7 @@ See [Ionic's E2E testing guide](../core/src/utils/test/playwright/docs/README.md
|
||||
##### Previewing in this repository
|
||||
|
||||
Follow the steps in the test directory for each framework:
|
||||
- Angular: [`/packages/angular/test`](/packages/angular/test/README.md)
|
||||
- Angular: [`/angular/test`](/angular/test/README.md)
|
||||
- React: [`/packages/react/test`](/packages/react/test/README.md)
|
||||
- Vue: [`/packages/vue/test`](/packages/vue/test/README.md)
|
||||
|
||||
@@ -322,7 +322,7 @@ Follow the steps to [preview changes in core](#preview-changes).
|
||||
#### Lint Changes
|
||||
|
||||
1. Run `npm run lint` to lint the TypeScript in the relevant directory:
|
||||
- Angular: [`/packages/angular/src`](/packages/angular/src)
|
||||
- Angular: [`/angular/src`](/angular/src)
|
||||
- React: [`/packages/react/src`](/packages/react/src)
|
||||
- Vue: [`/packages/vue/src`](/packages/vue/src)
|
||||
2. If there are lint errors, run `npm run lint.fix` to automatically fix any errors. Repeat step 1 to ensure the errors have been fixed, and manually fix them if not.
|
||||
@@ -330,7 +330,7 @@ Follow the steps to [preview changes in core](#preview-changes).
|
||||
#### Modifying Tests
|
||||
|
||||
1. Locate the e2e test to modify inside the relevant test app directory:
|
||||
- Angular: [`/packages/angular/test/base/e2e/src`](/packages/angular/test/base/e2e/src)
|
||||
- Angular: [`/angular/test/base/e2e/src`](/angular/test/base/e2e/src)
|
||||
- React: [`/packages/react/test/base/tests/e2e/specs`](/packages/react/test/base/tests/e2e/specs)
|
||||
- Vue: [`/packages/vue/test/base/tests/e2e/specs`](/packages/vue/test/base/tests/e2e/specs)
|
||||
2. If a test exists, modify the test by adding an example to reproduce the problem fixed or feature added.
|
||||
|
||||
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
@@ -14,7 +14,3 @@ updates:
|
||||
- dependency-name: "@stencil/sass"
|
||||
- dependency-name: "@stencil/vue-output-target"
|
||||
- dependency-name: "ionicons"
|
||||
- dependency-name: "@capacitor/core"
|
||||
- dependency-name: "@capacitor/keyboard"
|
||||
- dependency-name: "@capacitor/haptics"
|
||||
- dependency-name: "@capacitor/status-bar"
|
||||
|
||||
2
.github/labeler.yml
vendored
2
.github/labeler.yml
vendored
@@ -9,7 +9,7 @@
|
||||
- core/**/*
|
||||
|
||||
'package: angular':
|
||||
- packages/angular/**/*
|
||||
- angular/**/*
|
||||
- packages/angular-*/**/*
|
||||
|
||||
'package: react':
|
||||
|
||||
@@ -5,7 +5,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
|
||||
- name: Install Angular Server Dependencies
|
||||
run: npm ci
|
||||
|
||||
@@ -5,7 +5,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
@@ -14,25 +14,25 @@ runs:
|
||||
- name: Install Angular Dependencies
|
||||
run: npm ci
|
||||
shell: bash
|
||||
working-directory: ./packages/angular
|
||||
working-directory: ./angular
|
||||
- name: Sync
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./packages/angular
|
||||
working-directory: ./angular
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
shell: bash
|
||||
working-directory: ./packages/angular
|
||||
working-directory: ./angular
|
||||
- name: Build
|
||||
run: npm run build
|
||||
shell: bash
|
||||
working-directory: ./packages/angular
|
||||
working-directory: ./angular
|
||||
- name: Check Diff
|
||||
run: git diff --exit-code
|
||||
shell: bash
|
||||
working-directory: ./packages/angular
|
||||
working-directory: ./angular
|
||||
- uses: ./.github/workflows/actions/upload-archive
|
||||
with:
|
||||
name: ionic-angular
|
||||
output: ./packages/angular/AngularBuild.zip
|
||||
paths: ./packages/angular/dist
|
||||
output: ./angular/AngularBuild.zip
|
||||
paths: ./angular/dist
|
||||
|
||||
@@ -11,7 +11,7 @@ runs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
|
||||
14
.github/workflows/actions/build-core/action.yml
vendored
14
.github/workflows/actions/build-core/action.yml
vendored
@@ -1,28 +1,16 @@
|
||||
name: 'Build Ionic Core'
|
||||
description: 'Build Ionic Core'
|
||||
inputs:
|
||||
ionicons-version:
|
||||
description: 'The NPM tag of ionicons to install.'
|
||||
type: string
|
||||
required: false
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
working-directory: ./core
|
||||
shell: bash
|
||||
# If an Ionicons version was specified install that.
|
||||
# Otherwise just use the version defined in the package.json.
|
||||
- name: Install Ionicons Version
|
||||
if: inputs.ionicons-version != ''
|
||||
run: npm install ionicons@${{ inputs.ionicons-version }}
|
||||
working-directory: ./core
|
||||
shell: bash
|
||||
- name: Build Core
|
||||
run: npm run build -- --ci
|
||||
working-directory: ./core
|
||||
|
||||
@@ -5,7 +5,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
|
||||
@@ -5,7 +5,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
|
||||
@@ -5,7 +5,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
|
||||
@@ -5,7 +5,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
|
||||
@@ -14,6 +14,6 @@ runs:
|
||||
with:
|
||||
name: ${{ inputs.name }}
|
||||
path: ${{ inputs.path }}
|
||||
- name: Extract Archive
|
||||
- name: Exract Archive
|
||||
run: unzip -q -o ${{ inputs.path }}/${{ inputs.filename }}
|
||||
shell: bash
|
||||
|
||||
12
.github/workflows/actions/publish-npm/action.yml
vendored
12
.github/workflows/actions/publish-npm/action.yml
vendored
@@ -21,22 +21,16 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
# Provenance requires npm 9.5.0+
|
||||
node-version: 16.x
|
||||
- name: Install latest npm
|
||||
run: npm install -g npm@latest
|
||||
shell: bash
|
||||
# This ensures the local version of Lerna is installed
|
||||
# and that we do not use the global Lerna version
|
||||
- name: Install root dependencies
|
||||
run: npm ci
|
||||
shell: bash
|
||||
- name: Install Dependencies
|
||||
run: npx lerna bootstrap --include-dependencies --scope ${{ inputs.scope }} --ignore-scripts -- --legacy-peer-deps
|
||||
run: lerna bootstrap --include-dependencies --scope ${{ inputs.scope }} --ignore-scripts -- --legacy-peer-deps
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
- name: Update Version
|
||||
run: npx lerna version ${{ inputs.version }} --yes --exact --no-changelog --no-push --no-git-tag-version --preid=${{ inputs.preid }}
|
||||
run: lerna version ${{ inputs.version }} --yes --exact --no-changelog --no-push --no-git-tag-version --preid=${{ inputs.preid }}
|
||||
shell: bash
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
- name: Run Build
|
||||
|
||||
@@ -8,7 +8,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 16
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
@@ -27,16 +27,16 @@ runs:
|
||||
- name: Create Test App
|
||||
run: ./build.sh ${{ inputs.app }}
|
||||
shell: bash
|
||||
working-directory: ./packages/angular/test
|
||||
working-directory: ./angular/test
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
shell: bash
|
||||
working-directory: ./packages/angular/test/build/${{ inputs.app }}
|
||||
working-directory: ./angular/test/build/${{ inputs.app }}
|
||||
- name: Sync Built Changes
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./packages/angular/test/build/${{ inputs.app }}
|
||||
working-directory: ./angular/test/build/${{ inputs.app }}
|
||||
- name: Run Tests
|
||||
run: npm run test
|
||||
shell: bash
|
||||
working-directory: ./packages/angular/test/build/${{ inputs.app }}
|
||||
working-directory: ./angular/test/build/${{ inputs.app }}
|
||||
|
||||
@@ -5,7 +5,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
@@ -13,15 +13,6 @@ runs:
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- name: Check Diff
|
||||
run: |
|
||||
git diff --exit-code || {
|
||||
echo -e "\033[1;31m⚠️ Error: Differences Detected ⚠️\033[0m"
|
||||
echo -e "\033[1;31mThere are uncommitted changes between the build outputs from CI and your branch.\033[0m"
|
||||
echo -e "\033[1;31mPlease ensure you have followed these steps:\033[0m"
|
||||
echo -e "\033[1;31m1. Run 'npm run build' locally to generate the latest build output.\033[0m"
|
||||
echo -e "\033[1;31m2. Commit and push all necessary changes to your branch.\033[0m"
|
||||
echo -e "\033[1;31m3. Compare and validate the differences before proceeding.\033[0m"
|
||||
exit 1
|
||||
}
|
||||
run: git diff --exit-code
|
||||
shell: bash
|
||||
working-directory: ./core
|
||||
|
||||
@@ -5,7 +5,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
working-directory: ./core
|
||||
|
||||
@@ -13,7 +13,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
name: 'Test Core Spec'
|
||||
description: 'Test Core Spec'
|
||||
inputs:
|
||||
stencil-version:
|
||||
description: 'The NPM tag of @stencil/core to install.'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
run: npm install
|
||||
working-directory: ./core
|
||||
shell: bash
|
||||
- name: Install Stencil ${{ inputs.stencil-version }}
|
||||
run: npm install @stencil/core@${{ inputs.stencil-version }}
|
||||
shell: bash
|
||||
working-directory: ./core
|
||||
if: ${{ inputs.stencil-version != '' }}
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
|
||||
@@ -8,7 +8,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
|
||||
@@ -8,7 +8,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
|
||||
@@ -8,7 +8,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
|
||||
@@ -9,7 +9,7 @@ runs:
|
||||
steps:
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.x
|
||||
node-version: 16.x
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: ./artifacts
|
||||
@@ -34,24 +34,8 @@ runs:
|
||||
run: |
|
||||
git config user.name ionitron
|
||||
git config user.email hi@ionicframework.com
|
||||
|
||||
# This adds an empty entry for new
|
||||
# screenshot files so we can track them with
|
||||
# git diff
|
||||
git add src/\*.png --force -N
|
||||
|
||||
if git diff --exit-code; then
|
||||
echo -e "\033[1;31m⚠️ Error: No new screenshots generated ⚠️\033[0m"
|
||||
echo -e "\033[1;31mThis means that there were zero visual diffs when running screenshot tests.\033[0m"
|
||||
echo -e "\033[1;31mMake sure you have pushed any code changes that would result in visual diffs.\033[0m"
|
||||
exit 1
|
||||
else
|
||||
# This actually adds the contents
|
||||
# of the screenshots (including new ones)
|
||||
git add src/\*.png --force
|
||||
git commit -m "chore(): add updated snapshots"
|
||||
git push
|
||||
fi
|
||||
|
||||
git add src/\*.png --force
|
||||
git commit -m "chore(): add updated snapshots"
|
||||
git push
|
||||
shell: bash
|
||||
working-directory: ./core
|
||||
|
||||
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -4,12 +4,6 @@ on:
|
||||
pull_request:
|
||||
branches: [ '**' ]
|
||||
merge_group:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ionicons_npm_release_tag:
|
||||
required: false
|
||||
type: string
|
||||
description: What version of ionicons should be pulled from NPM? Use this if you want to test a custom version of Ionicons with Ionic.
|
||||
|
||||
# When pushing a new commit we should
|
||||
# cancel the previous test run to not
|
||||
@@ -24,8 +18,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/build-core
|
||||
with:
|
||||
ionicons-version: ${{ inputs.ionicons_npm_release_tag }}
|
||||
|
||||
test-core-clean-build:
|
||||
needs: [build-core]
|
||||
@@ -130,7 +122,7 @@ jobs:
|
||||
- uses: ./.github/workflows/actions/build-angular
|
||||
|
||||
build-angular-server:
|
||||
needs: [build-core]
|
||||
needs: [build-angular]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -140,7 +132,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
apps: [ng14, ng15, ng16, ng17]
|
||||
apps: [ng14, ng15, ng16]
|
||||
needs: [build-angular, build-angular-server]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
14
.github/workflows/release-ionic.yml
vendored
14
.github/workflows/release-ionic.yml
vendored
@@ -81,15 +81,15 @@ jobs:
|
||||
tag: ${{ inputs.tag }}
|
||||
version: ${{ inputs.version }}
|
||||
preid: ${{ inputs.preid }}
|
||||
working-directory: 'packages/angular'
|
||||
working-directory: 'angular'
|
||||
folder: './dist'
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
- name: Cache Built @ionic/angular
|
||||
uses: ./.github/workflows/actions/upload-archive
|
||||
with:
|
||||
name: ionic-angular
|
||||
output: packages/angular/AngularBuild.zip
|
||||
paths: packages/angular/dist
|
||||
output: ./angular/AngularBuild.zip
|
||||
paths: ./angular/dist
|
||||
|
||||
release-react:
|
||||
needs: [release-core]
|
||||
@@ -144,7 +144,7 @@ jobs:
|
||||
paths: packages/vue/dist packages/vue/css
|
||||
|
||||
release-angular-server:
|
||||
needs: [release-core]
|
||||
needs: [release-angular]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -154,6 +154,12 @@ jobs:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- name: Restore @ionic/angular built cache
|
||||
uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-angular
|
||||
path: ./angular
|
||||
filename: AngularBuild.zip
|
||||
- uses: ./.github/workflows/actions/publish-npm
|
||||
with:
|
||||
scope: '@ionic/angular-server'
|
||||
|
||||
13
.github/workflows/stencil-nightly.yml
vendored
13
.github/workflows/stencil-nightly.yml
vendored
@@ -8,12 +8,7 @@ on:
|
||||
# at 6:00 UTC (6:00 am UTC)
|
||||
- cron: '00 06 * * 1-5'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
npm_release_tag:
|
||||
required: true
|
||||
type: string
|
||||
description: What version should be pulled from NPM?
|
||||
default: nightly
|
||||
# allows for manual invocations in the GitHub UI
|
||||
|
||||
# When pushing a new commit we should
|
||||
# cancel the previous test run to not
|
||||
@@ -29,7 +24,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/build-core-stencil-prerelease
|
||||
with:
|
||||
stencil-version: ${{ inputs.npm_release_tag || 'nightly' }}
|
||||
stencil-version: nightly
|
||||
|
||||
test-core-clean-build:
|
||||
needs: [build-core-with-stencil-nightly]
|
||||
@@ -51,8 +46,6 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/test-core-spec
|
||||
with:
|
||||
stencil-version: ${{ inputs.npm_release_tag || 'nightly' }}
|
||||
|
||||
test-core-screenshot:
|
||||
strategy:
|
||||
@@ -140,7 +133,7 @@ jobs:
|
||||
- uses: ./.github/workflows/actions/build-angular
|
||||
|
||||
build-angular-server:
|
||||
needs: [build-core-with-stencil-nightly]
|
||||
needs: [build-angular]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
220
.github/workflows/stencil-v4.yml
vendored
Normal file
220
.github/workflows/stencil-v4.yml
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
# This workflow is intended to run against the `HEAD` of Stencil's primary branch.
|
||||
# See https://github.com/ionic-team/stencil for contents of the repository
|
||||
name: 'Stencil v4 Build'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run every Monday-Friday
|
||||
# at 6:00 UTC (6:00 am UTC)
|
||||
- cron: '00 06 * * 1-5'
|
||||
workflow_dispatch:
|
||||
# allows for manual invocations in the GitHub UI
|
||||
|
||||
# When pushing a new commit we should
|
||||
# cancel the previous test run to not
|
||||
# consume more runners than we need to.
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build-core-with-stencil-v4:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/build-core-stencil-prerelease
|
||||
with:
|
||||
stencil-version: v4-next
|
||||
|
||||
test-core-clean-build:
|
||||
needs: [build-core-with-stencil-v4]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/test-core-clean-build
|
||||
|
||||
test-core-lint:
|
||||
needs: [build-core-with-stencil-v4]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/test-core-lint
|
||||
|
||||
test-core-spec:
|
||||
needs: [build-core-with-stencil-v4]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/test-core-spec
|
||||
|
||||
test-core-screenshot:
|
||||
strategy:
|
||||
# This ensures that all screenshot shard
|
||||
# failures are reported so the dev can
|
||||
# review everything at once.
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Divide the tests into n buckets
|
||||
# and run those buckets in parallel.
|
||||
# To increase the number of shards,
|
||||
# add new items to the shard array
|
||||
# and change the value of totalShards
|
||||
# to be the length of the shard array.
|
||||
shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
|
||||
totalShards: [20]
|
||||
needs: [build-core-with-stencil-v4]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/test-core-screenshot
|
||||
with:
|
||||
shard: ${{ matrix.shard }}
|
||||
totalShards: ${{ matrix.totalShards }}
|
||||
|
||||
# Screenshots are required to pass
|
||||
# in order for the branch to be merge
|
||||
# eligible. However, the screenshot tests
|
||||
# are run on n runners where n can change
|
||||
# over time. The verify-screenshots step allows
|
||||
# us to have a required status check for screenshot
|
||||
# results without having to manually add each
|
||||
# matrix run in the branch protection rules
|
||||
# Source: https://github.community/t/status-check-for-a-matrix-jobs/127354
|
||||
verify-screenshots:
|
||||
if: ${{ always() }}
|
||||
needs: test-core-screenshot
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check build matrix status
|
||||
if: ${{ needs.test-core-screenshot.result != 'success' }}
|
||||
run: exit 1
|
||||
|
||||
build-vue:
|
||||
needs: [build-core-with-stencil-v4]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/build-vue
|
||||
|
||||
build-vue-router:
|
||||
needs: [build-vue]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/build-vue-router
|
||||
|
||||
test-vue-e2e:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
apps: [vue3]
|
||||
needs: [build-vue, build-vue-router]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/test-vue-e2e
|
||||
with:
|
||||
app: ${{ matrix.apps }}
|
||||
|
||||
verify-test-vue-e2e:
|
||||
if: ${{ always() }}
|
||||
needs: test-vue-e2e
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check build matrix status
|
||||
if: ${{ needs.test-vue-e2e.result != 'success' }}
|
||||
run: exit 1
|
||||
|
||||
build-angular:
|
||||
needs: [build-core-with-stencil-v4]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/build-angular
|
||||
|
||||
build-angular-server:
|
||||
needs: [build-angular]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/build-angular-server
|
||||
|
||||
test-angular-e2e:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
apps: [ng12, ng13, ng14, ng15]
|
||||
needs: [build-angular, build-angular-server]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/test-angular-e2e
|
||||
with:
|
||||
app: ${{ matrix.apps }}
|
||||
|
||||
verify-test-angular-e2e:
|
||||
if: ${{ always() }}
|
||||
needs: test-angular-e2e
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check build matrix status
|
||||
if: ${{ needs.test-angular-e2e.result != 'success' }}
|
||||
run: exit 1
|
||||
|
||||
build-react:
|
||||
needs: [build-core-with-stencil-v4]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/build-react
|
||||
|
||||
build-react-router:
|
||||
needs: [build-react]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/build-react-router
|
||||
|
||||
test-react-router-e2e:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
apps: [reactrouter5]
|
||||
needs: [build-react, build-react-router]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/test-react-router-e2e
|
||||
with:
|
||||
app: ${{ matrix.apps }}
|
||||
|
||||
verify-test-react-router-e2e:
|
||||
if: ${{ always() }}
|
||||
needs: test-react-router-e2e
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check build matrix status
|
||||
if: ${{ needs.test-react-router-e2e.result != 'success' }}
|
||||
run: exit 1
|
||||
|
||||
test-react-e2e:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
apps: [react17, react18]
|
||||
needs: [build-react, build-react-router]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/workflows/actions/test-react-e2e
|
||||
with:
|
||||
app: ${{ matrix.apps }}
|
||||
|
||||
verify-test-react-e2e:
|
||||
if: ${{ always() }}
|
||||
needs: test-react-e2e
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check build matrix status
|
||||
if: ${{ needs.test-react-e2e.result != 'success' }}
|
||||
run: exit 1
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -71,8 +71,8 @@ core/playwright-report/
|
||||
core/**/*-snapshots
|
||||
|
||||
# angular
|
||||
packages/angular/css/
|
||||
packages/angular/test/build/
|
||||
angular/css/
|
||||
angular/test/build/
|
||||
.angular/
|
||||
|
||||
# vue
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"enabled": false
|
||||
},
|
||||
"pullRequests": {
|
||||
"enabled": false
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
447
CHANGELOG.md
447
CHANGELOG.md
@@ -3,453 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.5.7](https://github.com/ionic-team/ionic-framework/compare/v7.5.6...v7.5.7) (2023-11-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** date inputs render correctly in mobile safari ([#28495](https://github.com/ionic-team/ionic-framework/issues/28495)) ([b833f0e](https://github.com/ionic-team/ionic-framework/commit/b833f0e826ddd261230e2e29b70e2dc884d8cb04)), closes [#28494](https://github.com/ionic-team/ionic-framework/issues/28494)
|
||||
* **datetime:** allow disabling datetime with prefer-wheel ([#28511](https://github.com/ionic-team/ionic-framework/issues/28511)) ([01130e1](https://github.com/ionic-team/ionic-framework/commit/01130e12e1d73bbf558da9d4dffd7122822ff39c))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.6](https://github.com/ionic-team/ionic-framework/compare/v7.5.5...v7.5.6) (2023-11-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** match MD spec on tablet ([#28501](https://github.com/ionic-team/ionic-framework/issues/28501)) ([6a2be9f](https://github.com/ionic-team/ionic-framework/commit/6a2be9fa3c12a893d98dc139a1575a6e7e3c7c26)), closes [#23977](https://github.com/ionic-team/ionic-framework/issues/23977)
|
||||
* **angular:** ng add @ionic/angular in standalone projects ([#28523](https://github.com/ionic-team/ionic-framework/issues/28523)) ([c07312e](https://github.com/ionic-team/ionic-framework/commit/c07312e5ed931f6f825ccf083c9dead9fa815843)), closes [#28514](https://github.com/ionic-team/ionic-framework/issues/28514)
|
||||
* **angular:** overlays are defined when using standalone controllers ([#28560](https://github.com/ionic-team/ionic-framework/issues/28560)) ([9453132](https://github.com/ionic-team/ionic-framework/commit/9453132aa8952b4adfa1326e61138b329e254f76)), closes [#28385](https://github.com/ionic-team/ionic-framework/issues/28385)
|
||||
* **datetime:** updating value with min scrolls to new value ([#28549](https://github.com/ionic-team/ionic-framework/issues/28549)) ([388d19e](https://github.com/ionic-team/ionic-framework/commit/388d19e04f83f85abd4602adb04cc71ac575764a)), closes [#28548](https://github.com/ionic-team/ionic-framework/issues/28548)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.5](https://github.com/ionic-team/ionic-framework/compare/v7.5.4...v7.5.5) (2023-11-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **accordion-group:** correct accordion is open on load ([#28510](https://github.com/ionic-team/ionic-framework/issues/28510)) ([a000dd2](https://github.com/ionic-team/ionic-framework/commit/a000dd2c0b65be8ab5b2ad19f2748fbca13d5085)), closes [#28506](https://github.com/ionic-team/ionic-framework/issues/28506)
|
||||
* **action-sheet:** adjust height for safe area with scrollable options ([#28504](https://github.com/ionic-team/ionic-framework/issues/28504)) ([900267e](https://github.com/ionic-team/ionic-framework/commit/900267eb36c36f2af63435f6b46acca52b3bdab7)), closes [#27777](https://github.com/ionic-team/ionic-framework/issues/27777)
|
||||
* **header:** collapsible large title does not flicker when collapse prop not reflected ([#28472](https://github.com/ionic-team/ionic-framework/issues/28472)) ([8227b0e](https://github.com/ionic-team/ionic-framework/commit/8227b0ee6d5250e122a34a83c644f8a74fbbafd5)), closes [#28466](https://github.com/ionic-team/ionic-framework/issues/28466)
|
||||
* **item-divider:** apply safe area to proper side regardless of direction ([#28420](https://github.com/ionic-team/ionic-framework/issues/28420)) ([4513e0c](https://github.com/ionic-team/ionic-framework/commit/4513e0c6b066d4990800c707e1d97f69c8fcfb0c))
|
||||
* **radio-group:** emit value change on componentDidLoad ([#28488](https://github.com/ionic-team/ionic-framework/issues/28488)) ([73b8bfd](https://github.com/ionic-team/ionic-framework/commit/73b8bfde3f060490958c10f58d0f68de80cb957f)), closes [#28356](https://github.com/ionic-team/ionic-framework/issues/28356)
|
||||
* **searchbar:** cancel icon aligns with back button ([#28478](https://github.com/ionic-team/ionic-framework/issues/28478)) ([c053fd9](https://github.com/ionic-team/ionic-framework/commit/c053fd9c68d9b1add1335db80be962215946a0b1)), closes [#28468](https://github.com/ionic-team/ionic-framework/issues/28468)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.4](https://github.com/ionic-team/ionic-framework/compare/v7.5.3...v7.5.4) (2023-11-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **inputs:** remove invalid legacy warnings in input, textarea, and select ([#28484](https://github.com/ionic-team/ionic-framework/issues/28484)) ([c765dcb](https://github.com/ionic-team/ionic-framework/commit/c765dcbac4148762768d8c2bea9103e7d38c510b))
|
||||
* **item:** apply safe area to proper side regardless of direction ([#28403](https://github.com/ionic-team/ionic-framework/issues/28403)) ([ed040b0](https://github.com/ionic-team/ionic-framework/commit/ed040b09e9cbd4246864e690542132defc6a6578))
|
||||
* **list:** remove border from last item with item-sliding ([#28439](https://github.com/ionic-team/ionic-framework/issues/28439)) ([cafafcc](https://github.com/ionic-team/ionic-framework/commit/cafafcc9d166ef536dcb73edd522c8f2a0fb95b6)), closes [#28435](https://github.com/ionic-team/ionic-framework/issues/28435)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.3](https://github.com/ionic-team/ionic-framework/compare/v7.5.2...v7.5.3) (2023-11-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** long words wrap to next line ([#28408](https://github.com/ionic-team/ionic-framework/issues/28408)) ([34257d6](https://github.com/ionic-team/ionic-framework/commit/34257d681e9034b0a001aa45e17222f3aab5ed76)), closes [#28406](https://github.com/ionic-team/ionic-framework/issues/28406)
|
||||
* **angular:** inputs on standalone form controls are reactive ([#28434](https://github.com/ionic-team/ionic-framework/issues/28434)) ([3b6e631](https://github.com/ionic-team/ionic-framework/commit/3b6e6318bf94125b3f8305b4d072a5945ceb3730)), closes [#28431](https://github.com/ionic-team/ionic-framework/issues/28431)
|
||||
* **angular:** NavController works with nested outlets ([#28421](https://github.com/ionic-team/ionic-framework/issues/28421)) ([90acad1](https://github.com/ionic-team/ionic-framework/commit/90acad1837b117542830ec0083389a35c5b4ee76)), closes [#28417](https://github.com/ionic-team/ionic-framework/issues/28417)
|
||||
* **angular:** run platform subscriptions inside zone ([#28404](https://github.com/ionic-team/ionic-framework/issues/28404)) ([a4b303e](https://github.com/ionic-team/ionic-framework/commit/a4b303e1338a35756a9cf7f67508d24d2f8537a2)), closes [#19539](https://github.com/ionic-team/ionic-framework/issues/19539)
|
||||
* **angular:** standalone form components do not error when multiple are used ([#28423](https://github.com/ionic-team/ionic-framework/issues/28423)) ([89698b3](https://github.com/ionic-team/ionic-framework/commit/89698b338fb05cde427c98720c238d2365abdaa7)), closes [#28418](https://github.com/ionic-team/ionic-framework/issues/28418)
|
||||
* **datetime:** allow calendar navigation in readonly mode; disallow keyboard navigation when disabled ([#28336](https://github.com/ionic-team/ionic-framework/issues/28336)) ([f6a6877](https://github.com/ionic-team/ionic-framework/commit/f6a6877044a6d912a92aab00c3c78897da09415d)), closes [#28121](https://github.com/ionic-team/ionic-framework/issues/28121)
|
||||
* **input, textarea, select:** use consistent sizes ([#28390](https://github.com/ionic-team/ionic-framework/issues/28390)) ([b31ecbb](https://github.com/ionic-team/ionic-framework/commit/b31ecbbfe8deb87604686d752e92e672dd9b277a)), closes [#28388](https://github.com/ionic-team/ionic-framework/issues/28388)
|
||||
* **list-header:** apply safe area to proper side regardless of direction ([#28371](https://github.com/ionic-team/ionic-framework/issues/28371)) ([f99d530](https://github.com/ionic-team/ionic-framework/commit/f99d5305fb4b1607b42e34a0b7653d8e1b5bf23f))
|
||||
* **segment:** avoid scrolling webkit bug ([#28376](https://github.com/ionic-team/ionic-framework/issues/28376)) ([8e2f818](https://github.com/ionic-team/ionic-framework/commit/8e2f81867175e9980e6d072b0a4414baae571223)), closes [#28373](https://github.com/ionic-team/ionic-framework/issues/28373)
|
||||
* **tab-bar:** apply safe area to proper side regardless of direction ([#28372](https://github.com/ionic-team/ionic-framework/issues/28372)) ([d47b7e7](https://github.com/ionic-team/ionic-framework/commit/d47b7e750310ceb2f2c7ecfda8343923ff8d564a))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.2](https://github.com/ionic-team/ionic-framework/compare/v7.5.1...v7.5.2) (2023-10-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert, action-sheet:** show scrollbar for long list of options ([#28369](https://github.com/ionic-team/ionic-framework/issues/28369)) ([60f3d65](https://github.com/ionic-team/ionic-framework/commit/60f3d6579498ebad75c4f5163fca3947ac2dadff)), closes [#18487](https://github.com/ionic-team/ionic-framework/issues/18487)
|
||||
* **angular:** remove form control side effects ([#28359](https://github.com/ionic-team/ionic-framework/issues/28359)) ([82d6309](https://github.com/ionic-team/ionic-framework/commit/82d6309ef1675c0a6e767e87c23f166d84579d8f)), closes [#28358](https://github.com/ionic-team/ionic-framework/issues/28358)
|
||||
* **fab:** apply safe area in positioning to proper side regardless of direction ([#28377](https://github.com/ionic-team/ionic-framework/issues/28377)) ([331c08a](https://github.com/ionic-team/ionic-framework/commit/331c08aad542de158e53ed351705d4c396bb4e90))
|
||||
* **input, searchbar, textarea:** ensure nativeInput is always available ([#28362](https://github.com/ionic-team/ionic-framework/issues/28362)) ([2b015b2](https://github.com/ionic-team/ionic-framework/commit/2b015b22144e306444f2bf30ace0b5cc7e32a710)), closes [#28283](https://github.com/ionic-team/ionic-framework/issues/28283)
|
||||
* **menu:** menu no longer disappears with multiple split panes ([#28370](https://github.com/ionic-team/ionic-framework/issues/28370)) ([5a30082](https://github.com/ionic-team/ionic-framework/commit/5a30082546cb19eb98128ca9091b35094841d4f2)), closes [#18683](https://github.com/ionic-team/ionic-framework/issues/18683) [#15538](https://github.com/ionic-team/ionic-framework/issues/15538) [#22341](https://github.com/ionic-team/ionic-framework/issues/22341)
|
||||
* **rtl:** allow :host to use rtl() ([#28353](https://github.com/ionic-team/ionic-framework/issues/28353)) ([6b7d288](https://github.com/ionic-team/ionic-framework/commit/6b7d288536307fcb49231dca66ab938b389ea85e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.1](https://github.com/ionic-team/ionic-framework/compare/v7.5.0...v7.5.1) (2023-10-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** do not create duplicate menuController instances ([#28343](https://github.com/ionic-team/ionic-framework/issues/28343)) ([fa78676](https://github.com/ionic-team/ionic-framework/commit/fa78676d57eb80655ee9447ffa07dcfdae0c6b2a)), closes [#28337](https://github.com/ionic-team/ionic-framework/issues/28337)
|
||||
* **angular:** export missing lifecycle interfaces for standalone package ([#28346](https://github.com/ionic-team/ionic-framework/issues/28346)) ([dd93e0b](https://github.com/ionic-team/ionic-framework/commit/dd93e0b2689511f3145606f4dbb2c30dcf4c2950)), closes [/github.com/ionic-team/ionic-angular-standalone-codemods/pull/13/files/baa37ef1e3e8ba773b693db280542efba815482a#r1356414362](https://github.com//github.com/ionic-team/ionic-angular-standalone-codemods/pull/13/files/baa37ef1e3e8ba773b693db280542efba815482a/issues/r1356414362)
|
||||
* **react:** cleanup functions are execute for lifecycle hooks ([#28319](https://github.com/ionic-team/ionic-framework/issues/28319)) ([1ba9973](https://github.com/ionic-team/ionic-framework/commit/1ba9973857503c6e47cb225b77a5b89e0a9d2718)), closes [#28186](https://github.com/ionic-team/ionic-framework/issues/28186)
|
||||
* **react:** lifecycle events are removed on page unmount ([#28316](https://github.com/ionic-team/ionic-framework/issues/28316)) ([f14a59c](https://github.com/ionic-team/ionic-framework/commit/f14a59c5e0670ed7cc9ce1a73a087a5af13266e2))
|
||||
* **title:** large title transition supports dynamic font scaling ([#28290](https://github.com/ionic-team/ionic-framework/issues/28290)) ([fe47594](https://github.com/ionic-team/ionic-framework/commit/fe47594dc0bbb047f0bade144cf07b084fbeef5e)), closes [#28351](https://github.com/ionic-team/ionic-framework/issues/28351)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [7.5.0](https://github.com/ionic-team/ionic-framework/compare/v7.4.4...v7.5.0) (2023-10-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** stop Enter keypress for checkboxes ([#28279](https://github.com/ionic-team/ionic-framework/issues/28279)) ([72b3899](https://github.com/ionic-team/ionic-framework/commit/72b389993df4b0dc392262a106d7949e176b13af))
|
||||
* **select:** use correct aria-haspopup value ([#28265](https://github.com/ionic-team/ionic-framework/issues/28265)) ([01167fc](https://github.com/ionic-team/ionic-framework/commit/01167fc185db9bbb45b3a4086aff98008a76af2c))
|
||||
* **toast:** toast does not warn when positionAnchor is undefined ([#28312](https://github.com/ionic-team/ionic-framework/issues/28312)) ([c37b3d8](https://github.com/ionic-team/ionic-framework/commit/c37b3d8bf4b440506fdc96455a532c6316e5673d))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **a11y:** add dynamic font scaling ([#28314](https://github.com/ionic-team/ionic-framework/issues/28314)) ([f806781](https://github.com/ionic-team/ionic-framework/commit/f8067819eeb577db163bf3e0f95fc73064d62b8a)), closes [#24638](https://github.com/ionic-team/ionic-framework/issues/24638) [#18592](https://github.com/ionic-team/ionic-framework/issues/18592)
|
||||
* **angular, react, vue, core:** export openURL utility ([#28295](https://github.com/ionic-team/ionic-framework/issues/28295)) ([6da82aa](https://github.com/ionic-team/ionic-framework/commit/6da82aab816b28bfc174f7634ded1fc1e06502ab)), closes [#27911](https://github.com/ionic-team/ionic-framework/issues/27911)
|
||||
* **angular:** ship Ionic components as Angular standalone components ([#28311](https://github.com/ionic-team/ionic-framework/issues/28311)) ([57e2476](https://github.com/ionic-team/ionic-framework/commit/57e247637005b10f0d21d5ac5f5232bcb1908301))
|
||||
* **datetime:** add support for h11 and h24 hour formats ([#28219](https://github.com/ionic-team/ionic-framework/issues/28219)) ([597bc3f](https://github.com/ionic-team/ionic-framework/commit/597bc3f085c5ff1451c73d0cf4d7d664943e712f)), closes [#23750](https://github.com/ionic-team/ionic-framework/issues/23750)
|
||||
* **toast:** allow custom positioning relative to specific element ([#28248](https://github.com/ionic-team/ionic-framework/issues/28248)) ([897ff6f](https://github.com/ionic-team/ionic-framework/commit/897ff6f7493d8d7e4ab22c6ae59de066b43ce682)), closes [#17499](https://github.com/ionic-team/ionic-framework/issues/17499)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.4.4](https://github.com/ionic-team/ionic-framework/compare/v7.4.3...v7.4.4) (2023-10-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animation:** play method resolves when animation is stopped ([#28264](https://github.com/ionic-team/ionic-framework/issues/28264)) ([e6031fb](https://github.com/ionic-team/ionic-framework/commit/e6031fbef0698dac0a346cd6202c47f2abf54f95))
|
||||
* **checkbox, radio, toggle:** disabled elements are not interactive ([#28294](https://github.com/ionic-team/ionic-framework/issues/28294)) ([c70432e](https://github.com/ionic-team/ionic-framework/commit/c70432e6934bcf1d570e1f7cf671c52d2bb52a8b)), closes [#28293](https://github.com/ionic-team/ionic-framework/issues/28293)
|
||||
* **content:** fullscreen offset is computed correctly with tab bar ([#28245](https://github.com/ionic-team/ionic-framework/issues/28245)) ([7375dd6](https://github.com/ionic-team/ionic-framework/commit/7375dd6abafdf7457f23deb53ad5f016456a6af2)), closes [#21130](https://github.com/ionic-team/ionic-framework/issues/21130)
|
||||
* **core:** allow fullscreen scroll content to flow outside container for translucent tab bar ([#28246](https://github.com/ionic-team/ionic-framework/issues/28246)) ([b297529](https://github.com/ionic-team/ionic-framework/commit/b297529afc4b93a93f7eaecd31dd5a88a3de5f4e)), closes [#17676](https://github.com/ionic-team/ionic-framework/issues/17676)
|
||||
* **core:** swipe to go back gesture has priority over other horizontal swipe gestures ([#28304](https://github.com/ionic-team/ionic-framework/issues/28304)) ([d5f0c77](https://github.com/ionic-team/ionic-framework/commit/d5f0c776dfb5cb40b8119c596805dad3adb621e0)), closes [#28303](https://github.com/ionic-team/ionic-framework/issues/28303)
|
||||
* **header:** collapsible large title main header does not flicker on load ([#28277](https://github.com/ionic-team/ionic-framework/issues/28277)) ([3259da0](https://github.com/ionic-team/ionic-framework/commit/3259da0de181c8f82c38d9de13733213c77d398f)), closes [#27060](https://github.com/ionic-team/ionic-framework/issues/27060)
|
||||
* **menu:** do not error if disabled or swipeGesture is changed mid-animation ([#28268](https://github.com/ionic-team/ionic-framework/issues/28268)) ([a169044](https://github.com/ionic-team/ionic-framework/commit/a1690441e5bcee8176da32700de6f9e3fde9635e)), closes [#20092](https://github.com/ionic-team/ionic-framework/issues/20092) [#19676](https://github.com/ionic-team/ionic-framework/issues/19676) [#19000](https://github.com/ionic-team/ionic-framework/issues/19000)
|
||||
* **segment:** scroll to active segment-button on first load ([#28276](https://github.com/ionic-team/ionic-framework/issues/28276)) ([1167a93](https://github.com/ionic-team/ionic-framework/commit/1167a9325fb930b6c727bc26889f5488d9620062)), closes [#28096](https://github.com/ionic-team/ionic-framework/issues/28096)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.4.3](https://github.com/ionic-team/ionic-framework/compare/v7.4.2...v7.4.3) (2023-10-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **fab-button:** position is correct with custom sizes ([#28195](https://github.com/ionic-team/ionic-framework/issues/28195)) ([eb41b55](https://github.com/ionic-team/ionic-framework/commit/eb41b556b57c97139b9c36dc3e3be3711d8afaca)), closes [#22564](https://github.com/ionic-team/ionic-framework/issues/22564)
|
||||
* **range:** knob positions are correct on initial render with custom elements build ([#28257](https://github.com/ionic-team/ionic-framework/issues/28257)) ([ac2c8e6](https://github.com/ionic-team/ionic-framework/commit/ac2c8e6c22da4d0d8224def24ddef56ee9d26246)), closes [#25444](https://github.com/ionic-team/ionic-framework/issues/25444)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.4.2](https://github.com/ionic-team/ionic-framework/compare/v7.4.1...v7.4.2) (2023-09-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **react:** Nav unmounts component while invoking popTo or popToRoot ([#27821](https://github.com/ionic-team/ionic-framework/issues/27821)) ([0edcb2c](https://github.com/ionic-team/ionic-framework/commit/0edcb2cd85133ae8c304c53c37ca829e5fbad447)), closes [#27798](https://github.com/ionic-team/ionic-framework/issues/27798)
|
||||
* **title:** large title uses custom font on transition ([#28231](https://github.com/ionic-team/ionic-framework/issues/28231)) ([71a7af0](https://github.com/ionic-team/ionic-framework/commit/71a7af0f52fe62937b1dea1ca2739e78801a2a6d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.4.1](https://github.com/ionic-team/ionic-framework/compare/v7.4.0...v7.4.1) (2023-09-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **overlays:** correctly re-add root to accessibility tree ([#28183](https://github.com/ionic-team/ionic-framework/issues/28183)) ([81714d4](https://github.com/ionic-team/ionic-framework/commit/81714d45bd97f0ba91729959b60a0dc1d1d06533)), closes [#28180](https://github.com/ionic-team/ionic-framework/issues/28180)
|
||||
* **radio,toggle,checkbox,select:** padded space is clickable in items ([#28136](https://github.com/ionic-team/ionic-framework/issues/28136)) ([5b7e422](https://github.com/ionic-team/ionic-framework/commit/5b7e422dc0bfd4d58fb31f62715af47e62dabb57)), closes [#27169](https://github.com/ionic-team/ionic-framework/issues/27169)
|
||||
* **range:** knob is not cut off in item with modern syntax ([#28199](https://github.com/ionic-team/ionic-framework/issues/28199)) ([0104d89](https://github.com/ionic-team/ionic-framework/commit/0104d899270d73e16f2850a5fd7d2ba25a9e7ef0)), closes [#27199](https://github.com/ionic-team/ionic-framework/issues/27199)
|
||||
* **scroll-assist:** improve input scroll accuracy with native resizing ([#28169](https://github.com/ionic-team/ionic-framework/issues/28169)) ([b5c736f](https://github.com/ionic-team/ionic-framework/commit/b5c736f5ac829efebedf3256ddf77ab3daa7a5f6)), closes [#22940](https://github.com/ionic-team/ionic-framework/issues/22940)
|
||||
* **scroll-assist:** re-run when keyboard changes ([#28174](https://github.com/ionic-team/ionic-framework/issues/28174)) ([3f06da4](https://github.com/ionic-team/ionic-framework/commit/3f06da4cfc0d59c658e17e09ccb1ea28a29339f9)), closes [#22940](https://github.com/ionic-team/ionic-framework/issues/22940)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [7.4.0](https://github.com/ionic-team/ionic-framework/compare/v7.3.4...v7.4.0) (2023-09-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** scroll to newly selected date when value changes ([#27806](https://github.com/ionic-team/ionic-framework/issues/27806)) ([32244fb](https://github.com/ionic-team/ionic-framework/commit/32244fbdd1931e59a9e3cedd2b143c8ee7d01459)), closes [#26391](https://github.com/ionic-team/ionic-framework/issues/26391)
|
||||
* **many:** add correct scale to stacked labels ([#28163](https://github.com/ionic-team/ionic-framework/issues/28163)) ([8cb8786](https://github.com/ionic-team/ionic-framework/commit/8cb878669e53bad25bbe2787826b6d02d292848a))
|
||||
* **range:** add correct margin in item ([#28161](https://github.com/ionic-team/ionic-framework/issues/28161)) ([1d2b867](https://github.com/ionic-team/ionic-framework/commit/1d2b867f2207d366e355265b081bc9aabe31ce7e))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **checkbox, radio, toggle, range:** stacked labels for form controls ([#28075](https://github.com/ionic-team/ionic-framework/issues/28075)) ([e6c7bb6](https://github.com/ionic-team/ionic-framework/commit/e6c7bb60e7e61c965f45e2bf3e3bd16f5125ad56))
|
||||
* **datetime:** add disabled part ([#28134](https://github.com/ionic-team/ionic-framework/issues/28134)) ([cd8d509](https://github.com/ionic-team/ionic-framework/commit/cd8d5091a133804ac97d0394354dcf7cd73d9355))
|
||||
* **datetime:** add parts for calendar day, active, and today ([#27641](https://github.com/ionic-team/ionic-framework/issues/27641)) ([79b005d](https://github.com/ionic-team/ionic-framework/commit/79b005da704c2ce481e1e3bc4d24cdba06a36d04)), closes [#25340](https://github.com/ionic-team/ionic-framework/issues/25340)
|
||||
* export TransitionOptions interface and getIonPageElement ([#28140](https://github.com/ionic-team/ionic-framework/issues/28140)) ([19f3bb2](https://github.com/ionic-team/ionic-framework/commit/19f3bb23fd5587848fc41a744ca46ef5985c04d2)), closes [#28137](https://github.com/ionic-team/ionic-framework/issues/28137)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.3.4](https://github.com/ionic-team/ionic-framework/compare/v7.3.3...v7.3.4) (2023-09-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **menu:** remove app dir from safe area padding ([#28123](https://github.com/ionic-team/ionic-framework/issues/28123)) ([e0542a7](https://github.com/ionic-team/ionic-framework/commit/e0542a7867871fa45a7fe6a4986e7de633063b4b))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.3.3](https://github.com/ionic-team/ionic-framework/compare/v7.3.2...v7.3.3) (2023-09-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modal:** swipe to dismiss resets status bar style ([#28110](https://github.com/ionic-team/ionic-framework/issues/28110)) ([176585f](https://github.com/ionic-team/ionic-framework/commit/176585f446b04a6a0cedab2e09417637dbfc78ee)), closes [#28105](https://github.com/ionic-team/ionic-framework/issues/28105)
|
||||
* **overlays:** prevent overlays from getting stuck open ([#28069](https://github.com/ionic-team/ionic-framework/issues/28069)) ([584e9d3](https://github.com/ionic-team/ionic-framework/commit/584e9d3be220343451c2d4b9bf90658ecd530de1)), closes [#27200](https://github.com/ionic-team/ionic-framework/issues/27200)
|
||||
* **popover:** dynamic width popover is positioned correctly ([#28072](https://github.com/ionic-team/ionic-framework/issues/28072)) ([2a80eb6](https://github.com/ionic-team/ionic-framework/commit/2a80eb6bd0b16a9dab9bea600bb7f935d25c0e1b)), closes [#27190](https://github.com/ionic-team/ionic-framework/issues/27190) [#24780](https://github.com/ionic-team/ionic-framework/issues/24780)
|
||||
* **react:** overlay content is shown with hook ([#28109](https://github.com/ionic-team/ionic-framework/issues/28109)) ([7b551fd](https://github.com/ionic-team/ionic-framework/commit/7b551fd54b9e16a2538e5b82a13d72b3007fa045)), closes [#28102](https://github.com/ionic-team/ionic-framework/issues/28102)
|
||||
* **textarea:** cols property is respected ([#28081](https://github.com/ionic-team/ionic-framework/issues/28081)) ([6d4eabc](https://github.com/ionic-team/ionic-framework/commit/6d4eabcc1046c28c1abf69a8bda3e06f80cf3f8f)), closes [#22142](https://github.com/ionic-team/ionic-framework/issues/22142)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.3.2](https://github.com/ionic-team/ionic-framework/compare/v7.3.1...v7.3.2) (2023-08-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** gracefully handle invalid min/max ([#28054](https://github.com/ionic-team/ionic-framework/issues/28054)) ([01fc9b4](https://github.com/ionic-team/ionic-framework/commit/01fc9b45116f7ad6ddc56c7fb1535dec798c2b3a)), closes [#28041](https://github.com/ionic-team/ionic-framework/issues/28041)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.3.1](https://github.com/ionic-team/ionic-framework/compare/v7.3.0...v7.3.1) (2023-08-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** ionTabsWillChange is fired before tab activation ([#27991](https://github.com/ionic-team/ionic-framework/issues/27991)) ([bbfb8f8](https://github.com/ionic-team/ionic-framework/commit/bbfb8f81a61475d7e73b63743db5d6a0cd979d21)), closes [#27212](https://github.com/ionic-team/ionic-framework/issues/27212)
|
||||
* **input, textarea:** clearOnEdit does not clear when pressing Tab ([#28005](https://github.com/ionic-team/ionic-framework/issues/28005)) ([444acc1](https://github.com/ionic-team/ionic-framework/commit/444acc1f1bca348b62dfb398067cc087529f67f1)), closes [#27746](https://github.com/ionic-team/ionic-framework/issues/27746)
|
||||
* **react:** avoid multiple invocations of onDidDismiss and onWillPresent ([#28020](https://github.com/ionic-team/ionic-framework/issues/28020)) ([0ac3df3](https://github.com/ionic-team/ionic-framework/commit/0ac3df3f378bdefc3a927adc798ebd9ec7a54fee)), closes [#28010](https://github.com/ionic-team/ionic-framework/issues/28010)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [7.3.0](https://github.com/ionic-team/ionic-framework/compare/v7.2.4...v7.3.0) (2023-08-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alert:** radio and checkbox labels wrap to next line ([#27898](https://github.com/ionic-team/ionic-framework/issues/27898)) ([0d3127a](https://github.com/ionic-team/ionic-framework/commit/0d3127ad09df3c914a8c254f14931de5ca3beb31)), closes [#17269](https://github.com/ionic-team/ionic-framework/issues/17269)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **action-sheet:** add htmlAttributes property for passing attributes to buttons ([#27863](https://github.com/ionic-team/ionic-framework/issues/27863)) ([5ce4ec0](https://github.com/ionic-team/ionic-framework/commit/5ce4ec0439e4f31aba31062fd8af4a2ad792a54f))
|
||||
* **alert:** add htmlAttributes property for passing attributes to buttons ([#27862](https://github.com/ionic-team/ionic-framework/issues/27862)) ([06be0e5](https://github.com/ionic-team/ionic-framework/commit/06be0e511164ebdaa6af9a3747d0585260c030a9))
|
||||
* **toast:** add htmlAttributes property for passing attributes to buttons ([#27855](https://github.com/ionic-team/ionic-framework/issues/27855)) ([9a68588](https://github.com/ionic-team/ionic-framework/commit/9a685882b7085d911ff09eedacc367629e32348a))
|
||||
* **toast:** add shadow part for cancel button ([#27921](https://github.com/ionic-team/ionic-framework/issues/27921)) ([e9faf54](https://github.com/ionic-team/ionic-framework/commit/e9faf54d0a7409521706ce9c8b0d26f3fbe9ba41)), closes [#27920](https://github.com/ionic-team/ionic-framework/issues/27920)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.2.4](https://github.com/ionic-team/ionic-framework/compare/v7.2.3...v7.2.4) (2023-08-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **tap-click:** do not error in document-less environment ([#27972](https://github.com/ionic-team/ionic-framework/issues/27972)) ([28bd4ba](https://github.com/ionic-team/ionic-framework/commit/28bd4ba720bb77d5f5c48cd7a45e0015daddc9dd))
|
||||
* **title:** large title aligns with ios spec ([#27969](https://github.com/ionic-team/ionic-framework/issues/27969)) ([8fa12fc](https://github.com/ionic-team/ionic-framework/commit/8fa12fc88857df27a1ca11249f0085e100fe1474)), closes [#27966](https://github.com/ionic-team/ionic-framework/issues/27966)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.2.3](https://github.com/ionic-team/ionic-framework/compare/v7.2.2...v7.2.3) (2023-08-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **button:** hidden button is added when form is set async ([#27955](https://github.com/ionic-team/ionic-framework/issues/27955)) ([e9fa300](https://github.com/ionic-team/ionic-framework/commit/e9fa30002bd5dec4f2f56a15c84eec1b3e794942)), closes [#27952](https://github.com/ionic-team/ionic-framework/issues/27952)
|
||||
* **datetime:** changing months work if partially visible ([#27917](https://github.com/ionic-team/ionic-framework/issues/27917)) ([eb19c28](https://github.com/ionic-team/ionic-framework/commit/eb19c289d6581639f6df7aff002bebdf2b27d31c)), closes [#27913](https://github.com/ionic-team/ionic-framework/issues/27913)
|
||||
* **item-sliding:** account for options added before watcher ([#27915](https://github.com/ionic-team/ionic-framework/issues/27915)) ([a0b3ef0](https://github.com/ionic-team/ionic-framework/commit/a0b3ef02af718e232246515bb873ad8c090fa55d)), closes [#27910](https://github.com/ionic-team/ionic-framework/issues/27910)
|
||||
* **many:** overlays present if isOpen is true on load ([#27933](https://github.com/ionic-team/ionic-framework/issues/27933)) ([a0e6ac6](https://github.com/ionic-team/ionic-framework/commit/a0e6ac6013552c5e3acdde33575d4aaf4d4c0bda)), closes [#27928](https://github.com/ionic-team/ionic-framework/issues/27928)
|
||||
* **modal:** setCurrentBreakpoint respects animated prop ([#27924](https://github.com/ionic-team/ionic-framework/issues/27924)) ([da55ab9](https://github.com/ionic-team/ionic-framework/commit/da55ab949ef1894738da5a6241176089b7a2b6e3)), closes [#27923](https://github.com/ionic-team/ionic-framework/issues/27923)
|
||||
* **nav:** improve reliability of swipe back gesture when quickly swiping back ([#27904](https://github.com/ionic-team/ionic-framework/issues/27904)) ([9500769](https://github.com/ionic-team/ionic-framework/commit/9500769f114d180613f0340b1a328b5e631b7188)), closes [#27893](https://github.com/ionic-team/ionic-framework/issues/27893)
|
||||
* **tab-button:** update event type interface on React ([#27950](https://github.com/ionic-team/ionic-framework/issues/27950)) ([1cf1eca](https://github.com/ionic-team/ionic-framework/commit/1cf1eca00239f3e98854466116e42f9a2e7b4590)), closes [#27949](https://github.com/ionic-team/ionic-framework/issues/27949)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.2.2](https://github.com/ionic-team/ionic-framework/compare/v7.2.1...v7.2.2) (2023-08-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime-button:** render correct text when passing partial date values ([#27816](https://github.com/ionic-team/ionic-framework/issues/27816)) ([bd1910b](https://github.com/ionic-team/ionic-framework/commit/bd1910ba69348877ad5f99d9db2b59d06693b91e)), closes [#27797](https://github.com/ionic-team/ionic-framework/issues/27797)
|
||||
* **input, textarea:** input does not block floating label ([#27870](https://github.com/ionic-team/ionic-framework/issues/27870)) ([f14c440](https://github.com/ionic-team/ionic-framework/commit/f14c440d6321ef9f168b272338e5cd21cab384ef)), closes [#27812](https://github.com/ionic-team/ionic-framework/issues/27812)
|
||||
* **item-options:** use correct safe area padding ([#27853](https://github.com/ionic-team/ionic-framework/issues/27853)) ([0b8f1bc](https://github.com/ionic-team/ionic-framework/commit/0b8f1bc7dd4170a2a8c9ed3aede173dd489b25ea))
|
||||
* **radio:** radios can be focused and are announced with group ([#27817](https://github.com/ionic-team/ionic-framework/issues/27817)) ([ba2f49b](https://github.com/ionic-team/ionic-framework/commit/ba2f49b8a460520d20ac198db800ea2d9e5b015f)), closes [#27438](https://github.com/ionic-team/ionic-framework/issues/27438)
|
||||
* **react, vue:** custom animations are used when going back ([#27895](https://github.com/ionic-team/ionic-framework/issues/27895)) ([824033f](https://github.com/ionic-team/ionic-framework/commit/824033f1d4b4a3e5d4c6a978a39e5bb1f33b5bb4)), closes [#27873](https://github.com/ionic-team/ionic-framework/issues/27873)
|
||||
* **select:** popover uses modern form syntax ([#27818](https://github.com/ionic-team/ionic-framework/issues/27818)) ([0c117cf](https://github.com/ionic-team/ionic-framework/commit/0c117cfe7f383b7c7837d27de5a6eee12ddd6c2f)), closes [#27071](https://github.com/ionic-team/ionic-framework/issues/27071) [#27786](https://github.com/ionic-team/ionic-framework/issues/27786)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.2.1](https://github.com/ionic-team/ionic-framework/compare/v7.2.0...v7.2.1) (2023-07-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **item-sliding:** buttons are not interactive on close ([#27829](https://github.com/ionic-team/ionic-framework/issues/27829)) ([6e4919c](https://github.com/ionic-team/ionic-framework/commit/6e4919caff90fc60988e5cc85ad7161844eb5b51)), closes [#22722](https://github.com/ionic-team/ionic-framework/issues/22722)
|
||||
* **modal:** body background is reset with inline card modals ([#27835](https://github.com/ionic-team/ionic-framework/issues/27835)) ([38626d9](https://github.com/ionic-team/ionic-framework/commit/38626d96809d1c6be523ea62a4fac1dec73ee891)), closes [#27830](https://github.com/ionic-team/ionic-framework/issues/27830)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [7.2.0](https://github.com/ionic-team/ionic-framework/compare/v7.1.4...v7.2.0) (2023-07-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **angular:** support binding routing data to component inputs ([#27694](https://github.com/ionic-team/ionic-framework/issues/27694)) ([90f4124](https://github.com/ionic-team/ionic-framework/commit/90f41243d9404caaad99076965b7cd649ddf7f33)), closes [#27476](https://github.com/ionic-team/ionic-framework/issues/27476)
|
||||
* **button:** allow button to increase in height when text wraps ([#27547](https://github.com/ionic-team/ionic-framework/issues/27547)) ([6fe716f](https://github.com/ionic-team/ionic-framework/commit/6fe716fd1320935632854e5d4f741b57801bda92))
|
||||
* **searchbar:** add name property ([#27737](https://github.com/ionic-team/ionic-framework/issues/27737)) ([7131037](https://github.com/ionic-team/ionic-framework/commit/71310372c94862342d607007ece127340df92a8c)), closes [#27675](https://github.com/ionic-team/ionic-framework/issues/27675)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.1.4](https://github.com/ionic-team/ionic-framework/compare/v7.1.3...v7.1.4) (2023-07-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** menu button is enabled with Angular Universal builds ([#27814](https://github.com/ionic-team/ionic-framework/issues/27814)) ([2cf1a0b](https://github.com/ionic-team/ionic-framework/commit/2cf1a0bcae7d766aa25951c53470876f9569906c)), closes [#27524](https://github.com/ionic-team/ionic-framework/issues/27524) [#22206](https://github.com/ionic-team/ionic-framework/issues/22206)
|
||||
* **button:** submit form when pressing enter key ([#27790](https://github.com/ionic-team/ionic-framework/issues/27790)) ([b78af75](https://github.com/ionic-team/ionic-framework/commit/b78af7598f19ca5e1b440ddc0091a62d86321066)), closes [#19368](https://github.com/ionic-team/ionic-framework/issues/19368)
|
||||
* **radio, checkbox, toggle:** add top and bottom margins when in ion-item ([#27788](https://github.com/ionic-team/ionic-framework/issues/27788)) ([35f0ec5](https://github.com/ionic-team/ionic-framework/commit/35f0ec581a55e0cb080f0793fb94d3e424c06d4d)), closes [#27498](https://github.com/ionic-team/ionic-framework/issues/27498)
|
||||
* safari no longer adjusts text in landscape ([#27787](https://github.com/ionic-team/ionic-framework/issues/27787)) ([66584b0](https://github.com/ionic-team/ionic-framework/commit/66584b03d0b33507170f954009998c72fb3f7755)), closes [#27782](https://github.com/ionic-team/ionic-framework/issues/27782)
|
||||
* **textarea:** stacked/floating textarea size is correct on safari ([#27766](https://github.com/ionic-team/ionic-framework/issues/27766)) ([2cb7013](https://github.com/ionic-team/ionic-framework/commit/2cb701395487c6a0304400f6b821659ae6def820)), closes [#27345](https://github.com/ionic-team/ionic-framework/issues/27345)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.1.3](https://github.com/ionic-team/ionic-framework/compare/v7.1.2...v7.1.3) (2023-07-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* avoid unresolved import warning on stencil apps ([#27765](https://github.com/ionic-team/ionic-framework/issues/27765)) ([2085025](https://github.com/ionic-team/ionic-framework/commit/2085025644f075e63d04bece56eca4f2beeadbb6)), closes [#27762](https://github.com/ionic-team/ionic-framework/issues/27762)
|
||||
* **overlays:** first button is not focused on backdrop tap ([#27774](https://github.com/ionic-team/ionic-framework/issues/27774)) ([82c568b](https://github.com/ionic-team/ionic-framework/commit/82c568b8c8e1e9934e1928452aa5216619290e7b)), closes [#27773](https://github.com/ionic-team/ionic-framework/issues/27773)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.1.2](https://github.com/ionic-team/ionic-framework/compare/v7.1.1...v7.1.2) (2023-07-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **back-button:** show correct background on focus + hover with ios ([#27723](https://github.com/ionic-team/ionic-framework/issues/27723)) ([db9a001](https://github.com/ionic-team/ionic-framework/commit/db9a0010df3c7fd0fcd0dbcd8c4ad3b30d022b5c)), closes [#27722](https://github.com/ionic-team/ionic-framework/issues/27722)
|
||||
* **nav:** root component is mounted with root params ([#27676](https://github.com/ionic-team/ionic-framework/issues/27676)) ([1f06be4](https://github.com/ionic-team/ionic-framework/commit/1f06be4a31965f2a949b4866a585aee6af0af29d)), closes [#27146](https://github.com/ionic-team/ionic-framework/issues/27146)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.1.1](https://github.com/ionic-team/ionic-framework/compare/v7.1.0...v7.1.1) (2023-06-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **textarea:** autogrow resizes correctly ([#27691](https://github.com/ionic-team/ionic-framework/issues/27691)) ([f263611](https://github.com/ionic-team/ionic-framework/commit/f263611260c465bfeefc2db7b1ea04bfa5b54303)), closes [#27688](https://github.com/ionic-team/ionic-framework/issues/27688)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [7.1.0](https://github.com/ionic-team/ionic-framework/compare/v7.0.14...v7.1.0) (2023-06-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** tabs supports conditional slot bindings ([#27582](https://github.com/ionic-team/ionic-framework/issues/27582)) ([d20bea5](https://github.com/ionic-team/ionic-framework/commit/d20bea561c362eacd250cdedbc9b79159eb2c95f)), closes [#19484](https://github.com/ionic-team/ionic-framework/issues/19484)
|
||||
* **datetime:** ascending order for years ([#27551](https://github.com/ionic-team/ionic-framework/issues/27551)) ([2098806](https://github.com/ionic-team/ionic-framework/commit/209880622a4600f88c4878e82975ad0492bd55db)), closes [#27422](https://github.com/ionic-team/ionic-framework/issues/27422)
|
||||
* import paths ([d10509f](https://github.com/ionic-team/ionic-framework/commit/d10509f84099515f62c895dcd736cc387e382bde))
|
||||
* **select:** hide notch cutout if no visible label provided ([#27649](https://github.com/ionic-team/ionic-framework/issues/27649)) ([606a892](https://github.com/ionic-team/ionic-framework/commit/606a892e400a531cac5c413dc7492a54ae0e1fea))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **datetime:** add part for month/year button ([#27618](https://github.com/ionic-team/ionic-framework/issues/27618)) ([d44422e](https://github.com/ionic-team/ionic-framework/commit/d44422e224374804010746a12f398d3c0d6a9f2c)), closes [#26596](https://github.com/ionic-team/ionic-framework/issues/26596)
|
||||
* **datetime:** add shadow parts and CSS variables for styling wheel pickers ([#27529](https://github.com/ionic-team/ionic-framework/issues/27529)) ([f2c1845](https://github.com/ionic-team/ionic-framework/commit/f2c1845fba11d8273331c601052f0f34457b6649)), closes [#25945](https://github.com/ionic-team/ionic-framework/issues/25945)
|
||||
* **input:** add experimental label slot ([#27650](https://github.com/ionic-team/ionic-framework/issues/27650)) ([a45395c](https://github.com/ionic-team/ionic-framework/commit/a45395cc02b2617b80e6c2389fa745e7c20540fc)), closes [#27061](https://github.com/ionic-team/ionic-framework/issues/27061)
|
||||
* **range:** add label prop ([#27408](https://github.com/ionic-team/ionic-framework/issues/27408)) ([368add2](https://github.com/ionic-team/ionic-framework/commit/368add2a5ca3820a1f9623c96d29bcccfa693fdc))
|
||||
* return if the pop on NavController was successful ([#27404](https://github.com/ionic-team/ionic-framework/issues/27404)) ([e80f0b2](https://github.com/ionic-team/ionic-framework/commit/e80f0b240968de0d642463e95a35adf8dbffd2e1)), closes [#27403](https://github.com/ionic-team/ionic-framework/issues/27403)
|
||||
* **segment, segment-button:** update segment value property to accept numbers ([#27222](https://github.com/ionic-team/ionic-framework/issues/27222)) ([ec95ae5](https://github.com/ionic-team/ionic-framework/commit/ec95ae5cd38e3d2b9ec9fdbc9e237fa1241f7a4e)), closes [#27221](https://github.com/ionic-team/ionic-framework/issues/27221)
|
||||
* **segment:** display segment as a grid and add an ellipsis to overflowing text in a segment button ([#27457](https://github.com/ionic-team/ionic-framework/issues/27457)) ([448e63f](https://github.com/ionic-team/ionic-framework/commit/448e63fef0aca603214cb357dec37e1db2a0f052)), closes [#16532](https://github.com/ionic-team/ionic-framework/issues/16532)
|
||||
* **select:** add label slot ([#27545](https://github.com/ionic-team/ionic-framework/issues/27545)) ([af92cb2](https://github.com/ionic-team/ionic-framework/commit/af92cb28c8819c88b40192b5dcbafedc1eb2064a)), closes [#26838](https://github.com/ionic-team/ionic-framework/issues/26838)
|
||||
* **select:** add props to customize toggle icons ([#27648](https://github.com/ionic-team/ionic-framework/issues/27648)) ([95e28b6](https://github.com/ionic-team/ionic-framework/commit/95e28b6629843af7dce62f20bc8e31adfb391990)), closes [#17248](https://github.com/ionic-team/ionic-framework/issues/17248)
|
||||
* **select:** expose container and label as CSS parts ([#27541](https://github.com/ionic-team/ionic-framework/issues/27541)) ([5c10f88](https://github.com/ionic-team/ionic-framework/commit/5c10f88b2eb4d869966ea9a6d1db34185cefe676)), closes [#27112](https://github.com/ionic-team/ionic-framework/issues/27112)
|
||||
* **textarea:** add experimental label slot ([#27677](https://github.com/ionic-team/ionic-framework/issues/27677)) ([8bcd9e8](https://github.com/ionic-team/ionic-framework/commit/8bcd9e8b35f252a4efaec7a7be7d69a70beefa9f)), closes [#27061](https://github.com/ionic-team/ionic-framework/issues/27061)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.0.14](https://github.com/ionic-team/ionic-framework/compare/v7.0.13...v7.0.14) (2023-06-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **keyboard:** keyboard events emit correctly when Capacitor is available but the Keyboard plugin is not ([#27655](https://github.com/ionic-team/ionic-framework/issues/27655)) ([7a38a00](https://github.com/ionic-team/ionic-framework/commit/7a38a006a94f1240d93102f2f42bbfc4d76a679e)), closes [#27654](https://github.com/ionic-team/ionic-framework/issues/27654)
|
||||
* **toast:** allow color for translucent toast ([#27652](https://github.com/ionic-team/ionic-framework/issues/27652)) ([d555375](https://github.com/ionic-team/ionic-framework/commit/d555375c146639b32e85c57a8cdd4d52313ef4cf)), closes [#27567](https://github.com/ionic-team/ionic-framework/issues/27567)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.0.13](https://github.com/ionic-team/ionic-framework/compare/v7.0.12...v7.0.13) (2023-06-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **react:** onDoubleClick fires on components ([#27611](https://github.com/ionic-team/ionic-framework/issues/27611)) ([3e191df](https://github.com/ionic-team/ionic-framework/commit/3e191df3dd43dcdd5a5f717166d4db9834340a2b)), closes [#21320](https://github.com/ionic-team/ionic-framework/issues/21320)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.0.12](https://github.com/ionic-team/ionic-framework/compare/v7.0.11...v7.0.12) (2023-06-08)
|
||||
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
| Project | Package | Version | Downloads| Links |
|
||||
| ------- | ------- | ------- | -------- |:-----:|
|
||||
| **Core** | [`@ionic/core`](https://www.npmjs.com/package/@ionic/core) | [](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) | [](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`](packages/angular/README.md)
|
||||
| **Angular** | [`@ionic/angular`](https://www.npmjs.com/package/@ionic/angular) | [](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) | [](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) | [](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)
|
||||
|
||||
|
||||
6
angular/.prettierignore
Normal file
6
angular/.prettierignore
Normal file
@@ -0,0 +1,6 @@
|
||||
dist
|
||||
scripts
|
||||
test
|
||||
src/directives/proxies.ts
|
||||
src/directives/proxies-list.ts
|
||||
src/directives/angular-component-lib/utils.ts
|
||||
@@ -3,291 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.5.7](https://github.com/ionic-team/ionic-framework/compare/v7.5.6...v7.5.7) (2023-11-29)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.6](https://github.com/ionic-team/ionic-framework/compare/v7.5.5...v7.5.6) (2023-11-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** ng add @ionic/angular in standalone projects ([#28523](https://github.com/ionic-team/ionic-framework/issues/28523)) ([c07312e](https://github.com/ionic-team/ionic-framework/commit/c07312e5ed931f6f825ccf083c9dead9fa815843)), closes [#28514](https://github.com/ionic-team/ionic-framework/issues/28514)
|
||||
* **angular:** overlays are defined when using standalone controllers ([#28560](https://github.com/ionic-team/ionic-framework/issues/28560)) ([9453132](https://github.com/ionic-team/ionic-framework/commit/9453132aa8952b4adfa1326e61138b329e254f76)), closes [#28385](https://github.com/ionic-team/ionic-framework/issues/28385)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.5](https://github.com/ionic-team/ionic-framework/compare/v7.5.4...v7.5.5) (2023-11-15)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.4](https://github.com/ionic-team/ionic-framework/compare/v7.5.3...v7.5.4) (2023-11-08)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.3](https://github.com/ionic-team/ionic-framework/compare/v7.5.2...v7.5.3) (2023-11-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** inputs on standalone form controls are reactive ([#28434](https://github.com/ionic-team/ionic-framework/issues/28434)) ([3b6e631](https://github.com/ionic-team/ionic-framework/commit/3b6e6318bf94125b3f8305b4d072a5945ceb3730)), closes [#28431](https://github.com/ionic-team/ionic-framework/issues/28431)
|
||||
* **angular:** NavController works with nested outlets ([#28421](https://github.com/ionic-team/ionic-framework/issues/28421)) ([90acad1](https://github.com/ionic-team/ionic-framework/commit/90acad1837b117542830ec0083389a35c5b4ee76)), closes [#28417](https://github.com/ionic-team/ionic-framework/issues/28417)
|
||||
* **angular:** run platform subscriptions inside zone ([#28404](https://github.com/ionic-team/ionic-framework/issues/28404)) ([a4b303e](https://github.com/ionic-team/ionic-framework/commit/a4b303e1338a35756a9cf7f67508d24d2f8537a2)), closes [#19539](https://github.com/ionic-team/ionic-framework/issues/19539)
|
||||
* **angular:** standalone form components do not error when multiple are used ([#28423](https://github.com/ionic-team/ionic-framework/issues/28423)) ([89698b3](https://github.com/ionic-team/ionic-framework/commit/89698b338fb05cde427c98720c238d2365abdaa7)), closes [#28418](https://github.com/ionic-team/ionic-framework/issues/28418)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.2](https://github.com/ionic-team/ionic-framework/compare/v7.5.1...v7.5.2) (2023-10-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** remove form control side effects ([#28359](https://github.com/ionic-team/ionic-framework/issues/28359)) ([82d6309](https://github.com/ionic-team/ionic-framework/commit/82d6309ef1675c0a6e767e87c23f166d84579d8f)), closes [#28358](https://github.com/ionic-team/ionic-framework/issues/28358)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.1](https://github.com/ionic-team/ionic-framework/compare/v7.5.0...v7.5.1) (2023-10-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** do not create duplicate menuController instances ([#28343](https://github.com/ionic-team/ionic-framework/issues/28343)) ([fa78676](https://github.com/ionic-team/ionic-framework/commit/fa78676d57eb80655ee9447ffa07dcfdae0c6b2a)), closes [#28337](https://github.com/ionic-team/ionic-framework/issues/28337)
|
||||
* **angular:** export missing lifecycle interfaces for standalone package ([#28346](https://github.com/ionic-team/ionic-framework/issues/28346)) ([dd93e0b](https://github.com/ionic-team/ionic-framework/commit/dd93e0b2689511f3145606f4dbb2c30dcf4c2950)), closes [/github.com/ionic-team/ionic-angular-standalone-codemods/pull/13/files/baa37ef1e3e8ba773b693db280542efba815482a#r1356414362](https://github.com//github.com/ionic-team/ionic-angular-standalone-codemods/pull/13/files/baa37ef1e3e8ba773b693db280542efba815482a/issues/r1356414362)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [7.5.0](https://github.com/ionic-team/ionic-framework/compare/v7.4.4...v7.5.0) (2023-10-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **angular, react, vue, core:** export openURL utility ([#28295](https://github.com/ionic-team/ionic-framework/issues/28295)) ([6da82aa](https://github.com/ionic-team/ionic-framework/commit/6da82aab816b28bfc174f7634ded1fc1e06502ab)), closes [#27911](https://github.com/ionic-team/ionic-framework/issues/27911)
|
||||
* **angular:** ship Ionic components as Angular standalone components ([#28311](https://github.com/ionic-team/ionic-framework/issues/28311)) ([57e2476](https://github.com/ionic-team/ionic-framework/commit/57e247637005b10f0d21d5ac5f5232bcb1908301))
|
||||
* **toast:** allow custom positioning relative to specific element ([#28248](https://github.com/ionic-team/ionic-framework/issues/28248)) ([897ff6f](https://github.com/ionic-team/ionic-framework/commit/897ff6f7493d8d7e4ab22c6ae59de066b43ce682)), closes [#17499](https://github.com/ionic-team/ionic-framework/issues/17499)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.4.4](https://github.com/ionic-team/ionic-framework/compare/v7.4.3...v7.4.4) (2023-10-11)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.4.3](https://github.com/ionic-team/ionic-framework/compare/v7.4.2...v7.4.3) (2023-10-04)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.4.2](https://github.com/ionic-team/ionic-framework/compare/v7.4.1...v7.4.2) (2023-09-27)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.4.1](https://github.com/ionic-team/ionic-framework/compare/v7.4.0...v7.4.1) (2023-09-20)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [7.4.0](https://github.com/ionic-team/ionic-framework/compare/v7.3.4...v7.4.0) (2023-09-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **checkbox, radio, toggle, range:** stacked labels for form controls ([#28075](https://github.com/ionic-team/ionic-framework/issues/28075)) ([e6c7bb6](https://github.com/ionic-team/ionic-framework/commit/e6c7bb60e7e61c965f45e2bf3e3bd16f5125ad56))
|
||||
* export TransitionOptions interface and getIonPageElement ([#28140](https://github.com/ionic-team/ionic-framework/issues/28140)) ([19f3bb2](https://github.com/ionic-team/ionic-framework/commit/19f3bb23fd5587848fc41a744ca46ef5985c04d2)), closes [#28137](https://github.com/ionic-team/ionic-framework/issues/28137)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.3.4](https://github.com/ionic-team/ionic-framework/compare/v7.3.3...v7.3.4) (2023-09-13)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.3.3](https://github.com/ionic-team/ionic-framework/compare/v7.3.2...v7.3.3) (2023-09-06)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.3.2](https://github.com/ionic-team/ionic-framework/compare/v7.3.1...v7.3.2) (2023-08-30)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.3.1](https://github.com/ionic-team/ionic-framework/compare/v7.3.0...v7.3.1) (2023-08-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** ionTabsWillChange is fired before tab activation ([#27991](https://github.com/ionic-team/ionic-framework/issues/27991)) ([bbfb8f8](https://github.com/ionic-team/ionic-framework/commit/bbfb8f81a61475d7e73b63743db5d6a0cd979d21)), closes [#27212](https://github.com/ionic-team/ionic-framework/issues/27212)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [7.3.0](https://github.com/ionic-team/ionic-framework/compare/v7.2.4...v7.3.0) (2023-08-16)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.2.4](https://github.com/ionic-team/ionic-framework/compare/v7.2.3...v7.2.4) (2023-08-16)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.2.3](https://github.com/ionic-team/ionic-framework/compare/v7.2.2...v7.2.3) (2023-08-09)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.2.2](https://github.com/ionic-team/ionic-framework/compare/v7.2.1...v7.2.2) (2023-08-02)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.2.1](https://github.com/ionic-team/ionic-framework/compare/v7.2.0...v7.2.1) (2023-07-26)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [7.2.0](https://github.com/ionic-team/ionic-framework/compare/v7.1.4...v7.2.0) (2023-07-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **angular:** support binding routing data to component inputs ([#27694](https://github.com/ionic-team/ionic-framework/issues/27694)) ([90f4124](https://github.com/ionic-team/ionic-framework/commit/90f41243d9404caaad99076965b7cd649ddf7f33)), closes [#27476](https://github.com/ionic-team/ionic-framework/issues/27476)
|
||||
* **searchbar:** add name property ([#27737](https://github.com/ionic-team/ionic-framework/issues/27737)) ([7131037](https://github.com/ionic-team/ionic-framework/commit/71310372c94862342d607007ece127340df92a8c)), closes [#27675](https://github.com/ionic-team/ionic-framework/issues/27675)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.1.4](https://github.com/ionic-team/ionic-framework/compare/v7.1.3...v7.1.4) (2023-07-19)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.1.3](https://github.com/ionic-team/ionic-framework/compare/v7.1.2...v7.1.3) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.1.2](https://github.com/ionic-team/ionic-framework/compare/v7.1.1...v7.1.2) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.1.1](https://github.com/ionic-team/ionic-framework/compare/v7.1.0...v7.1.1) (2023-06-26)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [7.1.0](https://github.com/ionic-team/ionic-framework/compare/v7.0.14...v7.1.0) (2023-06-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** tabs supports conditional slot bindings ([#27582](https://github.com/ionic-team/ionic-framework/issues/27582)) ([d20bea5](https://github.com/ionic-team/ionic-framework/commit/d20bea561c362eacd250cdedbc9b79159eb2c95f)), closes [#19484](https://github.com/ionic-team/ionic-framework/issues/19484)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **range:** add label prop ([#27408](https://github.com/ionic-team/ionic-framework/issues/27408)) ([368add2](https://github.com/ionic-team/ionic-framework/commit/368add2a5ca3820a1f9623c96d29bcccfa693fdc))
|
||||
* return if the pop on NavController was successful ([#27404](https://github.com/ionic-team/ionic-framework/issues/27404)) ([e80f0b2](https://github.com/ionic-team/ionic-framework/commit/e80f0b240968de0d642463e95a35adf8dbffd2e1)), closes [#27403](https://github.com/ionic-team/ionic-framework/issues/27403)
|
||||
* **segment, segment-button:** update segment value property to accept numbers ([#27222](https://github.com/ionic-team/ionic-framework/issues/27222)) ([ec95ae5](https://github.com/ionic-team/ionic-framework/commit/ec95ae5cd38e3d2b9ec9fdbc9e237fa1241f7a4e)), closes [#27221](https://github.com/ionic-team/ionic-framework/issues/27221)
|
||||
* **segment:** display segment as a grid and add an ellipsis to overflowing text in a segment button ([#27457](https://github.com/ionic-team/ionic-framework/issues/27457)) ([448e63f](https://github.com/ionic-team/ionic-framework/commit/448e63fef0aca603214cb357dec37e1db2a0f052)), closes [#16532](https://github.com/ionic-team/ionic-framework/issues/16532)
|
||||
* **select:** add props to customize toggle icons ([#27648](https://github.com/ionic-team/ionic-framework/issues/27648)) ([95e28b6](https://github.com/ionic-team/ionic-framework/commit/95e28b6629843af7dce62f20bc8e31adfb391990)), closes [#17248](https://github.com/ionic-team/ionic-framework/issues/17248)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.0.14](https://github.com/ionic-team/ionic-framework/compare/v7.0.13...v7.0.14) (2023-06-15)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.0.13](https://github.com/ionic-team/ionic-framework/compare/v7.0.12...v7.0.13) (2023-06-14)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.0.12](https://github.com/ionic-team/ionic-framework/compare/v7.0.11...v7.0.12) (2023-06-08)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
@@ -52,23 +52,3 @@ $ npx schematics @ionic/angular:ng-add
|
||||
|
||||
|
||||
You'll now be able to add ionic components to a vanilla Angular app setup.
|
||||
|
||||
## Project Structure
|
||||
|
||||
**common**
|
||||
|
||||
This is where logic that is shared between lazy loaded and standalone components live. For example, the lazy loaded IonPopover and standalone IonPopover components extend from a base IonPopover implementation that exists in this directory.
|
||||
|
||||
**Note:** This directory exposes internal APIs and is only accessed in the `standalone` and `src` submodules. Ionic developers should never import directly from `@ionic/angular/common`. Instead, they should import from `@ionic/angular` or `@ionic/angular/standalone`.
|
||||
|
||||
**standalone**
|
||||
|
||||
This is where the standalone component implementations live. It was added as a separate entry point to avoid any lazy loaded logic from accidentally being pulled in to the final build. Having a separate directory allows the lazy loaded implementation to remain accessible from `@ionic/angular` for backwards compatibility.
|
||||
|
||||
Ionic developers can access this by importing from `@ionic/angular/standalone`.
|
||||
|
||||
**src**
|
||||
|
||||
This is where the lazy loaded component implementations live.
|
||||
|
||||
Ionic developers can access this by importing from `@ionic/angular`.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "7.5.7",
|
||||
"version": "7.0.12",
|
||||
"description": "Angular specific wrappers for @ionic/core",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -28,7 +28,6 @@
|
||||
"build.core": "node scripts/build-core.js",
|
||||
"build.link": "npm run build && node scripts/link-copy.js",
|
||||
"build.ng": "ng-packagr -p ng-package.json -c tsconfig.json",
|
||||
"build.watch": "npm run build.ng -- --watch",
|
||||
"clean": "node scripts/clean.js",
|
||||
"clean-generated": "node ./scripts/clean-generated.js",
|
||||
"lint": "npm run eslint && npm run prettier -- --write --cache",
|
||||
@@ -48,7 +47,7 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ionic/core": "^7.5.7",
|
||||
"@ionic/core": "^7.0.12",
|
||||
"ionicons": "^7.0.0",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"tslib": "^2.3.0"
|
||||
@@ -61,12 +60,11 @@
|
||||
"zone.js": ">=0.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/core": "^17.0.0",
|
||||
"@angular-devkit/schematics": "^17.0.0",
|
||||
"@angular-devkit/core": "^14.0.0",
|
||||
"@angular-devkit/schematics": "^14.0.0",
|
||||
"@angular-eslint/eslint-plugin": "^14.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "^14.0.0",
|
||||
"@angular-eslint/template-parser": "^14.0.0",
|
||||
"@angular/cli": "^14.0.0",
|
||||
"@angular/common": "^14.0.0",
|
||||
"@angular/compiler": "^14.0.0",
|
||||
"@angular/compiler-cli": "^14.0.0",
|
||||
@@ -77,7 +75,7 @@
|
||||
"@angular/router": "^14.0.0",
|
||||
"@ionic/eslint-config": "^0.3.0",
|
||||
"@ionic/prettier-config": "^2.0.0",
|
||||
"@schematics/angular": "^17.0.0",
|
||||
"@schematics/angular": "^14.0.0",
|
||||
"@types/node": "12.12.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
@@ -5,7 +5,7 @@ const spawn = require('child_process').spawn;
|
||||
const typescriptPath = path.join(__dirname, '..', 'node_modules', '.bin');
|
||||
|
||||
function copyCSS() {
|
||||
const src = path.join(__dirname, '..', '..', '..', 'core', 'css');
|
||||
const src = path.join(__dirname, '..', '..', 'core', 'css');
|
||||
const dst = path.join(__dirname, '..','dist', 'css');
|
||||
|
||||
fs.removeSync(dst);
|
||||
@@ -6,7 +6,7 @@ set -e
|
||||
rm -f *.tgz
|
||||
|
||||
# Pack @ionic/core
|
||||
npm pack ../../core
|
||||
npm pack ../core
|
||||
|
||||
# Install Dependencies
|
||||
npm install *.tgz --no-save
|
||||
@@ -1,9 +1,11 @@
|
||||
import { NgZone } from '@angular/core';
|
||||
import type { Config, IonicWindow } from '@ionic/angular/common';
|
||||
import { raf } from '@ionic/angular/common';
|
||||
import { setupConfig } from '@ionic/core';
|
||||
import { applyPolyfills, defineCustomElements } from '@ionic/core/loader';
|
||||
|
||||
import { Config } from './providers/config';
|
||||
import { IonicWindow } from './types/interfaces';
|
||||
import { raf } from './util/util';
|
||||
|
||||
// TODO(FW-2827): types
|
||||
|
||||
export const appInitialize = (config: Config, doc: Document, zone: NgZone) => {
|
||||
@@ -12,14 +12,6 @@ export const proxyInputs = (Cmp: any, inputs: string[]) => {
|
||||
set(val: any) {
|
||||
this.z.runOutsideAngular(() => (this.el[item] = val));
|
||||
},
|
||||
/**
|
||||
* In the event that proxyInputs is called
|
||||
* multiple times re-defining these inputs
|
||||
* will cause an error to be thrown. As a result
|
||||
* we set configurable: true to indicate these
|
||||
* properties can be changed.
|
||||
*/
|
||||
configurable: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Directive, HostListener, ElementRef, Injector } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor, setIonicClasses } from '@ionic/angular/common';
|
||||
|
||||
import { ValueAccessor, setIonicClasses } from './value-accessor';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-checkbox,ion-toggle',
|
||||
@@ -18,8 +19,8 @@ export class BooleanValueAccessorDirective extends ValueAccessor {
|
||||
}
|
||||
|
||||
writeValue(value: boolean): void {
|
||||
this.elementRef.nativeElement.checked = this.lastValue = value;
|
||||
setIonicClasses(this.elementRef);
|
||||
this.el.nativeElement.checked = this.lastValue = value;
|
||||
setIonicClasses(this.el);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Directive, HostListener, ElementRef, Injector } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
|
||||
import { ValueAccessor } from './value-accessor';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-input[type=number]',
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
|
||||
import { ValueAccessor } from './value-accessor';
|
||||
|
||||
@Directive({
|
||||
/* tslint:disable-next-line:directive-selector */
|
||||
@@ -18,12 +19,9 @@ export class RadioValueAccessorDirective extends ValueAccessor {
|
||||
super(injector, el);
|
||||
}
|
||||
|
||||
// TODO(FW-2827): type (HTMLIonRadioElement and HTMLElement are both missing `checked`)
|
||||
@HostListener('ionSelect', ['$event.target'])
|
||||
_handleIonSelect(el: any): void {
|
||||
/**
|
||||
* The `el` type is any to access the `checked` state property
|
||||
* that is not exposed on the type interface.
|
||||
*/
|
||||
this.handleValueChange(el, el.checked);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
|
||||
import { ValueAccessor } from './value-accessor';
|
||||
|
||||
@Directive({
|
||||
/* tslint:disable-next-line:directive-selector */
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { ValueAccessor } from '@ionic/angular/common';
|
||||
|
||||
import { ValueAccessor } from './value-accessor';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-input:not([type=number]),ion-textarea,ion-searchbar',
|
||||
@@ -2,7 +2,7 @@ import { AfterViewInit, ElementRef, Injector, OnDestroy, Directive, HostListener
|
||||
import { ControlValueAccessor, NgControl } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { raf } from '../../utils/util';
|
||||
import { raf } from '../../util/util';
|
||||
|
||||
// TODO(FW-2827): types
|
||||
|
||||
@@ -17,11 +17,11 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
protected lastValue: any;
|
||||
private statusChanges?: Subscription;
|
||||
|
||||
constructor(protected injector: Injector, protected elementRef: ElementRef) {}
|
||||
constructor(protected injector: Injector, protected el: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.elementRef.nativeElement.value = this.lastValue = value;
|
||||
setIonicClasses(this.elementRef);
|
||||
this.el.nativeElement.value = this.lastValue = value;
|
||||
setIonicClasses(this.el);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,20 +38,20 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
* @param value The new value of the control.
|
||||
*/
|
||||
handleValueChange(el: HTMLElement, value: any): void {
|
||||
if (el === this.elementRef.nativeElement) {
|
||||
if (el === this.el.nativeElement) {
|
||||
if (value !== this.lastValue) {
|
||||
this.lastValue = value;
|
||||
this.onChange(value);
|
||||
}
|
||||
setIonicClasses(this.elementRef);
|
||||
setIonicClasses(this.el);
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('ionBlur', ['$event.target'])
|
||||
_handleBlurEvent(el: any): void {
|
||||
if (el === this.elementRef.nativeElement) {
|
||||
if (el === this.el.nativeElement) {
|
||||
this.onTouched();
|
||||
setIonicClasses(this.elementRef);
|
||||
setIonicClasses(this.el);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
this.elementRef.nativeElement.disabled = isDisabled;
|
||||
this.el.nativeElement.disabled = isDisabled;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
@@ -87,7 +87,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
|
||||
// Listen for changes in validity, disabled, or pending states
|
||||
if (ngControl.statusChanges) {
|
||||
this.statusChanges = ngControl.statusChanges.subscribe(() => setIonicClasses(this.elementRef));
|
||||
this.statusChanges = ngControl.statusChanges.subscribe(() => setIonicClasses(this.el));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,7 +102,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
const oldFn = formControl[method].bind(formControl);
|
||||
formControl[method] = (...params: any[]) => {
|
||||
oldFn(...params);
|
||||
setIonicClasses(this.elementRef);
|
||||
setIonicClasses(this.el);
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -129,7 +129,7 @@ export const setIonicClasses = (element: ElementRef): void => {
|
||||
|
||||
const getClasses = (element: HTMLElement) => {
|
||||
const classList = element.classList;
|
||||
const classes: string[] = [];
|
||||
const classes = [];
|
||||
for (let i = 0; i < classList.length; i++) {
|
||||
const item = classList.item(i);
|
||||
if (item !== null && startsWith(item, 'ng-')) {
|
||||
41
angular/src/directives/navigation/ion-back-button.ts
Normal file
41
angular/src/directives/navigation/ion-back-button.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Directive, HostListener, Input, Optional } from '@angular/core';
|
||||
import { AnimationBuilder } from '@ionic/core';
|
||||
|
||||
import { Config } from '../../providers/config';
|
||||
import { NavController } from '../../providers/nav-controller';
|
||||
|
||||
import { IonRouterOutlet } from './ion-router-outlet';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-back-button',
|
||||
})
|
||||
export class IonBackButtonDelegateDirective {
|
||||
@Input()
|
||||
defaultHref: string | undefined | null;
|
||||
|
||||
@Input()
|
||||
routerAnimation?: AnimationBuilder;
|
||||
|
||||
constructor(
|
||||
@Optional() private routerOutlet: IonRouterOutlet,
|
||||
private navCtrl: NavController,
|
||||
private config: Config
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@HostListener('click', ['$event'])
|
||||
onClick(ev: Event): void {
|
||||
const defaultHref = this.defaultHref || this.config.get('backButtonDefaultHref');
|
||||
|
||||
if (this.routerOutlet?.canGoBack()) {
|
||||
this.navCtrl.setDirection('back', undefined, undefined, this.routerAnimation);
|
||||
this.routerOutlet.pop();
|
||||
ev.preventDefault();
|
||||
} else if (defaultHref != null) {
|
||||
this.navCtrl.navigateBack(defaultHref, { animation: this.routerAnimation });
|
||||
ev.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,23 +15,18 @@ import {
|
||||
Output,
|
||||
SkipSelf,
|
||||
EnvironmentInjector,
|
||||
Input,
|
||||
InjectionToken,
|
||||
Injectable,
|
||||
reflectComponentType,
|
||||
} from '@angular/core';
|
||||
import type { Provider } from '@angular/core';
|
||||
import { OutletContext, Router, ActivatedRoute, ChildrenOutletContexts, PRIMARY_OUTLET, Data } from '@angular/router';
|
||||
import { componentOnReady } from '@ionic/core/components';
|
||||
import type { AnimationBuilder } from '@ionic/core/components';
|
||||
import { Observable, BehaviorSubject, Subscription, combineLatest, of } from 'rxjs';
|
||||
import { componentOnReady } from '@ionic/core';
|
||||
import { Observable, BehaviorSubject } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { AnimationBuilder } from '../../ionic-core';
|
||||
import { Config } from '../../providers/config';
|
||||
import { NavController } from '../../providers/nav-controller';
|
||||
|
||||
import { StackController } from './stack-controller';
|
||||
import { RouteView, StackDidChangeEvent, StackWillChangeEvent, getUrl, isTabSwitch } from './stack-utils';
|
||||
import { RouteView, getUrl } from './stack-utils';
|
||||
|
||||
// TODO(FW-2827): types
|
||||
|
||||
@@ -44,34 +39,24 @@ import { RouteView, StackDidChangeEvent, StackWillChangeEvent, getUrl, isTabSwit
|
||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
||||
export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
nativeEl: HTMLIonRouterOutletElement;
|
||||
activatedView: RouteView | null = null;
|
||||
tabsPrefix: string | undefined;
|
||||
|
||||
private activated: ComponentRef<any> | null = null;
|
||||
activatedView: RouteView | null = null;
|
||||
|
||||
private _activatedRoute: ActivatedRoute | null = null;
|
||||
private _swipeGesture?: boolean;
|
||||
private name: string;
|
||||
private stackCtrl: StackController;
|
||||
|
||||
// Maintain map of activated route proxies for each component instance
|
||||
private proxyMap = new WeakMap<any, ActivatedRoute>();
|
||||
|
||||
// Keep the latest activated route in a subject for the proxy routes to switch map to
|
||||
private currentActivatedRoute$ = new BehaviorSubject<{ component: any; activatedRoute: ActivatedRoute } | null>(null);
|
||||
|
||||
private activated: ComponentRef<any> | null = null;
|
||||
/** @internal */
|
||||
get activatedComponentRef(): ComponentRef<any> | null {
|
||||
return this.activated;
|
||||
}
|
||||
private _activatedRoute: ActivatedRoute | null = null;
|
||||
|
||||
/**
|
||||
* The name of the outlet
|
||||
*/
|
||||
@Input() name = PRIMARY_OUTLET;
|
||||
|
||||
/** @internal */
|
||||
@Output() stackWillChange = new EventEmitter<StackWillChangeEvent>();
|
||||
/** @internal */
|
||||
@Output() stackDidChange = new EventEmitter<StackDidChangeEvent>();
|
||||
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
|
||||
@@ -80,9 +65,6 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
private parentContexts = inject(ChildrenOutletContexts);
|
||||
private location = inject(ViewContainerRef);
|
||||
private environmentInjector = inject(EnvironmentInjector);
|
||||
private inputBinder = inject(INPUT_BINDER, { optional: true });
|
||||
/** @nodoc */
|
||||
readonly supportsBindingToComponentInputs = true;
|
||||
|
||||
// Ionic providers
|
||||
private config = inject(Config);
|
||||
@@ -127,7 +109,6 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.stackCtrl.destroy();
|
||||
this.inputBinder?.unsubscribeFromRouteData(this);
|
||||
}
|
||||
|
||||
getContext(): OutletContext | null {
|
||||
@@ -294,8 +275,6 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
this.currentActivatedRoute$.next({ component: cmpRef.instance, activatedRoute });
|
||||
}
|
||||
|
||||
this.inputBinder?.bindActivatedRouteToOutletComponent(this);
|
||||
|
||||
this.activatedView = enteringView;
|
||||
|
||||
/**
|
||||
@@ -309,16 +288,9 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
*/
|
||||
this.navCtrl.setTopOutlet(this);
|
||||
|
||||
const leavingView = this.stackCtrl.getActiveView();
|
||||
|
||||
this.stackWillChange.emit({
|
||||
enteringView,
|
||||
tabSwitch: isTabSwitch(enteringView, leavingView),
|
||||
});
|
||||
|
||||
this.stackCtrl.setActive(enteringView).then((data) => {
|
||||
this.activateEvents.emit(cmpRef.instance);
|
||||
this.stackDidChange.emit(data);
|
||||
this.stackEvents.emit(data);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -443,98 +415,3 @@ class OutletInjector implements Injector {
|
||||
return this.parent.get(token, notFoundValue);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: FW-4785 - Remove this once Angular 15 support is dropped
|
||||
const INPUT_BINDER = new InjectionToken<RoutedComponentInputBinder>('');
|
||||
|
||||
/**
|
||||
* Injectable used as a tree-shakable provider for opting in to binding router data to component
|
||||
* inputs.
|
||||
*
|
||||
* The RouterOutlet registers itself with this service when an `ActivatedRoute` is attached or
|
||||
* activated. When this happens, the service subscribes to the `ActivatedRoute` observables (params,
|
||||
* queryParams, data) and sets the inputs of the component using `ComponentRef.setInput`.
|
||||
* Importantly, when an input does not have an item in the route data with a matching key, this
|
||||
* input is set to `undefined`. If it were not done this way, the previous information would be
|
||||
* retained if the data got removed from the route (i.e. if a query parameter is removed).
|
||||
*
|
||||
* The `RouterOutlet` should unregister itself when destroyed via `unsubscribeFromRouteData` so that
|
||||
* the subscriptions are cleaned up.
|
||||
*/
|
||||
@Injectable()
|
||||
class RoutedComponentInputBinder {
|
||||
private outletDataSubscriptions = new Map<IonRouterOutlet, Subscription>();
|
||||
|
||||
bindActivatedRouteToOutletComponent(outlet: IonRouterOutlet): void {
|
||||
this.unsubscribeFromRouteData(outlet);
|
||||
this.subscribeToRouteData(outlet);
|
||||
}
|
||||
|
||||
unsubscribeFromRouteData(outlet: IonRouterOutlet): void {
|
||||
this.outletDataSubscriptions.get(outlet)?.unsubscribe();
|
||||
this.outletDataSubscriptions.delete(outlet);
|
||||
}
|
||||
|
||||
private subscribeToRouteData(outlet: IonRouterOutlet) {
|
||||
const { activatedRoute } = outlet;
|
||||
const dataSubscription = combineLatest([activatedRoute.queryParams, activatedRoute.params, activatedRoute.data])
|
||||
.pipe(
|
||||
switchMap(([queryParams, params, data], index) => {
|
||||
data = { ...queryParams, ...params, ...data };
|
||||
// Get the first result from the data subscription synchronously so it's available to
|
||||
// the component as soon as possible (and doesn't require a second change detection).
|
||||
if (index === 0) {
|
||||
return of(data);
|
||||
}
|
||||
// Promise.resolve is used to avoid synchronously writing the wrong data when
|
||||
// two of the Observables in the `combineLatest` stream emit one after
|
||||
// another.
|
||||
return Promise.resolve(data);
|
||||
})
|
||||
)
|
||||
.subscribe((data) => {
|
||||
// Outlet may have been deactivated or changed names to be associated with a different
|
||||
// route
|
||||
if (
|
||||
!outlet.isActivated ||
|
||||
!outlet.activatedComponentRef ||
|
||||
outlet.activatedRoute !== activatedRoute ||
|
||||
activatedRoute.component === null
|
||||
) {
|
||||
this.unsubscribeFromRouteData(outlet);
|
||||
return;
|
||||
}
|
||||
|
||||
const mirror = reflectComponentType(activatedRoute.component);
|
||||
if (!mirror) {
|
||||
this.unsubscribeFromRouteData(outlet);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const { templateName } of mirror.inputs) {
|
||||
outlet.activatedComponentRef.setInput(templateName, data[templateName]);
|
||||
}
|
||||
});
|
||||
|
||||
this.outletDataSubscriptions.set(outlet, dataSubscription);
|
||||
}
|
||||
}
|
||||
|
||||
export const provideComponentInputBinding = (): Provider => {
|
||||
return {
|
||||
provide: INPUT_BINDER,
|
||||
useFactory: componentInputBindingFactory,
|
||||
deps: [Router],
|
||||
};
|
||||
};
|
||||
|
||||
function componentInputBindingFactory(router?: Router) {
|
||||
/**
|
||||
* We cast the router to any here, since the componentInputBindingEnabled
|
||||
* property is not available until Angular v16.
|
||||
*/
|
||||
if ((router as any)?.componentInputBindingEnabled) {
|
||||
return new RoutedComponentInputBinder();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1,70 +1,62 @@
|
||||
import {
|
||||
AfterContentChecked,
|
||||
AfterContentInit,
|
||||
Directive,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
HostListener,
|
||||
Output,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { Component, ContentChild, EventEmitter, HostListener, Output, ViewChild } from '@angular/core';
|
||||
|
||||
import { NavController } from '../../providers/nav-controller';
|
||||
import { IonTabBar } from '../proxies';
|
||||
|
||||
import { StackDidChangeEvent, StackWillChangeEvent } from './stack-utils';
|
||||
import { IonRouterOutlet } from './ion-router-outlet';
|
||||
import { StackEvent } from './stack-utils';
|
||||
|
||||
@Directive({
|
||||
@Component({
|
||||
selector: 'ion-tabs',
|
||||
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;
|
||||
|
||||
flex-direction: column;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
contain: layout size style;
|
||||
}
|
||||
.tabs-inner {
|
||||
position: relative;
|
||||
|
||||
flex: 1;
|
||||
|
||||
contain: layout size style;
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
||||
export class IonTabs implements AfterContentInit, AfterContentChecked {
|
||||
/**
|
||||
* Note: These must be redeclared on each child class since it needs
|
||||
* access to generated components such as IonRouterOutlet and IonTabBar.
|
||||
*/
|
||||
outlet: any;
|
||||
tabBar: any;
|
||||
tabBars: any;
|
||||
// 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;
|
||||
|
||||
@ViewChild('tabsInner', { read: ElementRef, static: true }) tabsInner: ElementRef<HTMLDivElement>;
|
||||
|
||||
/**
|
||||
* Emitted before the tab view is changed.
|
||||
*/
|
||||
@Output() ionTabsWillChange = new EventEmitter<{ tab: string }>();
|
||||
/**
|
||||
* Emitted after the tab view is changed.
|
||||
*/
|
||||
@Output() ionTabsDidChange = new EventEmitter<{ tab: string }>();
|
||||
|
||||
private tabBarSlot = 'bottom';
|
||||
|
||||
constructor(private navCtrl: NavController) {}
|
||||
|
||||
ngAfterContentInit(): void {
|
||||
this.detectSlotChanges();
|
||||
}
|
||||
|
||||
ngAfterContentChecked(): void {
|
||||
this.detectSlotChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
onStackWillChange({ enteringView, tabSwitch }: StackWillChangeEvent): void {
|
||||
const stackId = enteringView.stackId;
|
||||
if (tabSwitch && stackId !== undefined) {
|
||||
onPageSelected(detail: StackEvent): void {
|
||||
const stackId = detail.enteringView.stackId;
|
||||
if (detail.tabSwitch && stackId !== undefined) {
|
||||
this.ionTabsWillChange.emit({ tab: stackId });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
onStackDidChange({ enteringView, tabSwitch }: StackDidChangeEvent): void {
|
||||
const stackId = enteringView.stackId;
|
||||
if (tabSwitch && stackId !== undefined) {
|
||||
if (this.tabBar) {
|
||||
this.tabBar.selectedTab = stackId;
|
||||
}
|
||||
@@ -145,48 +137,4 @@ export class IonTabs implements AfterContentInit, AfterContentChecked {
|
||||
getSelected(): string | undefined {
|
||||
return this.outlet.getActiveStackId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects changes to the slot attribute of the tab bar.
|
||||
*
|
||||
* If the slot attribute has changed, then the tab bar
|
||||
* should be relocated to the new slot position.
|
||||
*/
|
||||
private detectSlotChanges(): void {
|
||||
this.tabBars.forEach((tabBar: any) => {
|
||||
// el is a protected attribute from the generated component wrapper
|
||||
const currentSlot = tabBar.el.getAttribute('slot');
|
||||
|
||||
if (currentSlot !== this.tabBarSlot) {
|
||||
this.tabBarSlot = currentSlot;
|
||||
this.relocateTabBar();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Relocates the tab bar to the new slot position.
|
||||
*/
|
||||
private relocateTabBar(): void {
|
||||
/**
|
||||
* `el` is a protected attribute from the generated component wrapper.
|
||||
* To avoid having to manually create the wrapper for tab bar, we
|
||||
* cast the tab bar to any and access the protected attribute.
|
||||
*/
|
||||
const tabBar = (this.tabBar as any).el as HTMLElement;
|
||||
|
||||
if (this.tabBarSlot === 'top') {
|
||||
/**
|
||||
* A tab bar with a slot of "top" should be inserted
|
||||
* at the top of the container.
|
||||
*/
|
||||
this.tabsInner.nativeElement.before(tabBar);
|
||||
} else {
|
||||
/**
|
||||
* A tab bar with a slot of "bottom" or without a slot
|
||||
* should be inserted at the end of the container.
|
||||
*/
|
||||
this.tabsInner.nativeElement.after(tabBar);
|
||||
}
|
||||
}
|
||||
}
|
||||
40
angular/src/directives/navigation/nav-delegate.ts
Normal file
40
angular/src/directives/navigation/nav-delegate.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { ElementRef, Injector, Directive, EnvironmentInjector } from '@angular/core';
|
||||
|
||||
import { AngularDelegate } from '../../providers/angular-delegate';
|
||||
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['animated', 'animation', 'root', 'rootParams', 'swipeGesture'],
|
||||
methods: [
|
||||
'push',
|
||||
'insert',
|
||||
'insertPages',
|
||||
'pop',
|
||||
'popTo',
|
||||
'popToRoot',
|
||||
'removeIndex',
|
||||
'setRoot',
|
||||
'setPages',
|
||||
'getActive',
|
||||
'getByIndex',
|
||||
'canGoBack',
|
||||
'getPrevious',
|
||||
],
|
||||
})
|
||||
@Directive({
|
||||
selector: 'ion-nav',
|
||||
})
|
||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
||||
export class NavDelegate {
|
||||
protected el: HTMLElement;
|
||||
constructor(
|
||||
ref: ElementRef,
|
||||
environmentInjector: EnvironmentInjector,
|
||||
injector: Injector,
|
||||
angularDelegate: AngularDelegate
|
||||
) {
|
||||
this.el = ref.nativeElement;
|
||||
ref.nativeElement.delegate = angularDelegate.create(environmentInjector, injector);
|
||||
proxyOutputs(this, this.el, ['ionNavDidChange', 'ionNavWillChange']);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { LocationStrategy } from '@angular/common';
|
||||
import { ElementRef, OnChanges, OnInit, Directive, HostListener, Input, Optional } from '@angular/core';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import type { AnimationBuilder, RouterDirection } from '@ionic/core/components';
|
||||
import { AnimationBuilder, RouterDirection } from '@ionic/core';
|
||||
|
||||
import { NavController } from '../../providers/nav-controller';
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { ComponentRef, NgZone } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import type { AnimationBuilder, RouterDirection } from '@ionic/core/components';
|
||||
import { AnimationBuilder, RouterDirection } from '@ionic/core';
|
||||
|
||||
import { bindLifecycleEvents } from '../../providers/angular-delegate';
|
||||
import { NavController } from '../../providers/nav-controller';
|
||||
|
||||
import {
|
||||
RouteView,
|
||||
StackDidChangeEvent,
|
||||
StackEvent,
|
||||
computeStackId,
|
||||
destroyView,
|
||||
getUrl,
|
||||
@@ -61,7 +61,7 @@ export class StackController {
|
||||
return view;
|
||||
}
|
||||
|
||||
setActive(enteringView: RouteView): Promise<StackDidChangeEvent> {
|
||||
setActive(enteringView: RouteView): Promise<StackEvent> {
|
||||
const consumeResult = this.navCtrl.consumeTransition();
|
||||
let { direction, animation, animationBuilder } = consumeResult;
|
||||
const leavingView = this.activeView;
|
||||
@@ -224,13 +224,6 @@ export class StackController {
|
||||
return this.activeView ? this.activeView.stackId : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
getActiveView(): RouteView | undefined {
|
||||
return this.activeView;
|
||||
}
|
||||
|
||||
hasRunningTask(): boolean {
|
||||
return this.runningTask !== undefined;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ComponentRef } from '@angular/core';
|
||||
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
|
||||
import type { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core/components';
|
||||
import { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core';
|
||||
|
||||
export const insertView = (views: RouteView[], view: RouteView, direction: RouterDirection): RouteView[] => {
|
||||
if (direction === 'root') {
|
||||
@@ -79,23 +79,10 @@ export const destroyView = (view: RouteView | undefined): void => {
|
||||
}
|
||||
};
|
||||
|
||||
export interface StackWillChangeEvent {
|
||||
enteringView: RouteView;
|
||||
/**
|
||||
* `true` if the event is trigged as a result of a switch
|
||||
* between tab navigation stacks.
|
||||
*/
|
||||
tabSwitch: boolean;
|
||||
}
|
||||
|
||||
export interface StackDidChangeEvent {
|
||||
export interface StackEvent {
|
||||
enteringView: RouteView;
|
||||
direction: RouterDirection;
|
||||
animation: NavDirection | undefined;
|
||||
/**
|
||||
* `true` if the event is trigged as a result of a switch
|
||||
* between tab navigation stacks.
|
||||
*/
|
||||
tabSwitch: boolean;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ContentChild,
|
||||
Directive,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
NgZone,
|
||||
TemplateRef,
|
||||
} from '@angular/core';
|
||||
import type { Components, ModalBreakpointChangeEventDetail } from '@ionic/core/components';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from '../utils/proxy';
|
||||
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
|
||||
import { Components, ModalBreakpointChangeEventDetail } from '@ionic/core';
|
||||
|
||||
export declare interface IonModal extends Components.IonModal {
|
||||
/**
|
||||
@@ -33,7 +35,7 @@ export declare interface IonModal extends Components.IonModal {
|
||||
*/
|
||||
ionBreakpointDidChange: EventEmitter<CustomEvent<ModalBreakpointChangeEventDetail>>;
|
||||
/**
|
||||
* Emitted after the modal has presented. Shorthand for ionModalDidPresent.
|
||||
* Emitted after the modal has presented. Shorthand for ionModalWillDismiss.
|
||||
*/
|
||||
didPresent: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
@@ -49,61 +51,65 @@ export declare interface IonModal extends Components.IonModal {
|
||||
*/
|
||||
didDismiss: EventEmitter<CustomEvent>;
|
||||
}
|
||||
|
||||
const MODAL_INPUTS = [
|
||||
'animated',
|
||||
'keepContentsMounted',
|
||||
'backdropBreakpoint',
|
||||
'backdropDismiss',
|
||||
'breakpoints',
|
||||
'canDismiss',
|
||||
'cssClass',
|
||||
'enterAnimation',
|
||||
'event',
|
||||
'handle',
|
||||
'handleBehavior',
|
||||
'initialBreakpoint',
|
||||
'isOpen',
|
||||
'keyboardClose',
|
||||
'leaveAnimation',
|
||||
'mode',
|
||||
'presentingElement',
|
||||
'showBackdrop',
|
||||
'translucent',
|
||||
'trigger',
|
||||
];
|
||||
|
||||
const MODAL_METHODS = [
|
||||
'present',
|
||||
'dismiss',
|
||||
'onDidDismiss',
|
||||
'onWillDismiss',
|
||||
'setCurrentBreakpoint',
|
||||
'getCurrentBreakpoint',
|
||||
];
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: MODAL_INPUTS,
|
||||
methods: MODAL_METHODS,
|
||||
inputs: [
|
||||
'animated',
|
||||
'keepContentsMounted',
|
||||
'backdropBreakpoint',
|
||||
'backdropDismiss',
|
||||
'breakpoints',
|
||||
'canDismiss',
|
||||
'cssClass',
|
||||
'enterAnimation',
|
||||
'event',
|
||||
'handle',
|
||||
'handleBehavior',
|
||||
'initialBreakpoint',
|
||||
'isOpen',
|
||||
'keyboardClose',
|
||||
'leaveAnimation',
|
||||
'mode',
|
||||
'presentingElement',
|
||||
'showBackdrop',
|
||||
'translucent',
|
||||
'trigger',
|
||||
],
|
||||
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss', 'setCurrentBreakpoint', 'getCurrentBreakpoint'],
|
||||
})
|
||||
/**
|
||||
* @Component extends from @Directive
|
||||
* so by defining the inputs here we
|
||||
* do not need to re-define them for the
|
||||
* lazy loaded popover.
|
||||
*/
|
||||
@Directive({
|
||||
@Component({
|
||||
selector: 'ion-modal',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: MODAL_INPUTS,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `<div class="ion-delegate-host ion-page" *ngIf="isCmpOpen || keepContentsMounted">
|
||||
<ng-container [ngTemplateOutlet]="template"></ng-container>
|
||||
</div>`,
|
||||
inputs: [
|
||||
'animated',
|
||||
'keepContentsMounted',
|
||||
'backdropBreakpoint',
|
||||
'backdropDismiss',
|
||||
'breakpoints',
|
||||
'canDismiss',
|
||||
'cssClass',
|
||||
'enterAnimation',
|
||||
'event',
|
||||
'handle',
|
||||
'handleBehavior',
|
||||
'initialBreakpoint',
|
||||
'isOpen',
|
||||
'keyboardClose',
|
||||
'leaveAnimation',
|
||||
'mode',
|
||||
'presentingElement',
|
||||
'showBackdrop',
|
||||
'translucent',
|
||||
'trigger',
|
||||
],
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
||||
export class IonModal {
|
||||
// TODO(FW-2827): type
|
||||
@ContentChild(TemplateRef, { static: false }) template: TemplateRef<any>;
|
||||
|
||||
isCmpOpen = false;
|
||||
isCmpOpen: boolean = false;
|
||||
|
||||
protected el: HTMLElement;
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ContentChild,
|
||||
Directive,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
NgZone,
|
||||
TemplateRef,
|
||||
} from '@angular/core';
|
||||
import type { Components } from '@ionic/core/components';
|
||||
|
||||
import { ProxyCmp, proxyOutputs } from '../utils/proxy';
|
||||
|
||||
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.
|
||||
@@ -29,7 +30,7 @@ export declare interface IonPopover extends Components.IonPopover {
|
||||
*/
|
||||
ionPopoverDidDismiss: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted after the popover has presented. Shorthand for ionPopoverDidPresent.
|
||||
* Emitted after the popover has presented. Shorthand for ionPopoverWillDismiss.
|
||||
*/
|
||||
didPresent: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
@@ -45,54 +46,63 @@ export declare interface IonPopover extends Components.IonPopover {
|
||||
*/
|
||||
didDismiss: EventEmitter<CustomEvent>;
|
||||
}
|
||||
|
||||
const POPOVER_INPUTS = [
|
||||
'alignment',
|
||||
'animated',
|
||||
'arrow',
|
||||
'keepContentsMounted',
|
||||
'backdropDismiss',
|
||||
'cssClass',
|
||||
'dismissOnSelect',
|
||||
'enterAnimation',
|
||||
'event',
|
||||
'isOpen',
|
||||
'keyboardClose',
|
||||
'leaveAnimation',
|
||||
'mode',
|
||||
'showBackdrop',
|
||||
'translucent',
|
||||
'trigger',
|
||||
'triggerAction',
|
||||
'reference',
|
||||
'size',
|
||||
'side',
|
||||
];
|
||||
|
||||
const POPOVER_METHODS = ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss'];
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: POPOVER_INPUTS,
|
||||
methods: POPOVER_METHODS,
|
||||
inputs: [
|
||||
'alignment',
|
||||
'animated',
|
||||
'arrow',
|
||||
'keepContentsMounted',
|
||||
'backdropDismiss',
|
||||
'cssClass',
|
||||
'dismissOnSelect',
|
||||
'enterAnimation',
|
||||
'event',
|
||||
'isOpen',
|
||||
'keyboardClose',
|
||||
'leaveAnimation',
|
||||
'mode',
|
||||
'showBackdrop',
|
||||
'translucent',
|
||||
'trigger',
|
||||
'triggerAction',
|
||||
'reference',
|
||||
'size',
|
||||
'side',
|
||||
],
|
||||
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss'],
|
||||
})
|
||||
/**
|
||||
* @Component extends from @Directive
|
||||
* so by defining the inputs here we
|
||||
* do not need to re-define them for the
|
||||
* lazy loaded popover.
|
||||
*/
|
||||
@Directive({
|
||||
@Component({
|
||||
selector: 'ion-popover',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: POPOVER_INPUTS,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `<ng-container [ngTemplateOutlet]="template" *ngIf="isCmpOpen || keepContentsMounted"></ng-container>`,
|
||||
inputs: [
|
||||
'alignment',
|
||||
'animated',
|
||||
'arrow',
|
||||
'keepContentsMounted',
|
||||
'backdropDismiss',
|
||||
'cssClass',
|
||||
'dismissOnSelect',
|
||||
'enterAnimation',
|
||||
'event',
|
||||
'isOpen',
|
||||
'keyboardClose',
|
||||
'leaveAnimation',
|
||||
'mode',
|
||||
'showBackdrop',
|
||||
'translucent',
|
||||
'trigger',
|
||||
'triggerAction',
|
||||
'reference',
|
||||
'size',
|
||||
'side',
|
||||
],
|
||||
})
|
||||
|
||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
||||
export class IonPopover {
|
||||
// TODO(FW-2827): type
|
||||
@ContentChild(TemplateRef, { static: false }) template: TemplateRef<any>;
|
||||
|
||||
isCmpOpen = false;
|
||||
isCmpOpen: boolean = false;
|
||||
|
||||
protected el: HTMLElement;
|
||||
|
||||
@@ -8,6 +8,7 @@ export const DIRECTIVES = [
|
||||
d.IonAlert,
|
||||
d.IonApp,
|
||||
d.IonAvatar,
|
||||
d.IonBackButton,
|
||||
d.IonBackdrop,
|
||||
d.IonBadge,
|
||||
d.IonBreadcrumb,
|
||||
@@ -49,6 +50,7 @@ export const DIRECTIVES = [
|
||||
d.IonMenu,
|
||||
d.IonMenuButton,
|
||||
d.IonMenuToggle,
|
||||
d.IonNav,
|
||||
d.IonNavLink,
|
||||
d.IonNote,
|
||||
d.IonPicker,
|
||||
@@ -230,6 +230,28 @@ export class IonAvatar {
|
||||
export declare interface IonAvatar extends Components.IonAvatar {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['color', 'defaultHref', 'disabled', 'icon', 'mode', 'routerAnimation', 'text', 'type']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-back-button',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['color', 'defaultHref', 'disabled', 'icon', 'mode', 'routerAnimation', 'text', 'type'],
|
||||
})
|
||||
export class IonBackButton {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export declare interface IonBackButton extends Components.IonBackButton {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['stopPropagation', 'tappable', 'visible']
|
||||
})
|
||||
@@ -507,14 +529,14 @@ export declare interface IonCardTitle extends Components.IonCardTitle {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value']
|
||||
inputs: ['checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-checkbox',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value'],
|
||||
inputs: ['checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value'],
|
||||
})
|
||||
export class IonCheckbox {
|
||||
protected el: HTMLElement;
|
||||
@@ -980,9 +1002,8 @@ import type { InputChangeEventDetail as IIonInputInputChangeEventDetail } from '
|
||||
|
||||
export declare interface IonInput extends Components.IonInput {
|
||||
/**
|
||||
* The `ionInput` event is fired each time the user modifies the input's value.
|
||||
Unlike the `ionChange` event, the `ionInput` event is fired for each alteration
|
||||
to the input's value. This typically happens for each keystroke as the user types.
|
||||
* The `ionInput` event fires when the `value` of an `<ion-input>` element
|
||||
has been changed.
|
||||
|
||||
For elements that accept text input (`type=text`, `type=tel`, etc.), the interface
|
||||
is [`InputEvent`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent); for others,
|
||||
@@ -991,9 +1012,9 @@ the input is cleared on edit, the type is `null`.
|
||||
*/
|
||||
ionInput: EventEmitter<CustomEvent<IIonInputInputInputEventDetail>>;
|
||||
/**
|
||||
* The `ionChange` event is fired when the user modifies the input's value.
|
||||
Unlike the `ionInput` event, the `ionChange` event is only fired when changes
|
||||
are committed, not as the user types.
|
||||
* The `ionChange` event is fired for `<ion-input>` elements when the user
|
||||
modifies the element's value. Unlike the `ionInput` event, the `ionChange`
|
||||
event is not necessarily fired for each alteration to an element's value.
|
||||
|
||||
Depending on the way the users interacts with the element, the `ionChange`
|
||||
event fires at a different moment:
|
||||
@@ -1373,6 +1394,39 @@ export class IonMenuToggle {
|
||||
export declare interface IonMenuToggle extends Components.IonMenuToggle {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['animated', 'animation', 'root', 'rootParams', 'swipeGesture'],
|
||||
methods: ['push', 'insert', 'insertPages', 'pop', 'popTo', 'popToRoot', 'removeIndex', 'setRoot', 'setPages', 'getActive', 'getByIndex', 'canGoBack', 'getPrevious']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-nav',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['animated', 'animation', 'root', 'rootParams', 'swipeGesture'],
|
||||
})
|
||||
export class IonNav {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
proxyOutputs(this, this.el, ['ionNavWillChange', 'ionNavDidChange']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export declare interface IonNav extends Components.IonNav {
|
||||
/**
|
||||
* Event fired when the nav will change components
|
||||
*/
|
||||
ionNavWillChange: EventEmitter<CustomEvent<void>>;
|
||||
/**
|
||||
* Event fired when the nav has changed components
|
||||
*/
|
||||
ionNavDidChange: EventEmitter<CustomEvent<void>>;
|
||||
}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['component', 'componentProps', 'routerAnimation', 'routerDirection']
|
||||
})
|
||||
@@ -1503,14 +1557,14 @@ export declare interface IonProgressBar extends Components.IonProgressBar {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['alignment', 'color', 'disabled', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value']
|
||||
inputs: ['color', 'disabled', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-radio',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['alignment', 'color', 'disabled', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value'],
|
||||
inputs: ['color', 'disabled', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value'],
|
||||
})
|
||||
export class IonRadio {
|
||||
protected el: HTMLElement;
|
||||
@@ -1565,14 +1619,14 @@ export declare interface IonRadioGroup extends Components.IonRadioGroup {
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['activeBarStart', 'color', 'debounce', 'disabled', 'dualKnobs', 'label', 'labelPlacement', 'legacy', 'max', 'min', 'mode', 'name', 'pin', 'pinFormatter', 'snaps', 'step', 'ticks', 'value']
|
||||
inputs: ['activeBarStart', 'color', 'debounce', 'disabled', 'dualKnobs', 'labelPlacement', 'legacy', 'max', 'min', 'mode', 'name', 'pin', 'pinFormatter', 'snaps', 'step', 'ticks', 'value']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-range',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['activeBarStart', 'color', 'debounce', 'disabled', 'dualKnobs', 'label', 'labelPlacement', 'legacy', 'max', 'min', 'mode', 'name', 'pin', 'pinFormatter', 'snaps', 'step', 'ticks', 'value'],
|
||||
inputs: ['activeBarStart', 'color', 'debounce', 'disabled', 'dualKnobs', 'labelPlacement', 'legacy', 'max', 'min', 'mode', 'name', 'pin', 'pinFormatter', 'snaps', 'step', 'ticks', 'value'],
|
||||
})
|
||||
export class IonRange {
|
||||
protected el: HTMLElement;
|
||||
@@ -1788,7 +1842,7 @@ export declare interface IonRow extends Components.IonRow {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['animated', 'autocomplete', 'autocorrect', 'cancelButtonIcon', 'cancelButtonText', 'clearIcon', 'color', 'debounce', 'disabled', 'enterkeyhint', 'inputmode', 'mode', 'name', 'placeholder', 'searchIcon', 'showCancelButton', 'showClearButton', 'spellcheck', 'type', 'value'],
|
||||
inputs: ['animated', 'autocomplete', 'autocorrect', 'cancelButtonIcon', 'cancelButtonText', 'clearIcon', 'color', 'debounce', 'disabled', 'enterkeyhint', 'inputmode', 'mode', 'placeholder', 'searchIcon', 'showCancelButton', 'showClearButton', 'spellcheck', 'type', 'value'],
|
||||
methods: ['setFocus', 'getInputElement']
|
||||
})
|
||||
@Component({
|
||||
@@ -1796,7 +1850,7 @@ export declare interface IonRow extends Components.IonRow {}
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['animated', 'autocomplete', 'autocorrect', 'cancelButtonIcon', 'cancelButtonText', 'clearIcon', 'color', 'debounce', 'disabled', 'enterkeyhint', 'inputmode', 'mode', 'name', 'placeholder', 'searchIcon', 'showCancelButton', 'showClearButton', 'spellcheck', 'type', 'value'],
|
||||
inputs: ['animated', 'autocomplete', 'autocorrect', 'cancelButtonIcon', 'cancelButtonText', 'clearIcon', 'color', 'debounce', 'disabled', 'enterkeyhint', 'inputmode', 'mode', 'placeholder', 'searchIcon', 'showCancelButton', 'showClearButton', 'spellcheck', 'type', 'value'],
|
||||
})
|
||||
export class IonSearchbar {
|
||||
protected el: HTMLElement;
|
||||
@@ -1900,7 +1954,7 @@ export declare interface IonSegmentButton extends Components.IonSegmentButton {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'expandedIcon', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'legacy', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'selectedText', 'shape', 'toggleIcon', 'value'],
|
||||
inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'legacy', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'selectedText', 'shape', 'value'],
|
||||
methods: ['open']
|
||||
})
|
||||
@Component({
|
||||
@@ -1908,7 +1962,7 @@ export declare interface IonSegmentButton extends Components.IonSegmentButton {}
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'expandedIcon', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'legacy', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'selectedText', 'shape', 'toggleIcon', 'value'],
|
||||
inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'legacy', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'selectedText', 'shape', 'value'],
|
||||
})
|
||||
export class IonSelect {
|
||||
protected el: HTMLElement;
|
||||
@@ -2132,15 +2186,17 @@ import type { TextareaInputEventDetail as IIonTextareaTextareaInputEventDetail }
|
||||
|
||||
export declare interface IonTextarea extends Components.IonTextarea {
|
||||
/**
|
||||
* The `ionChange` event is fired when the user modifies the textarea's value.
|
||||
Unlike the `ionInput` event, the `ionChange` event is fired when
|
||||
the element loses focus after its value has been modified.
|
||||
* The `ionChange` event is fired for `<ion-textarea>` elements when the user
|
||||
modifies the element's value. Unlike the `ionInput` event, the `ionChange`
|
||||
event is not necessarily fired for each alteration to an element's value.
|
||||
|
||||
The `ionChange` event is fired when the element loses focus after its value
|
||||
has been modified.
|
||||
*/
|
||||
ionChange: EventEmitter<CustomEvent<IIonTextareaTextareaChangeEventDetail>>;
|
||||
/**
|
||||
* The `ionInput` event is fired each time the user modifies the textarea's value.
|
||||
Unlike the `ionChange` event, the `ionInput` event is fired for each alteration
|
||||
to the textarea's value. This typically happens for each keystroke as the user types.
|
||||
* The `ionInput` event fires when the `value` of an `<ion-textarea>` element
|
||||
has been changed.
|
||||
|
||||
When `clearOnEdit` is enabled, the `ionInput` event will be fired when
|
||||
the user clears the textarea by performing a keydown event.
|
||||
@@ -2201,7 +2257,7 @@ export declare interface IonTitle extends Components.IonTitle {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'positionAnchor', 'translucent', 'trigger'],
|
||||
inputs: ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'translucent', 'trigger'],
|
||||
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss']
|
||||
})
|
||||
@Component({
|
||||
@@ -2209,7 +2265,7 @@ export declare interface IonTitle extends Components.IonTitle {}
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'positionAnchor', 'translucent', 'trigger'],
|
||||
inputs: ['animated', 'buttons', 'color', 'cssClass', 'duration', 'enterAnimation', 'header', 'htmlAttributes', 'icon', 'isOpen', 'keyboardClose', 'layout', 'leaveAnimation', 'message', 'mode', 'position', 'translucent', 'trigger'],
|
||||
})
|
||||
export class IonToast {
|
||||
protected el: HTMLElement;
|
||||
@@ -2264,14 +2320,14 @@ Shorthand for ionToastDidDismiss.
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['alignment', 'checked', 'color', 'disabled', 'enableOnOffLabels', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value']
|
||||
inputs: ['checked', 'color', 'disabled', 'enableOnOffLabels', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-toggle',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['alignment', 'checked', 'color', 'disabled', 'enableOnOffLabels', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value'],
|
||||
inputs: ['checked', 'color', 'disabled', 'enableOnOffLabels', 'justify', 'labelPlacement', 'legacy', 'mode', 'name', 'value'],
|
||||
})
|
||||
export class IonToggle {
|
||||
protected el: HTMLElement;
|
||||
@@ -5,43 +5,41 @@ export { RadioValueAccessorDirective as RadioValueAccessor } from './directives/
|
||||
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 { IonTabs } from './directives/navigation/ion-tabs';
|
||||
export { IonBackButton } from './directives/navigation/ion-back-button';
|
||||
export { IonNav } from './directives/navigation/ion-nav';
|
||||
export { IonBackButtonDelegateDirective as 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,
|
||||
RouterLinkWithHrefDelegateDirective as RouterLinkWithHrefDelegate,
|
||||
} from './directives/navigation/router-link-delegate';
|
||||
|
||||
export { NavParams } from './directives/navigation/nav-params';
|
||||
export { IonModal } from './directives/overlays/modal';
|
||||
export { IonPopover } from './directives/overlays/popover';
|
||||
export * from './directives/proxies';
|
||||
export * from './directives/validators';
|
||||
|
||||
// PROVIDERS
|
||||
export {
|
||||
DomController,
|
||||
NavController,
|
||||
Config,
|
||||
Platform,
|
||||
AngularDelegate,
|
||||
NavParams,
|
||||
IonicRouteStrategy,
|
||||
ViewWillEnter,
|
||||
ViewWillLeave,
|
||||
ViewDidEnter,
|
||||
ViewDidLeave,
|
||||
} from '@ionic/angular/common';
|
||||
export { AlertController } from './providers/alert-controller';
|
||||
export { AnimationController } from './providers/animation-controller';
|
||||
export { AngularDelegate } from './providers/angular-delegate';
|
||||
export { ActionSheetController } from './providers/action-sheet-controller';
|
||||
export { GestureController } from './providers/gesture-controller';
|
||||
export { AlertController } from './providers/alert-controller';
|
||||
export { LoadingController } from './providers/loading-controller';
|
||||
export { MenuController } from './providers/menu-controller';
|
||||
export { ModalController } from './providers/modal-controller';
|
||||
export { PickerController } from './providers/picker-controller';
|
||||
export { ModalController } from './providers/modal-controller';
|
||||
export { Platform } from './providers/platform';
|
||||
export { PopoverController } from './providers/popover-controller';
|
||||
export { ToastController } from './providers/toast-controller';
|
||||
export { NavController } from './providers/nav-controller';
|
||||
export { DomController } from './providers/dom-controller';
|
||||
export { Config } from './providers/config';
|
||||
export { AnimationController } from './providers/animation-controller';
|
||||
export { GestureController } from './providers/gesture-controller';
|
||||
|
||||
// ROUTER STRATEGY
|
||||
export { IonicRouteStrategy } from './util/ionic-router-reuse-strategy';
|
||||
|
||||
// TYPES
|
||||
export * from './types/ionic-lifecycle-hooks';
|
||||
|
||||
// PACKAGE MODULE
|
||||
export { IonicModule } from './ionic-module';
|
||||
@@ -56,7 +54,6 @@ export {
|
||||
getPlatforms,
|
||||
isPlatform,
|
||||
getTimeGivenProgression,
|
||||
getIonPageElement,
|
||||
// TYPES
|
||||
Animation,
|
||||
AnimationBuilder,
|
||||
@@ -120,7 +117,6 @@ export {
|
||||
SearchbarInputEventDetail,
|
||||
SegmentChangeEventDetail,
|
||||
SegmentCustomEvent,
|
||||
SegmentValue,
|
||||
SelectChangeEventDetail,
|
||||
SelectCustomEvent,
|
||||
TabsCustomEvent,
|
||||
@@ -131,6 +127,4 @@ export {
|
||||
ToastLayout,
|
||||
ToggleChangeEventDetail,
|
||||
ToggleCustomEvent,
|
||||
TransitionOptions,
|
||||
openURL,
|
||||
} from '@ionic/core';
|
||||
@@ -1,6 +1,5 @@
|
||||
import { CommonModule, DOCUMENT } from '@angular/common';
|
||||
import { ModuleWithProviders, APP_INITIALIZER, NgModule, NgZone } from '@angular/core';
|
||||
import { ConfigToken, AngularDelegate, provideComponentInputBinding } from '@ionic/angular/common';
|
||||
import { IonicConfig } from '@ionic/core';
|
||||
|
||||
import { appInitialize } from './app-initialize';
|
||||
@@ -11,10 +10,10 @@ import {
|
||||
SelectValueAccessorDirective,
|
||||
TextValueAccessorDirective,
|
||||
} from './directives/control-value-accessors';
|
||||
import { IonBackButton } from './directives/navigation/ion-back-button';
|
||||
import { IonNav } from './directives/navigation/ion-nav';
|
||||
import { IonBackButtonDelegateDirective } 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,
|
||||
RouterLinkWithHrefDelegateDirective,
|
||||
@@ -22,7 +21,8 @@ import {
|
||||
import { IonModal } from './directives/overlays/modal';
|
||||
import { IonPopover } from './directives/overlays/popover';
|
||||
import { DIRECTIVES } from './directives/proxies-list';
|
||||
import { IonMaxValidator, IonMinValidator } from './directives/validators';
|
||||
import { AngularDelegate } from './providers/angular-delegate';
|
||||
import { ConfigToken } from './providers/config';
|
||||
import { ModalController } from './providers/modal-controller';
|
||||
import { PopoverController } from './providers/popover-controller';
|
||||
|
||||
@@ -44,14 +44,10 @@ const DECLARATIONS = [
|
||||
// navigation
|
||||
IonTabs,
|
||||
IonRouterOutlet,
|
||||
IonBackButton,
|
||||
IonNav,
|
||||
IonBackButtonDelegateDirective,
|
||||
NavDelegate,
|
||||
RouterLinkDelegateDirective,
|
||||
RouterLinkWithHrefDelegateDirective,
|
||||
|
||||
// validators
|
||||
IonMinValidator,
|
||||
IonMaxValidator,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
@@ -75,7 +71,6 @@ export class IonicModule {
|
||||
multi: true,
|
||||
deps: [ConfigToken, DOCUMENT, NgZone],
|
||||
},
|
||||
provideComponentInputBinding(),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { ActionSheetOptions } from '@ionic/core';
|
||||
import { actionSheetController } from '@ionic/core';
|
||||
import { ActionSheetOptions, actionSheetController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { AlertOptions } from '@ionic/core';
|
||||
import { alertController } from '@ionic/core';
|
||||
import { AlertOptions, alertController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
LIFECYCLE_WILL_ENTER,
|
||||
LIFECYCLE_WILL_LEAVE,
|
||||
LIFECYCLE_WILL_UNLOAD,
|
||||
} from '@ionic/core/components';
|
||||
} from '@ionic/core';
|
||||
|
||||
import { NavParams } from '../directives/navigation/nav-params';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { createAnimation, getTimeGivenProgression } from '@ionic/core';
|
||||
import type { Animation } from '@ionic/core';
|
||||
import { Animation, createAnimation, getTimeGivenProgression } from '@ionic/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable, InjectionToken } from '@angular/core';
|
||||
import type { Config as CoreConfig, IonicConfig } from '@ionic/core/components';
|
||||
import { Config as CoreConfig, IonicConfig } from '@ionic/core';
|
||||
|
||||
import { IonicWindow } from '../types/interfaces';
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Injectable, NgZone } from '@angular/core';
|
||||
import type { Gesture, GestureConfig } from '@ionic/core';
|
||||
import { createGesture } from '@ionic/core';
|
||||
import { NgZone, Injectable } from '@angular/core';
|
||||
import { Gesture, GestureConfig, createGesture } from '@ionic/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { LoadingOptions } from '@ionic/core';
|
||||
import { loadingController } from '@ionic/core';
|
||||
import { LoadingOptions, loadingController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -1,15 +1,17 @@
|
||||
import type { MenuControllerI } from '@ionic/core/components';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { menuController } from '@ionic/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class MenuController {
|
||||
constructor(private menuController: MenuControllerI) {}
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
return this.menuController.open(menuId);
|
||||
return menuController.open(menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -20,7 +22,7 @@ export class MenuController {
|
||||
* @return returns a promise when the menu is fully closed
|
||||
*/
|
||||
close(menuId?: string): Promise<boolean> {
|
||||
return this.menuController.close(menuId);
|
||||
return menuController.close(menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,7 +32,7 @@ export class MenuController {
|
||||
* @return returns a promise when the menu has been toggled
|
||||
*/
|
||||
toggle(menuId?: string): Promise<boolean> {
|
||||
return this.menuController.toggle(menuId);
|
||||
return menuController.toggle(menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,7 +44,7 @@ export class MenuController {
|
||||
* @return Returns the instance of the menu, which is useful for chaining.
|
||||
*/
|
||||
enable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement | undefined> {
|
||||
return this.menuController.enable(shouldEnable, menuId);
|
||||
return menuController.enable(shouldEnable, menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,7 +54,7 @@ export class MenuController {
|
||||
* @return Returns the instance of the menu, which is useful for chaining.
|
||||
*/
|
||||
swipeGesture(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement | undefined> {
|
||||
return this.menuController.swipeGesture(shouldEnable, menuId);
|
||||
return menuController.swipeGesture(shouldEnable, menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,7 +63,7 @@ export class MenuController {
|
||||
* If the menuId is not specified, it returns true if ANY menu is currenly open.
|
||||
*/
|
||||
isOpen(menuId?: string): Promise<boolean> {
|
||||
return this.menuController.isOpen(menuId);
|
||||
return menuController.isOpen(menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +71,7 @@ export class MenuController {
|
||||
* @return Returns true if the menu is currently enabled, otherwise false.
|
||||
*/
|
||||
isEnabled(menuId?: string): Promise<boolean> {
|
||||
return this.menuController.isEnabled(menuId);
|
||||
return menuController.isEnabled(menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,20 +84,20 @@ export class MenuController {
|
||||
* @return Returns the instance of the menu if found, otherwise `null`.
|
||||
*/
|
||||
get(menuId?: string): Promise<HTMLIonMenuElement | undefined> {
|
||||
return this.menuController.get(menuId);
|
||||
return menuController.get(menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the instance of the menu already opened, otherwise `null`.
|
||||
*/
|
||||
getOpen(): Promise<HTMLIonMenuElement | undefined> {
|
||||
return this.menuController.getOpen();
|
||||
return menuController.getOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns an array of all menu instances.
|
||||
*/
|
||||
getMenus(): Promise<HTMLIonMenuElement[]> {
|
||||
return this.menuController.getMenus();
|
||||
return menuController.getMenus();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Injector, Injectable, EnvironmentInjector, inject } from '@angular/core';
|
||||
import { AngularDelegate, OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { ModalOptions } from '@ionic/core';
|
||||
import { modalController } from '@ionic/core';
|
||||
import { ModalOptions, modalController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
import { AngularDelegate } from './angular-delegate';
|
||||
|
||||
@Injectable()
|
||||
export class ModalController extends OverlayBaseController<ModalOptions, HTMLIonModalElement> {
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { Injectable, Optional } from '@angular/core';
|
||||
import { NavigationExtras, Router, UrlSerializer, UrlTree, NavigationStart } from '@angular/router';
|
||||
import type { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core/components';
|
||||
import { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core';
|
||||
|
||||
import { IonRouterOutlet } from '../directives/navigation/router-outlet';
|
||||
import { IonRouterOutlet } from '../directives/navigation/ion-router-outlet';
|
||||
|
||||
import { Platform } from './platform';
|
||||
|
||||
@@ -131,21 +131,17 @@ 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`.
|
||||
*
|
||||
* Resolves to `true` if it was able to pop.
|
||||
*/
|
||||
async pop(): Promise<boolean> {
|
||||
async pop(): Promise<void> {
|
||||
let outlet = this.topOutlet;
|
||||
|
||||
while (outlet) {
|
||||
if (await outlet.pop()) {
|
||||
return true;
|
||||
break;
|
||||
} else {
|
||||
outlet = outlet.parentOutlet;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { PickerOptions } from '@ionic/core';
|
||||
import { pickerController } from '@ionic/core';
|
||||
import { PickerOptions, pickerController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -1,7 +1,6 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { NgZone, Inject, Injectable } from '@angular/core';
|
||||
import { getPlatforms, isPlatform } from '@ionic/core/components';
|
||||
import type { BackButtonEventDetail, KeyboardEventDetail, Platforms } from '@ionic/core/components';
|
||||
import { BackButtonEventDetail, KeyboardEventDetail, Platforms, getPlatforms, isPlatform } from '@ionic/core';
|
||||
import { Subscription, Subject } from 'rxjs';
|
||||
|
||||
// TODO(FW-2827): types
|
||||
@@ -23,13 +22,13 @@ export class Platform {
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
backButton = new Subject<BackButtonEventDetail>() as BackButtonEmitter;
|
||||
backButton: BackButtonEmitter = new Subject<BackButtonEventDetail>() as any;
|
||||
|
||||
/**
|
||||
* The keyboardDidShow event emits when the
|
||||
* on-screen keyboard is presented.
|
||||
*/
|
||||
keyboardDidShow = new Subject<KeyboardEventDetail>();
|
||||
keyboardDidShow = new Subject<KeyboardEventDetail>() as any;
|
||||
|
||||
/**
|
||||
* The keyboardDidHide event emits when the
|
||||
@@ -68,12 +67,12 @@ export class Platform {
|
||||
});
|
||||
};
|
||||
|
||||
proxyEvent(this.pause, doc, 'pause', zone);
|
||||
proxyEvent(this.resume, doc, 'resume', zone);
|
||||
proxyEvent(this.backButton, doc, 'ionBackButton', zone);
|
||||
proxyEvent(this.resize, this.win, 'resize', zone);
|
||||
proxyEvent(this.keyboardDidShow, this.win, 'ionKeyboardDidShow', zone);
|
||||
proxyEvent(this.keyboardDidHide, this.win, 'ionKeyboardDidHide', zone);
|
||||
proxyEvent(this.pause, doc, 'pause');
|
||||
proxyEvent(this.resume, doc, 'resume');
|
||||
proxyEvent(this.backButton, doc, 'ionBackButton');
|
||||
proxyEvent(this.resize, this.win, 'resize');
|
||||
proxyEvent(this.keyboardDidShow, this.win, 'ionKeyboardDidShow');
|
||||
proxyEvent(this.keyboardDidHide, this.win, 'ionKeyboardDidHide');
|
||||
|
||||
let readyResolve: (value: string) => void;
|
||||
this._readyPromise = new Promise((res) => {
|
||||
@@ -262,20 +261,12 @@ const readQueryParam = (url: string, key: string) => {
|
||||
return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null;
|
||||
};
|
||||
|
||||
const proxyEvent = <T>(emitter: Subject<T>, el: EventTarget, eventName: string, zone: NgZone) => {
|
||||
const proxyEvent = <T>(emitter: Subject<T>, el: EventTarget, eventName: string) => {
|
||||
if (el) {
|
||||
el.addEventListener(eventName, (ev) => {
|
||||
/**
|
||||
* `zone.run` is required to make sure that we are running inside the Angular zone
|
||||
* at all times. This is necessary since an app that has Capacitor will
|
||||
* override the `document.addEventListener` with its own implementation.
|
||||
* The override causes the event to no longer be in the Angular zone.
|
||||
*/
|
||||
zone.run(() => {
|
||||
// ?? cordova might emit "null" events
|
||||
const value = ev != null ? (ev as any).detail : undefined;
|
||||
emitter.next(value);
|
||||
});
|
||||
// ?? cordova might emit "null" events
|
||||
const value = ev != null ? (ev as any).detail : undefined;
|
||||
emitter.next(value);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -1,8 +1,11 @@
|
||||
import { Injector, inject, EnvironmentInjector } from '@angular/core';
|
||||
import { AngularDelegate, OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { PopoverOptions } from '@ionic/core';
|
||||
import { popoverController } from '@ionic/core';
|
||||
import { Injector, Injectable, inject, EnvironmentInjector } from '@angular/core';
|
||||
import { PopoverOptions, popoverController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
import { AngularDelegate } from './angular-delegate';
|
||||
|
||||
@Injectable()
|
||||
export class PopoverController extends OverlayBaseController<PopoverOptions, HTMLIonPopoverElement> {
|
||||
private angularDelegate = inject(AngularDelegate);
|
||||
private injector = inject(Injector);
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { OverlayBaseController } from '@ionic/angular/common';
|
||||
import type { ToastOptions } from '@ionic/core';
|
||||
import { toastController } from '@ionic/core';
|
||||
import { ToastOptions, toastController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -0,0 +1,6 @@
|
||||
/* Ionic Variables and Theming. */
|
||||
/* This is just a placeholder file For more info, please see: */
|
||||
/* https://ionicframework.com/docs/theming/basics */
|
||||
|
||||
/* To quickly generate your own theme, check out the color generator */
|
||||
/* https://ionicframework.com/docs/theming/color-generator */
|
||||
@@ -12,19 +12,10 @@ import {
|
||||
url,
|
||||
} from '@angular-devkit/schematics';
|
||||
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
|
||||
import { addRootProvider } from '@schematics/angular/utility';
|
||||
import { getWorkspace } from '@schematics/angular/utility/workspace';
|
||||
|
||||
import { addIonicModuleImportToNgModule } from '../utils/ast';
|
||||
|
||||
import {
|
||||
addArchitectBuilder,
|
||||
addAsset,
|
||||
addCli,
|
||||
addSchematics,
|
||||
addStyle,
|
||||
getDefaultAngularAppName,
|
||||
} from './../utils/config';
|
||||
import { addModuleImportToRootModule } from './../utils/ast';
|
||||
import { addArchitectBuilder, addAsset, addStyle, getDefaultAngularAppName } from './../utils/config';
|
||||
import { addPackageToPackageJson } from './../utils/package';
|
||||
import { Schema as IonAddOptions } from './schema';
|
||||
|
||||
@@ -42,53 +33,9 @@ function addIonicAngularToolkitToPackageJson(): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the @ionic/angular-toolkit schematics and cli configuration to the project's `angular.json` file.
|
||||
* @param projectName The name of the project.
|
||||
*/
|
||||
function addIonicAngularToolkitToAngularJson(): Rule {
|
||||
return (host: Tree) => {
|
||||
addCli(host, '@ionic/angular-toolkit');
|
||||
addSchematics(host, '@ionic/angular-toolkit:component', {
|
||||
styleext: 'scss',
|
||||
});
|
||||
addSchematics(host, '@ionic/angular-toolkit:page', {
|
||||
styleext: 'scss',
|
||||
});
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the `IonicModule.forRoot()` usage to the project's `AppModule`.
|
||||
* If the project does not use modules this will operate as a noop.
|
||||
* @param projectSourceRoot The source root path of the project.
|
||||
*/
|
||||
function addIonicAngularModuleToAppModule(projectSourceRoot: Path): Rule {
|
||||
return (host: Tree) => {
|
||||
const appModulePath = `${projectSourceRoot}/app/app.module.ts`;
|
||||
if (host.exists(appModulePath)) {
|
||||
addIonicModuleImportToNgModule(host, appModulePath);
|
||||
}
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the `provideIonicAngular` usage to the project's app config.
|
||||
* If the project does not use an app config this will operate as a noop.
|
||||
* @param projectName The name of the project.
|
||||
* @param projectSourceRoot The source root path of the project.
|
||||
*/
|
||||
function addProvideIonicAngular(projectName: string, projectSourceRoot: Path): Rule {
|
||||
return (host: Tree) => {
|
||||
const appConfig = `${projectSourceRoot}/app/app.config.ts`;
|
||||
if (host.exists(appConfig)) {
|
||||
return addRootProvider(
|
||||
projectName,
|
||||
({ code, external }) => code`${external('provideIonicAngular', '@ionic/angular/standalone')}({})`
|
||||
);
|
||||
}
|
||||
addModuleImportToRootModule(host, projectSourceRoot, 'IonicModule.forRoot()', '@ionic/angular');
|
||||
return host;
|
||||
};
|
||||
}
|
||||
@@ -116,49 +63,15 @@ function addIonicStyles(projectName: string, projectSourceRoot: Path): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
function addIonicons(projectName: string, projectSourceRoot: Path): Rule {
|
||||
function addIonicons(projectName: string): Rule {
|
||||
return (host: Tree) => {
|
||||
const hasAppModule = host.exists(`${projectSourceRoot}/app/app.module.ts`);
|
||||
|
||||
if (hasAppModule) {
|
||||
/**
|
||||
* Add Ionicons to the `angular.json` file only if the project
|
||||
* is using the lazy build of `@ionic/angular` with modules.
|
||||
*/
|
||||
const ioniconsGlob = {
|
||||
glob: '**/*.svg',
|
||||
input: 'node_modules/ionicons/dist/ionicons/svg',
|
||||
output: './svg',
|
||||
};
|
||||
addAsset(host, projectName, 'build', ioniconsGlob);
|
||||
addAsset(host, projectName, 'test', ioniconsGlob);
|
||||
}
|
||||
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
function addIonicConfig(projectSourceRoot: string): Rule {
|
||||
return (host: Tree) => {
|
||||
const ionicConfig = 'ionic.config.json';
|
||||
if (!host.exists(ionicConfig)) {
|
||||
const hasAppModule = host.exists(`${projectSourceRoot}/app/app.module.ts`);
|
||||
const type = hasAppModule ? 'angular' : 'angular-standalone';
|
||||
|
||||
host.create(
|
||||
ionicConfig,
|
||||
JSON.stringify(
|
||||
{
|
||||
name: 'ionic-app',
|
||||
app_id: '',
|
||||
type,
|
||||
integrations: {},
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
);
|
||||
}
|
||||
const ioniconsGlob = {
|
||||
glob: '**/*.svg',
|
||||
input: 'node_modules/ionicons/dist/ionicons/svg',
|
||||
output: './svg',
|
||||
};
|
||||
addAsset(host, projectName, 'build', ioniconsGlob);
|
||||
addAsset(host, projectName, 'test', ioniconsGlob);
|
||||
return host;
|
||||
};
|
||||
}
|
||||
@@ -216,13 +129,10 @@ export default function ngAdd(options: IonAddOptions): Rule {
|
||||
// @ionic/angular
|
||||
addIonicAngularToPackageJson(),
|
||||
addIonicAngularToolkitToPackageJson(),
|
||||
addIonicAngularToolkitToAngularJson(),
|
||||
addIonicAngularModuleToAppModule(sourcePath),
|
||||
addProvideIonicAngular(options.project, sourcePath),
|
||||
addIonicBuilder(options.project),
|
||||
addIonicStyles(options.project, sourcePath),
|
||||
addIonicons(options.project, sourcePath),
|
||||
addIonicConfig(sourcePath),
|
||||
addIonicons(options.project),
|
||||
mergeWith(rootTemplateSource),
|
||||
// install freshly added dependencies
|
||||
installNodeDeps(),
|
||||
52
angular/src/schematics/utils/ast.ts
Normal file
52
angular/src/schematics/utils/ast.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
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';
|
||||
|
||||
/**
|
||||
* Reads file given path and returns TypeScript source file.
|
||||
*/
|
||||
export function getSourceFile(host: Tree, path: string): ts.SourceFile {
|
||||
const buffer = host.read(path);
|
||||
if (!buffer) {
|
||||
throw new SchematicsException(`Could not find file for path: ${path}`);
|
||||
}
|
||||
const content = buffer.toString();
|
||||
const source = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);
|
||||
return source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import and add module to root app module.
|
||||
*/
|
||||
export function addModuleImportToRootModule(
|
||||
host: Tree,
|
||||
projectSourceRoot: string,
|
||||
moduleName: string,
|
||||
importSrc: string
|
||||
): void {
|
||||
addModuleImportToModule(host, normalize(`${projectSourceRoot}/app/app.module.ts`), moduleName, importSrc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import and add module to specific module path.
|
||||
* @param host the tree we are updating
|
||||
* @param modulePath src location of the module to import
|
||||
* @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 {
|
||||
const moduleSource = getSourceFile(host, modulePath);
|
||||
const changes = addImportToModule(moduleSource, modulePath, moduleName, src);
|
||||
const recorder = host.beginUpdate(modulePath);
|
||||
|
||||
changes.forEach((change) => {
|
||||
if (change instanceof InsertChange) {
|
||||
recorder.insertLeft(change.pos, change.toAdd);
|
||||
}
|
||||
});
|
||||
|
||||
host.commitUpdate(recorder);
|
||||
}
|
||||
@@ -1,28 +1,24 @@
|
||||
import type { JsonObject } from '@angular-devkit/core';
|
||||
import { WorkspaceDefinition } from '@angular-devkit/core/src/workspace';
|
||||
import { Tree, SchematicsException } from '@angular-devkit/schematics';
|
||||
import type { SchematicOptions } from '@angular/cli/lib/config/workspace-schema';
|
||||
import { parse } from 'jsonc-parser';
|
||||
|
||||
const ANGULAR_JSON_PATH = 'angular.json';
|
||||
const CONFIG_PATH = 'angular.json';
|
||||
|
||||
export function readConfig<T extends JsonObject = JsonObject>(host: Tree): T {
|
||||
return host.readJson(ANGULAR_JSON_PATH) as T;
|
||||
// TODO(FW-2827): types
|
||||
|
||||
export function readConfig(host: Tree): any {
|
||||
const sourceText = host.read(CONFIG_PATH)?.toString('utf-8');
|
||||
return JSON.parse(sourceText);
|
||||
}
|
||||
|
||||
export function writeConfig(host: Tree, config: JsonObject): void {
|
||||
host.overwrite(ANGULAR_JSON_PATH, JSON.stringify(config, null, 2));
|
||||
export function writeConfig(host: Tree, config: JSON): void {
|
||||
host.overwrite(CONFIG_PATH, JSON.stringify(config, null, 2));
|
||||
}
|
||||
|
||||
function isAngularBrowserProject(projectConfig: any): boolean {
|
||||
if (projectConfig.projectType === 'application') {
|
||||
const buildConfig = projectConfig.architect.build;
|
||||
// Angular 16 and lower
|
||||
const legacyAngularBuilder = buildConfig.builder === '@angular-devkit/build-angular:browser';
|
||||
// Angular 17+
|
||||
const modernAngularBuilder = buildConfig.builder === '@angular-devkit/build-angular:application';
|
||||
|
||||
return legacyAngularBuilder || modernAngularBuilder;
|
||||
return buildConfig.builder === '@angular-devkit/build-angular:browser';
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -42,7 +38,7 @@ export function getDefaultAngularAppName(config: any): string {
|
||||
return projectNames[0];
|
||||
}
|
||||
|
||||
function getAngularJson(config: any, projectName: string): any | never {
|
||||
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}`);
|
||||
@@ -63,8 +59,8 @@ function getAngularJson(config: any, projectName: string): any | never {
|
||||
|
||||
export function addStyle(host: Tree, projectName: string, stylePath: string): void {
|
||||
const config = readConfig(host);
|
||||
const angularJson = getAngularJson(config, projectName);
|
||||
angularJson.architect.build.options.styles.push({
|
||||
const appConfig = getAngularAppConfig(config, projectName);
|
||||
appConfig.architect.build.options.styles.push({
|
||||
input: stylePath,
|
||||
});
|
||||
writeConfig(host, config);
|
||||
@@ -77,8 +73,8 @@ export function addAsset(
|
||||
asset: string | { glob: string; input: string; output: string }
|
||||
): void {
|
||||
const config = readConfig(host);
|
||||
const angularJson = getAngularJson(config, projectName);
|
||||
const target = angularJson.architect[architect];
|
||||
const appConfig = getAngularAppConfig(config, projectName);
|
||||
const target = appConfig.architect[architect];
|
||||
if (target) {
|
||||
target.options.assets.push(asset);
|
||||
writeConfig(host, config);
|
||||
@@ -92,48 +88,11 @@ export function addArchitectBuilder(
|
||||
builderOpts: any
|
||||
): void | never {
|
||||
const config = readConfig(host);
|
||||
const angularJson = getAngularJson(config, projectName);
|
||||
angularJson.architect[builderName] = builderOpts;
|
||||
const appConfig = getAngularAppConfig(config, projectName);
|
||||
appConfig.architect[builderName] = builderOpts;
|
||||
writeConfig(host, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the angular.json to add an additional schematic collection
|
||||
* to the CLI configuration.
|
||||
*/
|
||||
export function addCli(host: Tree, collectionName: string): void | never {
|
||||
const angularJson = readConfig<any>(host);
|
||||
|
||||
if (angularJson.cli === undefined) {
|
||||
angularJson.cli = {};
|
||||
}
|
||||
|
||||
if (angularJson.cli.schematicCollections === undefined) {
|
||||
angularJson.cli.schematicCollections = [];
|
||||
}
|
||||
|
||||
angularJson.cli.schematicCollections.push(collectionName);
|
||||
|
||||
writeConfig(host, angularJson);
|
||||
}
|
||||
|
||||
// TODO(FW-5639): can remove [property: string]: any; when upgrading @angular/cli dev-dep to v16 or later
|
||||
export function addSchematics(
|
||||
host: Tree,
|
||||
schematicName: string,
|
||||
schematicOpts: SchematicOptions & { [property: string]: any }
|
||||
): void | never {
|
||||
const angularJson = readConfig<any>(host);
|
||||
|
||||
if (angularJson.schematics === undefined) {
|
||||
angularJson.schematics = {};
|
||||
}
|
||||
|
||||
angularJson.schematics[schematicName] = schematicOpts;
|
||||
|
||||
writeConfig(host, angularJson);
|
||||
}
|
||||
|
||||
export function getWorkspacePath(host: Tree): string {
|
||||
const possibleFiles = ['/angular.json', '/.angular.json'];
|
||||
const path = possibleFiles.filter((path) => host.exists(path))[0];
|
||||
579
angular/src/schematics/utils/devkit-utils/ast-utils.ts
Normal file
579
angular/src/schematics/utils/devkit-utils/ast-utils.ts
Normal file
@@ -0,0 +1,579 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* 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.
|
||||
* @param fileToEdit (file we want to add import to)
|
||||
* @param symbolName (item to import)
|
||||
* @param fileName (path to the file)
|
||||
* @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 {
|
||||
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) => {
|
||||
// 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);
|
||||
|
||||
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));
|
||||
if (findNodes(n, ts.SyntaxKind.AsteriskToken).length > 0) {
|
||||
importsAsterisk = true;
|
||||
}
|
||||
});
|
||||
|
||||
// if imports * from fileName, don't add symbolName
|
||||
if (importsAsterisk) {
|
||||
return new NoopChange();
|
||||
}
|
||||
|
||||
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.FromKeyword)[0].getStart();
|
||||
|
||||
return insertAfterLastOccurrence(imports, `, ${symbolName}`, fileToEdit, fallbackPos);
|
||||
}
|
||||
|
||||
return new NoopChange();
|
||||
}
|
||||
|
||||
// no such import declaration exists
|
||||
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;
|
||||
}
|
||||
const open = isDefault ? '' : '{ ';
|
||||
const close = isDefault ? '' : ' }';
|
||||
// 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' : ''}`;
|
||||
|
||||
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
|
||||
* @param kind
|
||||
* @param max The maximum number of items to return.
|
||||
* @return all nodes of kind, or [] if none is found
|
||||
*/
|
||||
export function findNodes(node: ts.Node, kind: ts.SyntaxKind, max = Infinity): ts.Node[] {
|
||||
if (!node || max == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const arr: ts.Node[] = [];
|
||||
if (node.kind === kind) {
|
||||
arr.push(node);
|
||||
max--;
|
||||
}
|
||||
if (max > 0) {
|
||||
for (const child of node.getChildren()) {
|
||||
findNodes(child, kind, max).forEach((node) => {
|
||||
if (max > 0) {
|
||||
arr.push(node);
|
||||
}
|
||||
max--;
|
||||
});
|
||||
|
||||
if (max <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the nodes from a source.
|
||||
* @param sourceFile The source file object.
|
||||
* @returns {Observable<ts.Node>} An observable of all the nodes in the source.
|
||||
*/
|
||||
export function getSourceNodes(sourceFile: ts.SourceFile): ts.Node[] {
|
||||
const nodes: ts.Node[] = [sourceFile];
|
||||
const result = [];
|
||||
|
||||
while (nodes.length > 0) {
|
||||
const node = nodes.shift();
|
||||
|
||||
if (node) {
|
||||
result.push(node);
|
||||
if (node.getChildCount(sourceFile) >= 0) {
|
||||
nodes.unshift(...node.getChildren());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function findNode(node: ts.Node, kind: ts.SyntaxKind, text: string): ts.Node | null {
|
||||
if (node.kind === kind && node.getText() === text) {
|
||||
// throw new Error(node.getText());
|
||||
return node;
|
||||
}
|
||||
|
||||
let foundNode: ts.Node | null = null;
|
||||
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
|
||||
*/
|
||||
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
|
||||
* of ts.SyntaxKind[nodes[i].kind] and save the changes in file.
|
||||
*
|
||||
* @param nodes insert after the last occurence of nodes
|
||||
* @param toInsert string to insert
|
||||
* @param file file to insert changes into
|
||||
* @param fallbackPos position to insert if toInsert happens to be the first occurence
|
||||
* @param syntaxKind the ts.SyntaxKind of the subchildren to insert after
|
||||
* @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 {
|
||||
// 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) {
|
||||
throw new Error();
|
||||
}
|
||||
if (syntaxKind) {
|
||||
lastItem = findNodes(lastItem, syntaxKind).sort(nodesByPosition).pop();
|
||||
}
|
||||
if (!lastItem && fallbackPos == undefined) {
|
||||
throw new Error(`tried to insert ${toInsert} as first occurence with no fallback position`);
|
||||
}
|
||||
const lastItemPosition: number = lastItem ? lastItem.getEnd() : fallbackPos;
|
||||
|
||||
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;
|
||||
} else if (node.kind == ts.SyntaxKind.StringLiteral) {
|
||||
return (node as ts.StringLiteral).text;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function _angularImportsFromNode(
|
||||
node: ts.ImportDeclaration,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_sourceFile: ts.SourceFile
|
||||
): { [name: string]: string } {
|
||||
const ms = node.moduleSpecifier;
|
||||
let modulePath: string;
|
||||
switch (ms.kind) {
|
||||
case ts.SyntaxKind.StringLiteral:
|
||||
modulePath = (ms as ts.StringLiteral).text;
|
||||
break;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!modulePath.startsWith('@angular/')) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (node.importClause) {
|
||||
if (node.importClause.name) {
|
||||
// This is of the form `import Name from 'path'`. Ignore.
|
||||
return {};
|
||||
} else if (node.importClause.namedBindings) {
|
||||
const nb = node.importClause.namedBindings;
|
||||
if (nb.kind == ts.SyntaxKind.NamespaceImport) {
|
||||
// This is of the form `import * as name from 'path'`. Return `name.`.
|
||||
return {
|
||||
[(nb as ts.NamespaceImport).name.text + '.']: modulePath,
|
||||
};
|
||||
} else {
|
||||
// This is of the form `import {a,b,c} from 'path'`
|
||||
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) => {
|
||||
acc[curr] = modulePath;
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
} else {
|
||||
// This is of the form `import 'path';`. Nothing to do.
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
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 }) => {
|
||||
for (const key of Object.keys(current)) {
|
||||
acc[key] = current[key];
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return getSourceNodes(source)
|
||||
.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) => {
|
||||
if (expr.expression.kind == ts.SyntaxKind.Identifier) {
|
||||
const id = expr.expression as ts.Identifier;
|
||||
|
||||
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;
|
||||
// If the left expression is not an identifier, just give up at that point.
|
||||
if (paExpr.expression.kind !== ts.SyntaxKind.Identifier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const id = paExpr.name.text;
|
||||
const moduleId = (paExpr.expression as ts.Identifier).getText(source);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
function findClassDeclarationParent(node: ts.Node): ts.ClassDeclaration | undefined {
|
||||
if (ts.isClassDeclaration(node)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
return node.parent && findClassDeclarationParent(node.parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a source file with @NgModule class(es), find the name of the first @NgModule class.
|
||||
*
|
||||
* @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 {
|
||||
// First, find the @NgModule decorators.
|
||||
const ngModulesMetadata = getDecoratorMetadata(source, 'NgModule', '@angular/core');
|
||||
if (ngModulesMetadata.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Then walk parent pointers up the AST, looking for the ClassDeclaration parent of the NgModule
|
||||
// metadata.
|
||||
const moduleClass = findClassDeclarationParent(ngModulesMetadata[0]);
|
||||
if (!moduleClass?.name) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Get the class name of the module ClassDeclaration.
|
||||
return moduleClass.name.text;
|
||||
}
|
||||
|
||||
export function addSymbolToNgModuleMetadata(
|
||||
source: ts.SourceFile,
|
||||
ngModulePath: string,
|
||||
metadataField: string,
|
||||
symbolName: string,
|
||||
importPath: string | null = null
|
||||
): Change[] {
|
||||
const nodes = getDecoratorMetadata(source, 'NgModule', '@angular/core');
|
||||
let node: any = nodes[0]; // tslint:disable-line:no-any
|
||||
|
||||
// Find the decorator declaration.
|
||||
if (!node) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Filter out every fields that's not "metadataField". Also handles string literals
|
||||
// (but not expressions).
|
||||
.filter((prop: ts.PropertyAssignment) => {
|
||||
const name = prop.name;
|
||||
switch (name.kind) {
|
||||
case ts.SyntaxKind.Identifier:
|
||||
return (name as ts.Identifier).getText(source) == metadataField;
|
||||
case ts.SyntaxKind.StringLiteral:
|
||||
return (name as ts.StringLiteral).text == metadataField;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Get the last node of the array literal.
|
||||
if (!matchingProperties) {
|
||||
return [];
|
||||
}
|
||||
if (matchingProperties.length == 0) {
|
||||
// We haven't found the field in the metadata declaration. Insert a new field.
|
||||
const expr = node as ts.ObjectLiteralExpression;
|
||||
let position: number;
|
||||
let toInsert: string;
|
||||
if (expr.properties.length == 0) {
|
||||
position = expr.getEnd() - 1;
|
||||
toInsert = ` ${metadataField}: [${symbolName}]\n`;
|
||||
} else {
|
||||
node = expr.properties[expr.properties.length - 1];
|
||||
position = node.getEnd();
|
||||
// Get the indentation of the last element, if any.
|
||||
const text = node.getFullText(source);
|
||||
const matches = text.match(/^\r?\n\s*/);
|
||||
if (matches.length > 0) {
|
||||
toInsert = `,${matches[0]}${metadataField}: [${symbolName}]`;
|
||||
} else {
|
||||
toInsert = `, ${metadataField}: [${symbolName}]`;
|
||||
}
|
||||
}
|
||||
if (importPath !== null) {
|
||||
return [
|
||||
new InsertChange(ngModulePath, position, toInsert),
|
||||
insertImport(source, ngModulePath, symbolName.replace(/\..*$/, ''), importPath),
|
||||
];
|
||||
} else {
|
||||
return [new InsertChange(ngModulePath, position, toInsert)];
|
||||
}
|
||||
}
|
||||
const assignment = matchingProperties[0] as ts.PropertyAssignment;
|
||||
|
||||
// If it's not an array, nothing we can do really.
|
||||
if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const arrLiteral = assignment.initializer as ts.ArrayLiteralExpression;
|
||||
if (arrLiteral.elements.length == 0) {
|
||||
// Forward the property.
|
||||
node = arrLiteral;
|
||||
} else {
|
||||
node = arrLiteral.elements;
|
||||
}
|
||||
|
||||
if (!node) {
|
||||
console.log('No app module found. Please add your new class to your component.');
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
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());
|
||||
if (symbolsArray.includes(symbolName)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
node = node[node.length - 1];
|
||||
}
|
||||
|
||||
let toInsert: string;
|
||||
let position = node.getEnd();
|
||||
if (node.kind == ts.SyntaxKind.ObjectLiteralExpression) {
|
||||
// We haven't found the field in the metadata declaration. Insert a new
|
||||
// field.
|
||||
const expr = node as ts.ObjectLiteralExpression;
|
||||
if (expr.properties.length == 0) {
|
||||
position = expr.getEnd() - 1;
|
||||
toInsert = ` ${metadataField}: [${symbolName}]\n`;
|
||||
} else {
|
||||
node = expr.properties[expr.properties.length - 1];
|
||||
position = node.getEnd();
|
||||
// Get the indentation of the last element, if any.
|
||||
const text = node.getFullText(source);
|
||||
if (text.match('^\r?\r?\n')) {
|
||||
toInsert = `,${text.match(/^\r?\n\s+/)[0]}${metadataField}: [${symbolName}]`;
|
||||
} else {
|
||||
toInsert = `, ${metadataField}: [${symbolName}]`;
|
||||
}
|
||||
}
|
||||
} else if (node.kind == ts.SyntaxKind.ArrayLiteralExpression) {
|
||||
// We found the field but it's empty. Insert it just before the `]`.
|
||||
position--;
|
||||
toInsert = `${symbolName}`;
|
||||
} else {
|
||||
// Get the indentation of the last element, if any.
|
||||
const text = node.getFullText(source);
|
||||
if (text.match(/^\r?\n/)) {
|
||||
toInsert = `,${text.match(/^\r?\n(\r?)\s+/)[0]}${symbolName}`;
|
||||
} else {
|
||||
toInsert = `, ${symbolName}`;
|
||||
}
|
||||
}
|
||||
if (importPath !== null) {
|
||||
return [
|
||||
new InsertChange(ngModulePath, position, toInsert),
|
||||
insertImport(source, ngModulePath, symbolName.replace(/\..*$/, ''), importPath),
|
||||
];
|
||||
}
|
||||
|
||||
return [new InsertChange(ngModulePath, position, toInsert)];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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[] {
|
||||
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[] {
|
||||
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[] {
|
||||
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[] {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an import already exists.
|
||||
*/
|
||||
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((imp: ts.ImportDeclaration) => imp.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral)
|
||||
.filter((imp: ts.ImportDeclaration) => {
|
||||
return (imp.moduleSpecifier as ts.StringLiteral).text === importPath;
|
||||
})
|
||||
.filter((imp: ts.ImportDeclaration) => {
|
||||
if (!imp.importClause) {
|
||||
return false;
|
||||
}
|
||||
const nodes = findNodes(imp.importClause, ts.SyntaxKind.ImportSpecifier).filter(
|
||||
(n) => n.getText() === classifiedName
|
||||
);
|
||||
|
||||
return nodes.length > 0;
|
||||
});
|
||||
|
||||
return matchingNodes.length > 0;
|
||||
}
|
||||
123
angular/src/schematics/utils/devkit-utils/change.ts
Normal file
123
angular/src/schematics/utils/devkit-utils/change.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
export interface Host {
|
||||
write(path: string, content: string): Promise<void>;
|
||||
read(path: string): Promise<string>;
|
||||
}
|
||||
|
||||
export interface Change {
|
||||
apply(host: Host): Promise<void>;
|
||||
|
||||
// The file this change should be applied to. Some changes might not apply to
|
||||
// a file (maybe the config).
|
||||
readonly path: string | null;
|
||||
|
||||
// The order this change should be applied. Normally the position inside the file.
|
||||
// Changes are applied from the bottom of a file to the top.
|
||||
readonly order: number;
|
||||
|
||||
// The description of this change. This will be outputted in a dry or verbose run.
|
||||
readonly description: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An operation that does nothing.
|
||||
*/
|
||||
export class NoopChange implements Change {
|
||||
description = 'No operation.';
|
||||
order = Infinity;
|
||||
path = null;
|
||||
apply(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will add text to the source code.
|
||||
*/
|
||||
export class InsertChange implements Change {
|
||||
order: number;
|
||||
description: string;
|
||||
|
||||
constructor(public path: string, public pos: number, public toAdd: string) {
|
||||
if (pos < 0) {
|
||||
throw new Error('Negative positions are invalid');
|
||||
}
|
||||
this.description = `Inserted ${toAdd} into position ${pos} of ${path}`;
|
||||
this.order = pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) => {
|
||||
const prefix = content.substring(0, this.pos);
|
||||
const suffix = content.substring(this.pos);
|
||||
|
||||
return host.write(this.path, `${prefix}${this.toAdd}${suffix}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will remove text from the source code.
|
||||
*/
|
||||
export class RemoveChange implements Change {
|
||||
order: number;
|
||||
description: string;
|
||||
|
||||
constructor(public path: string, private pos: number, private toRemove: string) {
|
||||
if (pos < 0) {
|
||||
throw new Error('Negative positions are invalid');
|
||||
}
|
||||
this.description = `Removed ${toRemove} into position ${pos} of ${path}`;
|
||||
this.order = pos;
|
||||
}
|
||||
|
||||
apply(host: Host): Promise<void> {
|
||||
return host.read(this.path).then((content) => {
|
||||
const prefix = content.substring(0, this.pos);
|
||||
const suffix = content.substring(this.pos + this.toRemove.length);
|
||||
|
||||
// TODO: throw error if toRemove doesn't match removed string.
|
||||
return host.write(this.path, `${prefix}${suffix}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will replace text from the source code.
|
||||
*/
|
||||
export class ReplaceChange implements Change {
|
||||
order: number;
|
||||
description: string;
|
||||
|
||||
constructor(public path: string, private pos: number, private oldText: string, private newText: string) {
|
||||
if (pos < 0) {
|
||||
throw new Error('Negative positions are invalid');
|
||||
}
|
||||
this.description = `Replaced ${oldText} into position ${pos} of ${path} with ${newText}`;
|
||||
this.order = pos;
|
||||
}
|
||||
|
||||
apply(host: Host): Promise<void> {
|
||||
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);
|
||||
|
||||
if (text !== this.oldText) {
|
||||
return Promise.reject(new Error(`Invalid replace: "${text}" != "${this.oldText}".`));
|
||||
}
|
||||
|
||||
// TODO: throw error if oldText doesn't match removed string.
|
||||
return host.write(this.path, `${prefix}${this.newText}${suffix}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
5
angular/src/schematics/utils/devkit-utils/readme.md
Normal file
5
angular/src/schematics/utils/devkit-utils/readme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
### Devkit Utils
|
||||
|
||||
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.
|
||||
91
angular/src/schematics/utils/devkit-utils/route-utils.ts
Normal file
91
angular/src/schematics/utils/devkit-utils/route-utils.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* 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';
|
||||
|
||||
/**
|
||||
* Add Import `import { symbolName } from fileName` if the import doesn't exit
|
||||
* already. Assumes fileToEdit can be resolved and accessed.
|
||||
* @param fileToEdit (file we want to add import to)
|
||||
* @param symbolName (item to import)
|
||||
* @param fileName (path to the file)
|
||||
* @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 {
|
||||
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) => {
|
||||
// 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);
|
||||
|
||||
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));
|
||||
if (findNodes(n, ts.SyntaxKind.AsteriskToken).length > 0) {
|
||||
importsAsterisk = true;
|
||||
}
|
||||
});
|
||||
|
||||
// if imports * from fileName, don't add symbolName
|
||||
if (importsAsterisk) {
|
||||
return new NoopChange();
|
||||
}
|
||||
|
||||
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.FromKeyword)[0].getStart();
|
||||
|
||||
return insertAfterLastOccurrence(imports, `, ${symbolName}`, fileToEdit, fallbackPos);
|
||||
}
|
||||
|
||||
return new NoopChange();
|
||||
}
|
||||
|
||||
// no such import declaration exists
|
||||
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;
|
||||
}
|
||||
const open = isDefault ? '' : '{ ';
|
||||
const close = isDefault ? '' : ' }';
|
||||
// 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' : ''}`;
|
||||
|
||||
return insertAfterLastOccurrence(allImports, toInsert, fileToEdit, fallbackPos, ts.SyntaxKind.StringLiteral);
|
||||
}
|
||||
@@ -7,3 +7,8 @@ export interface IonicWindow extends Window {
|
||||
Ionic: IonicGlobal;
|
||||
__zone_symbol__requestAnimationFrame?: (ts: FrameRequestCallback) => number;
|
||||
}
|
||||
|
||||
export interface HTMLStencilElement extends HTMLElement {
|
||||
componentOnReady?(): Promise<this>;
|
||||
forceUpdate?(): void;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user