Compare commits

..

21 Commits

Author SHA1 Message Date
Sean Perkins
1194f24756 chore: adjustments for unmounting 2023-10-06 13:48:02 -04:00
Sean Perkins
f49ce7a835 wip 2023-08-30 18:32:41 -04:00
Sean Perkins
1ab433be07 wip 2023-08-29 16:46:46 -04:00
Liam DeBeasi
fa4113117d fix(menu): emit ionMenuChange when re-mounted (#28049)
Issue number: resolves #28030

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

`ion-menu` registers itself with the menu controller when
`connectedCallback` fires and then unregisters itself when
`disconnectedCallback` fires. When the menu was removed from the DOM,
`disconnectedCallback` was not always being fired due to
https://github.com/ionic-team/stencil/issues/4070.

`ion-menu-button` checks to see if it should be visible by grabbing the
current menu in
314055cf7a/core/src/components/menu-button/menu-button.tsx (L74).

Since `disconnectedCallback` was not being fired, `ion-menu-button`
would still find the menu even when it was no longer in the DOM. In this
case, the menu was not being unregistered due to `disconnectedCallback`
not firing.

When the linked Stencil bug was resolved in Stencil 4.0.3, the menu
button started to disappear. This is happening because
`disconnectedCallback` is now correctly called when the menu is removed
from the DOM. Since `disconnectedCallback` is called, the menu is
un-registered from the menu controller, and the menu button can no
longer find it.

However, this revealed a long-standing bug where re-adding the menu
would not fire `ionMenuChange` again. As a result, the menu button
remained hidden.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- The menu now fires `ionMenuChange` on `connectedCallback` as long as
`componentDidLoad` has already been run.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev build: `7.3.2-dev.11692803611.15c1bc87`

---------

Co-authored-by: Sean Perkins <sean@ionic.io>
2023-08-24 14:37:49 +00:00
dependabot[bot]
f450f0a58a chore(deps-dev): Bump @capacitor/core from 5.2.3 to 5.3.0 in /core (#28053)
Bumps [@capacitor/core](https://github.com/ionic-team/capacitor) from
5.2.3 to 5.3.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ionic-team/capacitor/releases"><code>@​capacitor/core</code>'s
releases</a>.</em></p>
<blockquote>
<h2>5.3.0</h2>
<h1><a
href="https://github.com/ionic-team/capacitor/compare/5.2.3...5.3.0">5.3.0</a>
(2023-08-23)</h1>
<h3>Bug Fixes</h3>
<ul>
<li><strong>cookies:</strong> remove session cookies when initializing
the cookie manager (<a
href="037863bea6">037863b</a>)</li>
<li><strong>http:</strong> disconnect active connections if call or
bridge is destroyed (<a
href="a1ed6cc6f0">a1ed6cc</a>)</li>
<li><strong>http:</strong> return numbers and booleans as-is when
application/json is the content type (<a
href="03dd3f96c7">03dd3f9</a>)</li>
</ul>
<h3>Features</h3>
<ul>
<li>better support monorepos (<a
href="https://redirect.github.com/ionic-team/capacitor/issues/6811">#6811</a>)
(<a
href="ae35e29fb8">ae35e29</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/ionic-team/capacitor/blob/5.3.0/CHANGELOG.md"><code>@​capacitor/core</code>'s
changelog</a>.</em></p>
<blockquote>
<h1><a
href="https://github.com/ionic-team/capacitor/compare/5.2.3...5.3.0">5.3.0</a>
(2023-08-23)</h1>
<h3>Bug Fixes</h3>
<ul>
<li><strong>cookies:</strong> remove session cookies when initializing
the cookie manager (<a
href="037863bea6">037863b</a>)</li>
<li><strong>http:</strong> disconnect active connections if call or
bridge is destroyed (<a
href="a1ed6cc6f0">a1ed6cc</a>)</li>
<li><strong>http:</strong> return numbers and booleans as-is when
application/json is the content type (<a
href="03dd3f96c7">03dd3f9</a>)</li>
</ul>
<h3>Features</h3>
<ul>
<li>better support monorepos (<a
href="https://redirect.github.com/ionic-team/capacitor/issues/6811">#6811</a>)
(<a
href="ae35e29fb8">ae35e29</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="599aae4211"><code>599aae4</code></a>
Release 5.3.0</li>
<li><a
href="688767092e"><code>6887670</code></a>
chore: update files and workflows for 5.x branch (<a
href="https://redirect.github.com/ionic-team/capacitor/issues/6834">#6834</a>)</li>
<li><a
href="03dd3f96c7"><code>03dd3f9</code></a>
fix(http): return numbers and booleans as-is when application/json is
the con...</li>
<li><a
href="ae35e29fb8"><code>ae35e29</code></a>
feat: better support monorepos (<a
href="https://redirect.github.com/ionic-team/capacitor/issues/6811">#6811</a>)</li>
<li><a
href="037863bea6"><code>037863b</code></a>
fix(cookies): remove session cookies when initializing the cookie
manager</li>
<li><a
href="a1ed6cc6f0"><code>a1ed6cc</code></a>
fix(http): disconnect active connections if call or bridge is
destroyed</li>
<li>See full diff in <a
href="https://github.com/ionic-team/capacitor/compare/5.2.3...5.3.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@capacitor/core&package-manager=npm_and_yarn&previous-version=5.2.3&new-version=5.3.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-24 14:34:21 +00:00
Liam DeBeasi
314055cf7a merge release-7.3.1
Release 7.3.1
2023-08-23 09:23:03 -05:00
ionitron
b333bdffe6 chore(): update package lock files 2023-08-23 13:40:28 +00:00
ionitron
5701f7661e v7.3.1 2023-08-23 13:40:10 +00:00
Sean Perkins
2b5da93cdc chore(infra): clean build check will log recommended steps (#28044)
Issue number: N/A

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

When the CI process detects a diff between the output in CI after
running `npm run build` versus the checked out branch, it will log the
diff and exit. It isn't very clear to developers what they need to do or
why this job step exists.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Logs a prettier error message to the developer to let them know the
steps they can take when a diff is detected in CI

![CleanShot 2023-08-22 at 15 05
03](https://github.com/ionic-team/ionic-framework/assets/13732623/0397523e-5f4d-4ab6-8a2e-d9ecc4d085ba)

Example run:
https://github.com/ionic-team/ionic-framework/actions/runs/5942940488/job/16117074372?pr=28044

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
2023-08-22 20:04:16 +00:00
Damian Tarnawsky
e2be7fdf3a docs(picker): describe how to set the initial value of a picker column (#28034)
## What is the current behavior?
There is no description of how to set the value of a picker.

## What is the new behavior?
Readers can find this description in the documentation.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

---------

Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
2023-08-22 14:36:30 +00:00
Sean Perkins
0ac3df3f37 fix(react): avoid multiple invocations of onDidDismiss and onWillPresent (#28020)
Issue number: Resolves #28010

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

`onDidDismiss` and `onWillPresent` will fire twice when having a manual
binding in your implementation for inline overlays.

e.g.:
```tsx
<IonAlert onDidDismiss={() => console.log('hello world')} />
```

Will result in:
> hello world
> hello world

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- `onDidDismiss` and `onWillPresent` do not execute the callback handler
twice per invocation

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev-build: `7.3.1-dev.11692305436.16a4008f`
2023-08-22 14:23:04 +00:00
dependabot[bot]
5a1bbc9fa3 chore(deps): Bump @stencil/core from 4.0.3 to 4.1.0 in /core (#28037)
Bumps [@stencil/core](https://github.com/ionic-team/stencil) from 4.0.3
to 4.1.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ionic-team/stencil/releases"><code>@​stencil/core</code>'s
releases</a>.</em></p>
<blockquote>
<h2>🐟 4.1.0 (2023-08-21)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>runtime:</strong> adds a testing check to the forceUpdate
method (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4682">#4682</a>)
(<a
href="7e9544d4c9">7e9544d</a>)</li>
<li><strong>typings:</strong> add crossorigin html attr to img (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4686">#4686</a>)
(<a
href="65d60fbef1">65d60fb</a>),
closes <a
href="https://redirect.github.com/ionic-team/stencil/issues/4685">#4685</a></li>
</ul>
<h3>Features</h3>
<ul>
<li><strong>compiler:</strong> include <code>getAssetPath</code> in
generated export statement (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4683">#4683</a>)
(<a
href="821da79c3b">821da79</a>)</li>
<li><strong>config:</strong> add experimentalSlotFixes config value (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4652">#4652</a>)
(<a
href="392af26f08">392af26</a>)</li>
</ul>
<h2>🚣 4.0.5 (2023-08-14)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>compiler:</strong> match tsconfig include paths properly (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4676">#4676</a>)
(<a
href="664ecb78cb">664ecb7</a>),
closes <a
href="https://redirect.github.com/ionic-team/stencil/issues/4667">#4667</a></li>
</ul>
<h2>🍧 4.0.4 (2023-08-07)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>runtime:</strong> <code>forceUpdate</code> calls only
execute when in a browser env (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4591">#4591</a>)
(<a
href="b203263482">b203263</a>)</li>
<li><strong>typings:</strong> add additional transition events to
DOMAttributes (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4645">#4645</a>)
(<a
href="420052f26b">420052f</a>),
closes <a
href="https://redirect.github.com/ionic-team/stencil/issues/4643">#4643</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/ionic-team/stencil/blob/main/CHANGELOG.md"><code>@​stencil/core</code>'s
changelog</a>.</em></p>
<blockquote>
<h1>🐟 <a
href="https://github.com/ionic-team/stencil/compare/v4.0.5...v4.1.0">4.1.0</a>
(2023-08-21)</h1>
<h3>Bug Fixes</h3>
<ul>
<li><strong>runtime:</strong> adds a testing check to the forceUpdate
method (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4682">#4682</a>)
(<a
href="7e9544d4c9">7e9544d</a>)</li>
<li><strong>typings:</strong> add crossorigin html attr to img (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4686">#4686</a>)
(<a
href="65d60fbef1">65d60fb</a>),
closes <a
href="https://redirect.github.com/ionic-team/stencil/issues/4685">#4685</a></li>
</ul>
<h3>Features</h3>
<ul>
<li><strong>compiler:</strong> include <code>getAssetPath</code> in
generated export statement (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4683">#4683</a>)
(<a
href="821da79c3b">821da79</a>)</li>
<li><strong>config:</strong> add experimentalSlotFixes config value (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4652">#4652</a>)
(<a
href="392af26f08">392af26</a>)</li>
</ul>
<h2>🚣 <a
href="https://github.com/ionic-team/stencil/compare/v4.0.4...v4.0.5">4.0.5</a>
(2023-08-14)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>compiler:</strong> match tsconfig include paths properly (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4676">#4676</a>)
(<a
href="664ecb78cb">664ecb7</a>),
closes <a
href="https://redirect.github.com/ionic-team/stencil/issues/4667">#4667</a></li>
</ul>
<h2>🍧 <a
href="https://github.com/ionic-team/stencil/compare/v4.0.3...v4.0.4">4.0.4</a>
(2023-08-07)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>runtime:</strong> <code>forceUpdate</code> calls only
execute when in a browser env (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4591">#4591</a>)
(<a
href="b203263482">b203263</a>)</li>
<li><strong>typings:</strong> add additional transition events to
DOMAttributes (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4645">#4645</a>)
(<a
href="420052f26b">420052f</a>),
closes <a
href="https://redirect.github.com/ionic-team/stencil/issues/4643">#4643</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d44d97e167"><code>d44d97e</code></a>
🐟 v4.1.0</li>
<li><a
href="b75bfaad13"><code>b75bfaa</code></a>
chore(deps): update typescript-eslint to v6.4.0 (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4706">#4706</a>)</li>
<li><a
href="5b8cb70e95"><code>5b8cb70</code></a>
chore(deps): update dependency postcss to v8.4.28 (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4704">#4704</a>)</li>
<li><a
href="412915b711"><code>412915b</code></a>
chore(deps): update dependency puppeteer to v21.1.0 (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4707">#4707</a>)</li>
<li><a
href="3427c809b6"><code>3427c80</code></a>
chore(deps): update actions/setup-node action to v3.8.1 (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4703">#4703</a>)</li>
<li><a
href="bb2b651981"><code>bb2b651</code></a>
chore(deps): update dependency prettier to v3.0.2 (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4705">#4705</a>)</li>
<li><a
href="392af26f08"><code>392af26</code></a>
feat(config): add experimentalSlotFixes config value (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4652">#4652</a>)</li>
<li><a
href="0e947726ac"><code>0e94772</code></a>
chore(repo): stub pull request action (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4675">#4675</a>)</li>
<li><a
href="2629fab16e"><code>2629fab</code></a>
chore(deps): update dependency puppeteer to v21.0.3 (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4689">#4689</a>)</li>
<li><a
href="2b7ea71fd3"><code>2b7ea71</code></a>
chore(deps): update dependency eslint to v8.47.0 (<a
href="https://redirect.github.com/ionic-team/stencil/issues/4692">#4692</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/ionic-team/stencil/compare/v4.0.3...v4.1.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@stencil/core&package-manager=npm_and_yarn&previous-version=4.0.3&new-version=4.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-22 14:22:50 +00:00
Wyntau Lau
e2c1845a24 chore(docs): fix broken angular link in README (#28039)
<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->
angular link in README is broken

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->
angular link in README is fixed now.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
2023-08-22 14:14:24 +00:00
dependabot[bot]
679821861a chore(deps-dev): Bump @playwright/test from 1.37.0 to 1.37.1 in /core (#28023)
Bumps [@playwright/test](https://github.com/Microsoft/playwright) from
1.37.0 to 1.37.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Microsoft/playwright/releases"><code>@​playwright/test</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v1.37.1</h2>
<h3>Highlights</h3>
<p><a
href="https://redirect.github.com/microsoft/playwright/issues/26496">microsoft/playwright#26496</a>
- [REGRESSION] webServer stdout is always getting printed
<a
href="https://redirect.github.com/microsoft/playwright/issues/26492">microsoft/playwright#26492</a>
- [REGRESSION] test.only with project dependency is not working</p>
<h2>Browser Versions</h2>
<ul>
<li>Chromium 116.0.5845.82</li>
<li>Mozilla Firefox 115.0</li>
<li>WebKit 17.0</li>
</ul>
<p>This version was also tested against the following stable
channels:</p>
<ul>
<li>Google Chrome 115</li>
<li>Microsoft Edge 115</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="60696ef493"><code>60696ef</code></a>
chore: mark 1.37.1 (<a
href="https://redirect.github.com/Microsoft/playwright/issues/26530">#26530</a>)</li>
<li><a
href="4f2528535f"><code>4f25285</code></a>
cherry-pick(<a
href="https://redirect.github.com/Microsoft/playwright/issues/26503">#26503</a>):
chore: fix .only in dependent tests</li>
<li><a
href="0d5e6245ba"><code>0d5e624</code></a>
cherry-pick(<a
href="https://redirect.github.com/Microsoft/playwright/issues/26475">#26475</a>):
docs: terminate img tag (<a
href="https://redirect.github.com/Microsoft/playwright/issues/26476">#26476</a>)</li>
<li><a
href="f1597577b5"><code>f159757</code></a>
cherry-pick(<a
href="https://redirect.github.com/Microsoft/playwright/issues/26473">#26473</a>):
docs(merge): add screenshot, print url, fix name (<a
href="https://redirect.github.com/Microsoft/playwright/issues/26474">#26474</a>)</li>
<li><a
href="ca975a92eb"><code>ca975a9</code></a>
cherry-pick(<a
href="https://redirect.github.com/Microsoft/playwright/issues/26465">#26465</a>):
docs(test-sharding): fix GitHub workflow snippets (<a
href="https://redirect.github.com/Microsoft/playwright/issues/26468">#26468</a>)</li>
<li><a
href="35c63d9f32"><code>35c63d9</code></a>
cherry-pick(<a
href="https://redirect.github.com/Microsoft/playwright/issues/26466">#26466</a>):
docs(release-notes): fix supported OSes table (<a
href="https://redirect.github.com/Microsoft/playwright/issues/26467">#26467</a>)</li>
<li><a
href="cdc21bbb7b"><code>cdc21bb</code></a>
cherry-pick(<a
href="https://redirect.github.com/Microsoft/playwright/issues/26439">#26439</a>):
docs: language release notes (<a
href="https://redirect.github.com/Microsoft/playwright/issues/26442">#26442</a>)</li>
<li><a
href="1112e897b0"><code>1112e89</code></a>
cherry-pick(<a
href="https://redirect.github.com/Microsoft/playwright/issues/26436">#26436</a>):
docs(release-notes): add 1.37 video (<a
href="https://redirect.github.com/Microsoft/playwright/issues/26438">#26438</a>)</li>
<li>See full diff in <a
href="https://github.com/Microsoft/playwright/compare/v1.37.0...v1.37.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=@playwright/test&package-manager=npm_and_yarn&previous-version=1.37.0&new-version=1.37.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-21 17:07:30 +00:00
Sean Perkins
77707b8c1e fix(angular): min/max validator for ion-input type number (#27993)
Issue number: Resolves #23480

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

Angular's min/max validators do not work with `ion-input[type=number]`.
Using the built-in validators with `ion-input` will not update the
control status to invalid, reflect the `ng-invalid` class or report the
correct errors.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- The `IonicModule` now includes two additional directive declarations
that extend Angular's built-in min/max validators and target the
`ion-input` component when using `type="number"`.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
2023-08-18 18:34:02 +00:00
Liam DeBeasi
444acc1f1b fix(input, textarea): clearOnEdit does not clear when pressing Tab (#28005)
Issue number: resolves #27746

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

Pressing the Tab key when focused on an input/textarea with
`clearOnEdit` clears the text field and then moves focus to the next
focusable element.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Pressing the Tab key does not clear the text field even when
clearOnEdit is enabled.
- Added test coverage
- I also noticed that input did not have an `index.html` file in the
basic directory, so I added that.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev build: `7.3.1-dev.11692202566.13cd16c4`
2023-08-18 17:06:54 +00:00
Sean Perkins
bbfb8f81a6 fix(angular): ionTabsWillChange is fired before tab activation (#27991)
Issue number: Resolves #27212

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

`ionTabsWillChange` emits _after_ the tab view is activated in the
stack.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- `ionTabsWillChange` emits _before_ the tab view is activated in the
stack.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev-build: `7.2.4-dev.11692040948.1fd0ecd2`
2023-08-18 15:07:09 +00:00
Sean Perkins
1015c06cbe chore(react): update sync script to use tgz (#28019)
Issue number: N/A

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

The sync script for the React package is out of date. It will
unintentionally delete the symlink directory of the build output from
`core/`, which breaks the build for the package during local
development.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Updates the sync script to package and install the `core` package 
- Matches the implementation pattern used for the sync script in our
other packages

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
2023-08-18 14:42:20 +00:00
Liam DeBeasi
6bcefcd9ed docs(button): add comments to renderHiddenButton (#28017)
As part of our incident learning review, the team would like to add
better documentation as to the purpose of `renderHiddenButton` for
future reference.
2023-08-17 17:04:01 +00:00
Liam DeBeasi
901679882c merge release-7.3.0
Release 7.3.0
2023-08-16 09:42:12 -05:00
ionitron
6877d659d3 chore(): update package lock files 2023-08-16 14:23:51 +00:00
66 changed files with 1520 additions and 527 deletions

View File

@@ -13,6 +13,15 @@ runs:
path: ./core
filename: CoreBuild.zip
- name: Check Diff
run: git diff --exit-code
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
}
shell: bash
working-directory: ./core

View File

@@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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)

View File

@@ -55,7 +55,7 @@
| Project | Package | Version | Downloads| Links |
| ------- | ------- | ------- | -------- |:-----:|
| **Core** | [`@ionic/core`](https://www.npmjs.com/package/@ionic/core) | [![version](https://img.shields.io/npm/v/@ionic/core/latest.svg)](https://www.npmjs.com/package/@ionic/core) | <a href="https://www.npmjs.com/package/@ionic/core" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/core.svg" alt="NPM Downloads" /></a> | [`README.md`](core/README.md)
| **Angular** | [`@ionic/angular`](https://www.npmjs.com/package/@ionic/angular) | [![version](https://img.shields.io/npm/v/@ionic/angular/latest.svg)](https://www.npmjs.com/package/@ionic/angular) | <a href="https://www.npmjs.com/package/@ionic/angular" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/angular.svg" alt="NPM Downloads" /></a> | [`README.md`](angular/README.md)
| **Angular** | [`@ionic/angular`](https://www.npmjs.com/package/@ionic/angular) | [![version](https://img.shields.io/npm/v/@ionic/angular/latest.svg)](https://www.npmjs.com/package/@ionic/angular) | <a href="https://www.npmjs.com/package/@ionic/angular" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/angular.svg" alt="NPM Downloads" /></a> | [`README.md`](packages/angular/README.md)
| **Vue** | [`@ionic/vue`](https://www.npmjs.com/package/@ionic/vue) | [![version](https://img.shields.io/npm/v/@ionic/vue/latest.svg)](https://www.npmjs.com/package/@ionic/vue) | <a href="https://www.npmjs.com/package/@ionic/vue" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/vue.svg" alt="NPM Downloads" /></a> | [`README.md`](packages/vue/README.md)
| **React** | [`@ionic/react`](https://www.npmjs.com/package/@ionic/react) | [![version](https://img.shields.io/npm/v/@ionic/react/latest.svg)](https://www.npmjs.com/package/@ionic/react) | <a href="https://www.npmjs.com/package/@ionic/react" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/react.svg" alt="NPM Downloads" /></a> |[`README.md`](packages/react/README.md)

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [7.3.1](https://github.com/ionic-team/ionic-framework/compare/v7.3.0...v7.3.1) (2023-08-23)
### Bug Fixes
* **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)
# [7.3.0](https://github.com/ionic-team/ionic-framework/compare/v7.2.4...v7.3.0) (2023-08-16)

62
core/package-lock.json generated
View File

@@ -1,28 +1,28 @@
{
"name": "@ionic/core",
"version": "7.3.0",
"version": "7.3.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "7.3.0",
"version": "7.3.1",
"license": "MIT",
"dependencies": {
"@stencil/core": "^4.0.3",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
},
"devDependencies": {
"@axe-core/playwright": "^4.7.3",
"@capacitor/core": "^5.2.3",
"@capacitor/core": "^5.3.0",
"@capacitor/haptics": "^5.0.6",
"@capacitor/keyboard": "^5.0.6",
"@capacitor/status-bar": "^5.0.6",
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@jest/core": "^27.5.1",
"@playwright/test": "^1.37.0",
"@playwright/test": "^1.37.1",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/angular-output-target": "^0.7.1",
@@ -607,9 +607,9 @@
"dev": true
},
"node_modules/@capacitor/core": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.2.3.tgz",
"integrity": "sha512-Q1zbgt3Mvldy7six2/GX54kTL0ozgnR37jeDUAXL/fOJBF4Iorr/8A0OjGEAnwEjpR1la7uFZUunESMFyMLhEQ==",
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.3.0.tgz",
"integrity": "sha512-mvhh1yJtcUTZ0hUUriBKKpxq47hn75bjxH3tYPRgAFu1z3gowCg+OtG4Rce3W5gr5fSfCjQgOSL0Vp7k9hPUWw==",
"dev": true,
"dependencies": {
"tslib": "^2.1.0"
@@ -1541,13 +1541,13 @@
}
},
"node_modules/@playwright/test": {
"version": "1.37.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.0.tgz",
"integrity": "sha512-181WBLk4SRUyH1Q96VZl7BP6HcK0b7lbdeKisn3N/vnjitk+9HbdlFz/L5fey05vxaAhldIDnzo8KUoy8S3mmQ==",
"version": "1.37.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.1.tgz",
"integrity": "sha512-bq9zTli3vWJo8S3LwB91U0qDNQDpEXnw7knhxLM0nwDvexQAwx9tO8iKDZSqqneVq+URd/WIoz+BALMqUTgdSg==",
"dev": true,
"dependencies": {
"@types/node": "*",
"playwright-core": "1.37.0"
"playwright-core": "1.37.1"
},
"bin": {
"playwright": "cli.js"
@@ -1634,9 +1634,9 @@
}
},
"node_modules/@stencil/core": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.0.3.tgz",
"integrity": "sha512-uWaH4tQ16So1feRu4tD0gupDEnzhRatS3Jp9uYkYlqjhx5BQ/ox5CTP+lxboochqkLlvkmXvhH9SsEV+UTsYTA==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg==",
"bin": {
"stencil": "bin/stencil"
},
@@ -8194,9 +8194,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.37.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.0.tgz",
"integrity": "sha512-1c46jhTH/myQw6sesrcuHVtLoSNfJv8Pfy9t3rs6subY7kARv0HRw5PpyfPYPpPtQvBOmgbE6K+qgYUpj81LAA==",
"version": "1.37.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz",
"integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==",
"dev": true,
"bin": {
"playwright-core": "cli.js"
@@ -10788,9 +10788,9 @@
"dev": true
},
"@capacitor/core": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.2.3.tgz",
"integrity": "sha512-Q1zbgt3Mvldy7six2/GX54kTL0ozgnR37jeDUAXL/fOJBF4Iorr/8A0OjGEAnwEjpR1la7uFZUunESMFyMLhEQ==",
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.3.0.tgz",
"integrity": "sha512-mvhh1yJtcUTZ0hUUriBKKpxq47hn75bjxH3tYPRgAFu1z3gowCg+OtG4Rce3W5gr5fSfCjQgOSL0Vp7k9hPUWw==",
"dev": true,
"requires": {
"tslib": "^2.1.0"
@@ -11455,14 +11455,14 @@
}
},
"@playwright/test": {
"version": "1.37.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.0.tgz",
"integrity": "sha512-181WBLk4SRUyH1Q96VZl7BP6HcK0b7lbdeKisn3N/vnjitk+9HbdlFz/L5fey05vxaAhldIDnzo8KUoy8S3mmQ==",
"version": "1.37.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.37.1.tgz",
"integrity": "sha512-bq9zTli3vWJo8S3LwB91U0qDNQDpEXnw7knhxLM0nwDvexQAwx9tO8iKDZSqqneVq+URd/WIoz+BALMqUTgdSg==",
"dev": true,
"requires": {
"@types/node": "*",
"fsevents": "2.3.2",
"playwright-core": "1.37.0"
"playwright-core": "1.37.1"
}
},
"@rollup/plugin-node-resolve": {
@@ -11524,9 +11524,9 @@
"requires": {}
},
"@stencil/core": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.0.3.tgz",
"integrity": "sha512-uWaH4tQ16So1feRu4tD0gupDEnzhRatS3Jp9uYkYlqjhx5BQ/ox5CTP+lxboochqkLlvkmXvhH9SsEV+UTsYTA=="
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg=="
},
"@stencil/react-output-target": {
"version": "0.5.3",
@@ -16332,9 +16332,9 @@
}
},
"playwright-core": {
"version": "1.37.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.0.tgz",
"integrity": "sha512-1c46jhTH/myQw6sesrcuHVtLoSNfJv8Pfy9t3rs6subY7kARv0HRw5PpyfPYPpPtQvBOmgbE6K+qgYUpj81LAA==",
"version": "1.37.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.37.1.tgz",
"integrity": "sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==",
"dev": true
},
"postcss": {

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "7.3.0",
"version": "7.3.1",
"description": "Base components for Ionic",
"keywords": [
"ionic",
@@ -31,20 +31,20 @@
"loader/"
],
"dependencies": {
"@stencil/core": "^4.0.3",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
},
"devDependencies": {
"@axe-core/playwright": "^4.7.3",
"@capacitor/core": "^5.2.3",
"@capacitor/core": "^5.3.0",
"@capacitor/haptics": "^5.0.6",
"@capacitor/keyboard": "^5.0.6",
"@capacitor/status-bar": "^5.0.6",
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@jest/core": "^27.5.1",
"@playwright/test": "^1.37.0",
"@playwright/test": "^1.37.1",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/angular-output-target": "^0.7.1",

View File

@@ -153,6 +153,15 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
*/
@Event() ionBlur!: EventEmitter<void>;
/**
* This is responsible for rendering a hidden native
* button element inside the associated form. This allows
* users to submit a form by pressing "Enter" when a text
* field inside of the form is focused. The native button
* rendered inside of `ion-button` is in the Shadow DOM
* and therefore does not participate in form submission
* which is why the following code is necessary.
*/
private renderHiddenButton() {
const formEl = (this.formEl = this.findForm());
if (formEl) {
@@ -323,6 +332,13 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
fill = this.inToolbar || this.inListHeader ? 'clear' : 'solid';
}
/**
* We call renderHiddenButton in the render function to account
* for any properties being set async. For example, changing the
* "type" prop from "button" to "submit" after the component has
* loaded would warrant the hidden button being added to the
* associated form.
*/
{
type !== 'button' && this.renderHiddenButton();
}

View File

@@ -532,7 +532,7 @@ export class Input implements ComponentInterface {
* Clear the input if the control has not been previously cleared during focus.
* Do not clear if the user hitting enter to submit a form.
*/
if (!this.didInputClearOnEdit && this.hasValue() && ev.key !== 'Enter') {
if (!this.didInputClearOnEdit && this.hasValue() && ev.key !== 'Enter' && ev.key !== 'Tab') {
this.value = '';
this.emitInputChange(ev);
}

View File

@@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Input - Basic</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
.grid {
display: grid;
grid-template-columns: repeat(5, minmax(250px, 1fr));
grid-row-gap: 20px;
grid-column-gap: 20px;
}
h2 {
font-size: 12px;
font-weight: normal;
color: #6f7378;
margin-top: 10px;
}
@media screen and (max-width: 800px) {
.grid {
grid-template-columns: 1fr;
padding: 0;
}
}
</style>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Input - Basic</ion-title>
</ion-toolbar>
</ion-header>
<ion-content id="content" class="ion-padding">
<div class="grid">
<div class="grid-item">
<h2>Default</h2>
<ion-input value="hi@ionic.io" label="Email"></ion-input>
</div>
</div>
</ion-content>
</ion-app>
</body>
</html>

View File

@@ -0,0 +1,43 @@
import { expect } from '@playwright/test';
import { test, configs } from '@utils/test/playwright';
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('input: clearOnEdit'), () => {
test('should clear when typed into', async ({ page }) => {
await page.setContent(`<ion-input value="abc" clear-on-edit="true" aria-label="input"></ion-input>`, config);
const ionInput = await page.spyOnEvent('ionInput');
const input = page.locator('ion-input');
await input.locator('input').type('h');
await ionInput.next();
await expect(input).toHaveJSProperty('value', 'h');
});
test('should not clear when enter is pressed', async ({ page }) => {
await page.setContent(`<ion-input value="abc" clear-on-edit="true" aria-label="input"></ion-input>`, config);
const input = page.locator('ion-input');
await input.locator('input').focus();
await page.keyboard.press('Enter');
await page.waitForChanges();
await expect(input).toHaveJSProperty('value', 'abc');
});
test('should not clear when tab is pressed', async ({ page }) => {
await page.setContent(`<ion-input value="abc" clear-on-edit="true" aria-label="input"></ion-input>`, config);
const input = page.locator('ion-input');
await input.locator('input').focus();
await page.keyboard.press('Tab');
await page.waitForChanges();
await expect(input).toHaveJSProperty('value', 'abc');
});
});
});

View File

@@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Menu - Async</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<script type="module">
import { menuController } from '../../../../dist/ionic/index.esm.js';
window.menuController = menuController;
</script>
</head>
<body>
<ion-app>
<div id="menu-container">
<ion-menu content-id="main">
<ion-content class="ion-padding"> Menu Content </ion-content>
</ion-menu>
</div>
<div class="ion-page" id="main">
<ion-header>
<ion-toolbar>
<ion-title>Menu - Async</ion-title>
<ion-buttons slot="start" id="buttons-container"></ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
Main Content
<button onclick="trigger()" id="trigger">Add Menu To DOM</button>
</ion-content>
</div>
</ion-app>
<script>
const buttons = document.querySelector('ion-buttons');
const menuButton = document.createElement('ion-menu-button');
const menu = document.querySelector('ion-menu');
const menuContainer = document.querySelector('#menu-container');
let firstLoad = true;
// When the menu loads, immediately remove it from the DOM
document.body.addEventListener('ionMenuChange', () => {
if (firstLoad) {
menuContainer.removeChild(menu);
buttons.appendChild(menuButton);
firstLoad = false;
}
});
const trigger = () => {
menuContainer.append(menu);
};
</script>
</body>
</html>

View File

@@ -0,0 +1,25 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';
/**
* This behavior does not vary across modes/directions
*/
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('menu button: async'), () => {
test('menu button should be visible if menu is moved', async ({ page }) => {
await page.goto(`/src/components/menu-button/test/async`, config);
const menu = page.locator('ion-menu');
const menuButton = page.locator('ion-menu-button');
const triggerButton = page.locator('#trigger');
await expect(menu).not.toBeAttached();
await expect(menuButton).toBeHidden();
await triggerButton.click();
await expect(menu).toBeAttached();
await expect(menuButton).toBeVisible();
});
});
});

View File

@@ -38,6 +38,7 @@ export class Menu implements ComponentInterface, MenuI {
private lastOnEnd = 0;
private gesture?: Gesture;
private blocker = GESTURE_CONTROLLER.createBlocker({ disableScroll: true });
private didLoad = false;
isAnimating = false;
width!: number;
@@ -216,6 +217,7 @@ export class Menu implements ComponentInterface, MenuI {
// register this menu with the app's menu controller
menuController._register(this);
this.menuChanged();
this.gesture = (await import('../../utils/gesture')).createGesture({
el: document,
@@ -237,10 +239,22 @@ export class Menu implements ComponentInterface, MenuI {
}
async componentDidLoad() {
this.ionMenuChange.emit({ disabled: this.disabled, open: this._isOpen });
this.didLoad = true;
this.menuChanged();
this.updateState();
}
private menuChanged() {
/**
* Inform dependent components such as ion-menu-button
* that the menu is ready. Note that we only want to do this
* once the menu has been rendered which is why we check for didLoad.
*/
if (this.didLoad) {
this.ionMenuChange.emit({ disabled: this.disabled, open: this._isOpen });
}
}
async disconnectedCallback() {
/**
* The menu should be closed when it is

View File

@@ -27,6 +27,9 @@ export interface PickerButton {
export interface PickerColumn {
name: string;
align?: string;
/**
* Changing this value allows the initial value of a picker column to be set.
*/
selectedIndex?: number;
prevSelected?: number;
prefix?: string;

View File

@@ -0,0 +1,37 @@
import { expect } from '@playwright/test';
import { test, configs } from '@utils/test/playwright';
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('textarea: clearOnEdit'), () => {
test('should clear when typed into', async ({ page }) => {
await page.setContent(
`<ion-textarea value="abc" clear-on-edit="true" aria-label="textarea"></ion-textarea>`,
config
);
const ionInput = await page.spyOnEvent('ionInput');
const textarea = page.locator('ion-textarea');
await textarea.locator('textarea').type('h');
await ionInput.next();
await expect(textarea).toHaveJSProperty('value', 'h');
});
test('should not clear when tab is pressed', async ({ page }) => {
await page.setContent(
`<ion-textarea value="abc" clear-on-edit="true" aria-label="textarea"></ion-textarea>`,
config
);
const textarea = page.locator('ion-textarea');
await textarea.locator('textarea').focus();
await page.keyboard.press('Tab');
await page.waitForChanges();
await expect(textarea).toHaveJSProperty('value', 'abc');
});
});
});

View File

@@ -434,7 +434,7 @@ export class Textarea implements ComponentInterface {
/**
* Check if we need to clear the text input if clearOnEdit is enabled
*/
private checkClearOnEdit(ev: Event) {
private checkClearOnEdit(ev: KeyboardEvent) {
if (!this.clearOnEdit) {
return;
}
@@ -442,7 +442,7 @@ export class Textarea implements ComponentInterface {
* Clear the textarea if the control has not been previously cleared
* during focus.
*/
if (!this.didTextareaClearOnEdit && this.hasValue()) {
if (!this.didTextareaClearOnEdit && this.hasValue() && ev.key !== 'Tab') {
this.value = '';
this.emitInputChange(ev);
}
@@ -501,7 +501,7 @@ export class Textarea implements ComponentInterface {
this.ionBlur.emit(ev);
};
private onKeyDown = (ev: Event) => {
private onKeyDown = (ev: KeyboardEvent) => {
this.checkClearOnEdit(ev);
};

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [7.3.1](https://github.com/ionic-team/ionic-framework/compare/v7.3.0...v7.3.1) (2023-08-23)
**Note:** Version bump only for package @ionic/docs
# [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/docs

View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/docs",
"version": "7.3.0",
"version": "7.3.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/docs",
"version": "7.3.0",
"version": "7.3.1",
"license": "MIT"
}
}

View File

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

View File

@@ -4,5 +4,5 @@
"docs",
"packages/*"
],
"version": "7.3.0"
"version": "7.3.1"
}

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [7.3.1](https://github.com/ionic-team/ionic-framework/compare/v7.3.0...v7.3.1) (2023-08-23)
**Note:** Version bump only for package @ionic/angular-server
# [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-server

View File

@@ -1,15 +1,15 @@
{
"name": "@ionic/angular-server",
"version": "7.3.0",
"version": "7.3.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular-server",
"version": "7.3.0",
"version": "7.3.1",
"license": "MIT",
"dependencies": {
"@ionic/core": "^7.3.0"
"@ionic/core": "^7.3.1"
},
"devDependencies": {
"@angular-eslint/eslint-plugin": "^14.0.0",
@@ -1060,25 +1060,25 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.4.tgz",
"integrity": "sha512-FM7r1+drjC6Kf4RVkUjwA7mn8wM4QF1W+YjzjYExUNd3O5XGBYxW7OrNXGX3asnhHVDN9QRs6QLKa59SjMQHPw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.3.1.tgz",
"integrity": "sha512-RdSJsFYx2oJ08duw+DMWVAfzjxlnA+o1saX5gOLSjP5SpwP7FqURFfXrFHIFN2vIKbeqUZTZjQwPHnrFsX04dQ==",
"dependencies": {
"@stencil/core": "^3.4.0",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
}
},
"node_modules/@ionic/core/node_modules/@stencil/core": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.1.tgz",
"integrity": "sha512-7rjOmM0W9K5op2gtOQRLERGH1155rv2fm6ppxOzYqqG8ISct4m9skp5XgUBYPu+GSPsJFdRuCIQs0IuVsG/7+g==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg==",
"bin": {
"stencil": "bin/stencil"
},
"engines": {
"node": ">=14.10.0",
"npm": ">=6.0.0"
"node": ">=16.0.0",
"npm": ">=7.10.0"
}
},
"node_modules/@ionic/eslint-config": {
@@ -7342,19 +7342,19 @@
"dev": true
},
"@ionic/core": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.4.tgz",
"integrity": "sha512-FM7r1+drjC6Kf4RVkUjwA7mn8wM4QF1W+YjzjYExUNd3O5XGBYxW7OrNXGX3asnhHVDN9QRs6QLKa59SjMQHPw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.3.1.tgz",
"integrity": "sha512-RdSJsFYx2oJ08duw+DMWVAfzjxlnA+o1saX5gOLSjP5SpwP7FqURFfXrFHIFN2vIKbeqUZTZjQwPHnrFsX04dQ==",
"requires": {
"@stencil/core": "^3.4.0",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
},
"dependencies": {
"@stencil/core": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.1.tgz",
"integrity": "sha512-7rjOmM0W9K5op2gtOQRLERGH1155rv2fm6ppxOzYqqG8ISct4m9skp5XgUBYPu+GSPsJFdRuCIQs0IuVsG/7+g=="
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg=="
}
}
},

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular-server",
"version": "7.3.0",
"version": "7.3.1",
"description": "Angular SSR Module for Ionic",
"keywords": [
"ionic",
@@ -61,6 +61,6 @@
},
"prettier": "@ionic/prettier-config",
"dependencies": {
"@ionic/core": "^7.3.0"
"@ionic/core": "^7.3.1"
}
}

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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

View File

@@ -1,15 +1,15 @@
{
"name": "@ionic/angular",
"version": "7.3.0",
"version": "7.3.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular",
"version": "7.3.0",
"version": "7.3.1",
"license": "MIT",
"dependencies": {
"@ionic/core": "^7.3.0",
"@ionic/core": "^7.3.1",
"ionicons": "^7.0.0",
"jsonc-parser": "^3.0.0",
"tslib": "^2.3.0"
@@ -1227,25 +1227,25 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.4.tgz",
"integrity": "sha512-FM7r1+drjC6Kf4RVkUjwA7mn8wM4QF1W+YjzjYExUNd3O5XGBYxW7OrNXGX3asnhHVDN9QRs6QLKa59SjMQHPw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.3.1.tgz",
"integrity": "sha512-RdSJsFYx2oJ08duw+DMWVAfzjxlnA+o1saX5gOLSjP5SpwP7FqURFfXrFHIFN2vIKbeqUZTZjQwPHnrFsX04dQ==",
"dependencies": {
"@stencil/core": "^3.4.0",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
}
},
"node_modules/@ionic/core/node_modules/@stencil/core": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.1.tgz",
"integrity": "sha512-7rjOmM0W9K5op2gtOQRLERGH1155rv2fm6ppxOzYqqG8ISct4m9skp5XgUBYPu+GSPsJFdRuCIQs0IuVsG/7+g==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg==",
"bin": {
"stencil": "bin/stencil"
},
"engines": {
"node": ">=14.10.0",
"npm": ">=6.0.0"
"node": ">=16.0.0",
"npm": ">=7.10.0"
}
},
"node_modules/@ionic/eslint-config": {
@@ -8104,19 +8104,19 @@
"dev": true
},
"@ionic/core": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.4.tgz",
"integrity": "sha512-FM7r1+drjC6Kf4RVkUjwA7mn8wM4QF1W+YjzjYExUNd3O5XGBYxW7OrNXGX3asnhHVDN9QRs6QLKa59SjMQHPw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.3.1.tgz",
"integrity": "sha512-RdSJsFYx2oJ08duw+DMWVAfzjxlnA+o1saX5gOLSjP5SpwP7FqURFfXrFHIFN2vIKbeqUZTZjQwPHnrFsX04dQ==",
"requires": {
"@stencil/core": "^3.4.0",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
},
"dependencies": {
"@stencil/core": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.1.tgz",
"integrity": "sha512-7rjOmM0W9K5op2gtOQRLERGH1155rv2fm6ppxOzYqqG8ISct4m9skp5XgUBYPu+GSPsJFdRuCIQs0IuVsG/7+g=="
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg=="
}
}
},

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "7.3.0",
"version": "7.3.1",
"description": "Angular specific wrappers for @ionic/core",
"keywords": [
"ionic",
@@ -47,7 +47,7 @@
}
},
"dependencies": {
"@ionic/core": "^7.3.0",
"@ionic/core": "^7.3.1",
"ionicons": "^7.0.0",
"jsonc-parser": "^3.0.0",
"tslib": "^2.3.0"

View File

@@ -30,7 +30,7 @@ import { Config } from '../../providers/config';
import { NavController } from '../../providers/nav-controller';
import { StackController } from './stack-controller';
import { RouteView, getUrl } from './stack-utils';
import { RouteView, StackDidChangeEvent, StackWillChangeEvent, getUrl, isTabSwitch } from './stack-utils';
// TODO(FW-2827): types
@@ -66,7 +66,11 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
*/
@Input() name = PRIMARY_OUTLET;
@Output() stackEvents = new EventEmitter<any>();
/** @internal */
@Output() stackWillChange = new EventEmitter<StackWillChangeEvent>();
/** @internal */
@Output() stackDidChange = new EventEmitter<StackDidChangeEvent>();
// eslint-disable-next-line @angular-eslint/no-output-rename
@Output('activate') activateEvents = new EventEmitter<any>();
// eslint-disable-next-line @angular-eslint/no-output-rename
@@ -304,9 +308,16 @@ 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.stackEvents.emit(data);
this.stackDidChange.emit(data);
});
}

View File

@@ -16,14 +16,19 @@ import { NavController } from '../../providers/nav-controller';
import { IonTabBar } from '../proxies';
import { IonRouterOutlet } from './ion-router-outlet';
import { StackEvent } from './stack-utils';
import { StackDidChangeEvent, StackWillChangeEvent } from './stack-utils';
@Component({
selector: 'ion-tabs',
template: `
<ng-content select="[slot=top]"></ng-content>
<div class="tabs-inner" #tabsInner>
<ion-router-outlet #outlet tabs="true" (stackEvents)="onPageSelected($event)"></ion-router-outlet>
<ion-router-outlet
#outlet
tabs="true"
(stackWillChange)="onStackWillChange($event)"
(stackDidChange)="onStackDidChange($event)"
></ion-router-outlet>
</div>
<ng-content></ng-content>
`,
@@ -62,7 +67,13 @@ export class IonTabs implements AfterContentInit, AfterContentChecked {
@ContentChild(IonTabBar, { static: false }) tabBar: IonTabBar | undefined;
@ContentChildren(IonTabBar) tabBars: QueryList<IonTabBar>;
/**
* 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';
@@ -80,10 +91,19 @@ export class IonTabs implements AfterContentInit, AfterContentChecked {
/**
* @internal
*/
onPageSelected(detail: StackEvent): void {
const stackId = detail.enteringView.stackId;
if (detail.tabSwitch && stackId !== undefined) {
onStackWillChange({ enteringView, tabSwitch }: StackWillChangeEvent): void {
const stackId = enteringView.stackId;
if (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;
}

View File

@@ -8,7 +8,7 @@ import { NavController } from '../../providers/nav-controller';
import {
RouteView,
StackEvent,
StackDidChangeEvent,
computeStackId,
destroyView,
getUrl,
@@ -61,7 +61,7 @@ export class StackController {
return view;
}
setActive(enteringView: RouteView): Promise<StackEvent> {
setActive(enteringView: RouteView): Promise<StackDidChangeEvent> {
const consumeResult = this.navCtrl.consumeTransition();
let { direction, animation, animationBuilder } = consumeResult;
const leavingView = this.activeView;
@@ -224,6 +224,13 @@ export class StackController {
return this.activeView ? this.activeView.stackId : undefined;
}
/**
* @internal
*/
getActiveView(): RouteView | undefined {
return this.activeView;
}
hasRunningTask(): boolean {
return this.runningTask !== undefined;
}

View File

@@ -79,10 +79,23 @@ export const destroyView = (view: RouteView | undefined): void => {
}
};
export interface StackEvent {
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 {
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;
}

View File

@@ -0,0 +1,2 @@
export * from './max-validator';
export * from './min-validator';

View File

@@ -0,0 +1,22 @@
import { Directive, forwardRef, Provider } from '@angular/core';
import { MaxValidator, NG_VALIDATORS } from '@angular/forms';
/**
* @description
* Provider which adds `MaxValidator` to the `NG_VALIDATORS` multi-provider list.
*/
export const ION_MAX_VALIDATOR: Provider = {
provide: NG_VALIDATORS,
useExisting: forwardRef(() => IonMaxValidator),
multi: true,
};
@Directive({
selector:
'ion-input[type=number][max][formControlName],ion-input[type=number][max][formControl],ion-input[type=number][max][ngModel]',
providers: [ION_MAX_VALIDATOR],
// eslint-disable-next-line @angular-eslint/no-host-metadata-property
host: { '[attr.max]': '_enabled ? max : null' },
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class IonMaxValidator extends MaxValidator {}

View File

@@ -0,0 +1,22 @@
import { Directive, forwardRef, Provider } from '@angular/core';
import { MinValidator, NG_VALIDATORS } from '@angular/forms';
/**
* @description
* Provider which adds `MinValidator` to the `NG_VALIDATORS` multi-provider list.
*/
export const ION_MIN_VALIDATOR: Provider = {
provide: NG_VALIDATORS,
useExisting: forwardRef(() => IonMinValidator),
multi: true,
};
@Directive({
selector:
'ion-input[type=number][min][formControlName],ion-input[type=number][min][formControl],ion-input[type=number][min][ngModel]',
providers: [ION_MIN_VALIDATOR],
// eslint-disable-next-line @angular-eslint/no-host-metadata-property
host: { '[attr.min]': '_enabled ? min : null' },
})
// eslint-disable-next-line @angular-eslint/directive-class-suffix
export class IonMinValidator extends MinValidator {}

View File

@@ -17,6 +17,7 @@ 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 { AngularDelegate } from './providers/angular-delegate';

View File

@@ -22,6 +22,7 @@ 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';
@@ -49,6 +50,10 @@ const DECLARATIONS = [
NavDelegate,
RouterLinkDelegateDirective,
RouterLinkWithHrefDelegateDirective,
// validators
IonMinValidator,
IonMaxValidator,
];
@NgModule({

View File

@@ -30,6 +30,8 @@ describe('Form', () => {
toggle: false,
input: '',
input2: 'Default Value',
inputMin: 1,
inputMax: 1,
checkbox: false
});
});
@@ -55,6 +57,8 @@ describe('Form', () => {
toggle: false,
input: 'Some value',
input2: 'Default Value',
inputMin: 1,
inputMax: 1,
checkbox: false
});
});
@@ -67,6 +71,8 @@ describe('Form', () => {
toggle: true,
input: '',
input2: 'Default Value',
inputMin: 1,
inputMax: 1,
checkbox: false
});
});
@@ -79,6 +85,8 @@ describe('Form', () => {
toggle: false,
input: '',
input2: 'Default Value',
inputMin: 1,
inputMax: 1,
checkbox: true
});
});
@@ -99,6 +107,8 @@ describe('Form', () => {
toggle: true,
input: '',
input2: 'Default Value',
inputMin: 1,
inputMax: 1,
checkbox: false
});
cy.get('ion-checkbox').click();
@@ -108,10 +118,39 @@ describe('Form', () => {
toggle: true,
input: '',
input2: 'Default Value',
inputMin: 1,
inputMax: 1,
checkbox: true
});
});
});
describe('validators', () => {
it('ion-input should error with min set', () => {
const control = cy.get('form ion-input[formControlName="inputMin"]');
control.should('have.class', 'ng-valid');
control.type('{backspace}0');
control.within(() => cy.get('input').blur());
control.should('have.class', 'ng-invalid');
});
it('ion-input should error with max set', () => {
const control = cy.get('form ion-input[formControlName="inputMax"]');
control.should('have.class', 'ng-valid');
control.type('2');
control.within(() => cy.get('input').blur());
control.should('have.class', 'ng-invalid');
});
});
});
function testStatus(status) {

View File

@@ -1,15 +1,12 @@
<ion-header>
<ion-toolbar>
<ion-title>
Forms test
</ion-title>
<ion-title> Forms test </ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
<ion-list>
<ion-item>
<ion-label>DateTime</ion-label>
<ion-datetime formControlName="datetime" min="1994-03-14" max="2017-12-09" display-format="MM/DD/YYYY">
@@ -29,13 +26,16 @@
</ion-item>
<ion-item>
<ion-toggle formControlName="toggle">
Toggle
</ion-toggle>
<ion-toggle formControlName="toggle"> Toggle </ion-toggle>
</ion-item>
<ion-item>
<ion-input label="Input (required)" formControlName="input" class="required" id="touched-input-test"></ion-input>
<ion-input
label="Input (required)"
formControlName="input"
class="required"
id="touched-input-test"
></ion-input>
</ion-item>
<ion-button id="input-touched" (click)="setTouched()">Set Input Touched</ion-button>
@@ -45,11 +45,20 @@
</ion-item>
<ion-item>
<ion-checkbox formControlName="checkbox">
Checkbox
</ion-checkbox>
<ion-checkbox formControlName="checkbox"> Checkbox </ion-checkbox>
</ion-item>
<ion-item>
<ion-label>Min</ion-label>
<ion-input formControlName="inputMin" type="number"></ion-input>
<pre>errors: {{ profileForm.controls['inputMin'].errors | json }}</pre>
</ion-item>
<ion-item>
<ion-label>Max</ion-label>
<ion-input formControlName="inputMax" type="number"></ion-input>
<pre>errors: {{ profileForm.controls['inputMax'].errors | json }}</pre>
</ion-item>
</ion-list>
<p>
Form Status: <span id="status">{{ profileForm.status }}</span>
@@ -58,18 +67,15 @@
Form value: <span id="data">{{ profileForm.value | json }}</span>
</p>
<p>
Form Submit: <span id="submit">{{submitted}}</span>
Form Submit: <span id="submit">{{ submitted }}</span>
</p>
<ion-button id="mark-all-touched-button" (click)="markAllAsTouched()">Mark all as touched</ion-button>
<ion-button id="submit-button" type="submit" [disabled]="!profileForm.valid">Submit</ion-button>
</form>
<ion-list>
<ion-item>
<ion-toggle [formControl]="outsideToggle">
Outside form
</ion-toggle>
<ion-note slot="end">{{outsideToggle.value}}</ion-note>
<ion-toggle [formControl]="outsideToggle"> Outside form </ion-toggle>
<ion-note slot="end">{{ outsideToggle.value }}</ion-note>
</ion-item>
</ion-list>
<p>

View File

@@ -18,6 +18,8 @@ export class FormComponent {
toggle: [false],
input: ['', Validators.required],
input2: ['Default Value'],
inputMin: [1, Validators.min(1)],
inputMax: [1, Validators.max(1)],
checkbox: [false]
}, {
updateOn: typeof (window as any) !== 'undefined' && window.location.hash === '#blur' ? 'blur' : 'change'

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [7.3.1](https://github.com/ionic-team/ionic-framework/compare/v7.3.0...v7.3.1) (2023-08-23)
**Note:** Version bump only for package @ionic/react-router
# [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/react-router

View File

@@ -1,15 +1,15 @@
{
"name": "@ionic/react-router",
"version": "7.3.0",
"version": "7.3.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/react-router",
"version": "7.3.0",
"version": "7.3.1",
"license": "MIT",
"dependencies": {
"@ionic/react": "^7.3.0",
"@ionic/react": "^7.3.1",
"tslib": "*"
},
"devDependencies": {
@@ -205,11 +205,11 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.4.tgz",
"integrity": "sha512-FM7r1+drjC6Kf4RVkUjwA7mn8wM4QF1W+YjzjYExUNd3O5XGBYxW7OrNXGX3asnhHVDN9QRs6QLKa59SjMQHPw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.3.1.tgz",
"integrity": "sha512-RdSJsFYx2oJ08duw+DMWVAfzjxlnA+o1saX5gOLSjP5SpwP7FqURFfXrFHIFN2vIKbeqUZTZjQwPHnrFsX04dQ==",
"dependencies": {
"@stencil/core": "^3.4.0",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
}
@@ -401,11 +401,11 @@
}
},
"node_modules/@ionic/react": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.2.4.tgz",
"integrity": "sha512-+DoeTu7FgQYBF7Eco6xbvJ44pGeQBkaEROr0Nim83pLFN9Myff1uaqqv+E+8bmEJTE5qlthy0IE6V3e1OY3UXg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.3.1.tgz",
"integrity": "sha512-X1n81R85mprFqwqNiISiqrzYWbMJm0chPxJZkVOzFwuWG7fKDKY1vigNvm5eI/lOTbfC2xjJZOYwuHAxHi3BoQ==",
"dependencies": {
"@ionic/core": "7.2.4",
"@ionic/core": "7.3.1",
"ionicons": "^7.0.0",
"tslib": "*"
},
@@ -486,15 +486,15 @@
}
},
"node_modules/@stencil/core": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.2.tgz",
"integrity": "sha512-FAUhUVaakCy29nU2GwO/HQBRV1ihPRvncz3PUc8oR+UJLAxGabTmP8PLY7wvHfbw+Cvi4VXfJFTBvdfDu6iKPQ==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg==",
"bin": {
"stencil": "bin/stencil"
},
"engines": {
"node": ">=14.10.0",
"npm": ">=6.0.0"
"node": ">=16.0.0",
"npm": ">=7.10.0"
}
},
"node_modules/@types/estree": {
@@ -3663,11 +3663,11 @@
"dev": true
},
"@ionic/core": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.4.tgz",
"integrity": "sha512-FM7r1+drjC6Kf4RVkUjwA7mn8wM4QF1W+YjzjYExUNd3O5XGBYxW7OrNXGX3asnhHVDN9QRs6QLKa59SjMQHPw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.3.1.tgz",
"integrity": "sha512-RdSJsFYx2oJ08duw+DMWVAfzjxlnA+o1saX5gOLSjP5SpwP7FqURFfXrFHIFN2vIKbeqUZTZjQwPHnrFsX04dQ==",
"requires": {
"@stencil/core": "^3.4.0",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
},
@@ -3786,11 +3786,11 @@
"requires": {}
},
"@ionic/react": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.2.4.tgz",
"integrity": "sha512-+DoeTu7FgQYBF7Eco6xbvJ44pGeQBkaEROr0Nim83pLFN9Myff1uaqqv+E+8bmEJTE5qlthy0IE6V3e1OY3UXg==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/react/-/react-7.3.1.tgz",
"integrity": "sha512-X1n81R85mprFqwqNiISiqrzYWbMJm0chPxJZkVOzFwuWG7fKDKY1vigNvm5eI/lOTbfC2xjJZOYwuHAxHi3BoQ==",
"requires": {
"@ionic/core": "7.2.4",
"@ionic/core": "7.3.1",
"ionicons": "^7.0.0",
"tslib": "*"
}
@@ -3844,9 +3844,9 @@
}
},
"@stencil/core": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.2.tgz",
"integrity": "sha512-FAUhUVaakCy29nU2GwO/HQBRV1ihPRvncz3PUc8oR+UJLAxGabTmP8PLY7wvHfbw+Cvi4VXfJFTBvdfDu6iKPQ=="
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg=="
},
"@types/estree": {
"version": "0.0.39",

View File

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

View File

@@ -46,6 +46,10 @@ class IonRouterInner extends React.PureComponent<IonRouteProps, IonRouteState> {
findLeavingViewItemByRouteInfo: this.viewStack.findLeavingViewItemByRouteInfo,
addViewItem: this.viewStack.add,
unMountViewItem: this.viewStack.remove,
registerIonPage: this.viewStack.registerIonPage,
unmountLeavingViews: this.viewStack.unmountLeavingViews,
mountIntermediaryViews: this.viewStack.mountIntermediaryViews,
size: this.viewStack.size,
};
constructor(props: IonRouteProps) {

View File

@@ -1,7 +1,9 @@
import type { RouteInfo, ViewItem } from '@ionic/react';
import { IonRoute, ViewLifeCycleManager, ViewStacks, generateId } from '@ionic/react';
import React from 'react';
import { matchPath } from 'react-router';
// import { matchPath } from 'react-router';
import { matchPath } from './utils/matchPath';
export class ReactRouterViewStack extends ViewStacks {
constructor() {
@@ -13,35 +15,30 @@ export class ReactRouterViewStack extends ViewStacks {
this.findViewItemByPathname = this.findViewItemByPathname.bind(this);
}
createViewItem(outletId: string, reactElement: React.ReactElement, routeInfo: RouteInfo, page?: HTMLElement) {
const viewItem: ViewItem = {
createViewItem(
outletId: string,
reactElement: React.ReactElement,
routeInfo: RouteInfo,
ionPage?: HTMLElement
): ViewItem {
const ionRoute = reactElement.type === IonRoute;
return {
id: generateId('viewItem'),
outletId,
ionPageElement: page,
ionPageElement: ionPage,
reactElement,
mount: true,
ionRoute: false,
ionRoute,
disableIonPageManagement: ionRoute && reactElement.props.disableIonPageManagement,
mount: false,
routeData: {
match: matchPath({
pathname: routeInfo.pathname,
componentProps: reactElement.props,
}),
childProps: reactElement.props,
},
};
const matchProps = {
exact: reactElement.props.exact,
path: reactElement.props.path || reactElement.props.from,
component: reactElement.props.component,
};
const match = matchPath(routeInfo.pathname, matchProps);
if (reactElement.type === IonRoute) {
viewItem.ionRoute = true;
viewItem.disableIonPageManagement = reactElement.props.disableIonPageManagement;
}
viewItem.routeData = {
match,
childProps: reactElement.props,
};
return viewItem;
}
getChildrenToRender(outletId: string, ionRouterOutlet: React.ReactElement, routeInfo: RouteInfo) {
@@ -58,7 +55,7 @@ export class ReactRouterViewStack extends ViewStacks {
});
const children = viewItems.map((viewItem) => {
let clonedChild;
let clonedChild: React.ReactNode;
if (viewItem.ionRoute && !viewItem.disableIonPageManagement) {
clonedChild = (
<ViewLifeCycleManager
@@ -96,8 +93,20 @@ export class ReactRouterViewStack extends ViewStacks {
return children;
}
/**
* Registers the `<IonPage>` element reference to
* the view item with the matching route info.
*/
registerIonPage(viewItem: ViewItem, ionPage: HTMLElement) {
if (viewItem) {
// TODO view doesn't check if it exists
viewItem.ionPageElement = ionPage;
viewItem.ionRoute = true;
}
}
findViewItemByRouteInfo(routeInfo: RouteInfo, outletId?: string, updateMatch?: boolean) {
const { viewItem, match } = this.findViewItemByPath(routeInfo.pathname, outletId);
const { viewItem, match } = this.findViewItemByPath(routeInfo.pathname, outletId, false);
const shouldUpdateMatch = updateMatch === undefined || updateMatch === true;
if (shouldUpdateMatch && viewItem && match) {
viewItem.routeData.match = match;
@@ -106,31 +115,30 @@ export class ReactRouterViewStack extends ViewStacks {
}
findLeavingViewItemByRouteInfo(routeInfo: RouteInfo, outletId?: string, mustBeIonRoute = true) {
const { viewItem } = this.findViewItemByPath(routeInfo.lastPathname!, outletId, false, mustBeIonRoute);
const { viewItem } = this.findViewItemByPath(routeInfo.lastPathname!, outletId, mustBeIonRoute);
return viewItem;
}
findViewItemByPathname(pathname: string, outletId?: string) {
const { viewItem } = this.findViewItemByPath(pathname, outletId);
const { viewItem } = this.findViewItemByPath(pathname, outletId, false);
return viewItem;
}
private findViewItemByPath(pathname: string, outletId?: string, forceExact?: boolean, mustBeIonRoute?: boolean) {
private findViewItemByPath(pathname: string, outletId?: string, mustBeIonRoute = false) {
let viewItem: ViewItem | undefined;
let match: ReturnType<typeof matchPath> | undefined;
let viewStack: ViewItem[];
if (outletId) {
viewStack = this.getViewItemsForOutlet(outletId);
const viewStack = this.getViewItemsForOutlet(outletId);
viewStack.some(matchView);
if (!viewItem) {
viewStack.some(matchDefaultRoute);
}
} else {
const viewItems = this.getAllViewItems();
viewItems.some(matchView);
const viewStack = this.getAllViewItems();
viewStack.some(matchView);
if (!viewItem) {
viewItems.some(matchDefaultRoute);
viewStack.some(matchDefaultRoute);
}
}
@@ -140,15 +148,14 @@ export class ReactRouterViewStack extends ViewStacks {
if (mustBeIonRoute && !v.ionRoute) {
return false;
}
const matchProps = {
exact: forceExact ? true : v.routeData.childProps.exact,
path: v.routeData.childProps.path || v.routeData.childProps.from,
component: v.routeData.childProps.component,
};
const myMatch = matchPath(pathname, matchProps);
if (myMatch) {
match = matchPath({
pathname,
componentProps: v.routeData.childProps,
});
if (match) {
viewItem = v;
match = myMatch;
return true;
}
return false;
@@ -171,13 +178,16 @@ export class ReactRouterViewStack extends ViewStacks {
}
}
function matchComponent(node: React.ReactElement, pathname: string, forceExact?: boolean) {
const matchProps = {
exact: forceExact ? true : node.props.exact,
path: node.props.path || node.props.from,
component: node.props.component,
};
const match = matchPath(pathname, matchProps);
function matchComponent(node: React.ReactElement, pathname: string) {
// const matchProps = {
// exact: forceExact ? true : node.props.exact,
// path: node.props.path || node.props.from,
// component: node.props.component,
// };
// const match = matchPath(pathname, matchProps);
return match;
return matchPath({
pathname,
componentProps: node.props,
});
}

View File

@@ -1,21 +1,20 @@
import type { RouteInfo, StackContextState, ViewItem } from '@ionic/react';
import type { AnimationBuilder, RouteInfo, RouterDirection, StackContextState } from '@ionic/react';
import { RouteManagerContext, StackContext, generateId, getConfig } from '@ionic/react';
import type { ReactNode } from 'react';
import React from 'react';
import { matchPath } from 'react-router-dom';
import { clonePageElement } from './clonePageElement';
// TODO(FW-2959): types
interface StackManagerProps {
routeInfo: RouteInfo;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface StackManagerState {}
interface StackManagerState {
components: ReactNode[];
}
const isViewVisible = (el: HTMLElement) =>
!el.classList.contains('ion-page-invisible') && !el.classList.contains('ion-page-hidden');
const isViewVisible = (enteringEl: HTMLElement) => {
return !enteringEl.classList.contains('ion-page-invisible') && !enteringEl.classList.contains('ion-page-hidden');
};
export class StackManager extends React.PureComponent<StackManagerProps, StackManagerState> {
id: string;
@@ -31,16 +30,18 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
};
private clearOutletTimeout: any;
private pendingPageTransition = false;
constructor(props: StackManagerProps) {
super(props);
this.registerIonPage = this.registerIonPage.bind(this);
this.transitionPage = this.transitionPage.bind(this);
this.transition = this.transition.bind(this);
this.handlePageTransition = this.handlePageTransition.bind(this);
this.id = generateId('routerOutlet');
this.id = generateId('ion-router-outlet');
this.prevProps = undefined;
this.skipTransition = false;
this.state = {
components: [],
};
}
componentDidMount() {
@@ -58,7 +59,7 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
}
if (this.routerOutletElement) {
this.setupRouterOutlet(this.routerOutletElement);
this.handlePageTransition(this.props.routeInfo);
this.setupViewItem();
}
}
@@ -68,145 +69,362 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
if (pathname !== prevPathname) {
this.prevProps = prevProps;
this.handlePageTransition(this.props.routeInfo);
} else if (this.pendingPageTransition) {
this.handlePageTransition(this.props.routeInfo);
this.pendingPageTransition = false;
console.log('pathname changed... setup view item?!');
this.setupViewItem();
}
// if (pathname !== prevPathname) {
// this.prevProps = prevProps;
// console.log('calling page transition because path is different', {
// pathname,
// prevPathname,
// });
// this.handlePageTransition(this.props.routeInfo);
// } else if (this.pendingPageTransition) {
// console.log('calling page transition because pending transition');
// this.handlePageTransition(this.props.routeInfo);
// this.pendingPageTransition = false;
// }
}
componentWillUnmount() {
/**
* Remove stack data for this outlet
* when outlet is destroyed otherwise
* we will see cached view data.
*/
this.clearOutletTimeout = this.context.clearOutlet(this.id);
}
async handlePageTransition(routeInfo: RouteInfo) {
if (!this.routerOutletElement || !this.routerOutletElement.commit) {
setupViewItem() {
// TODO additional checks
const { id, props } = this;
const { routeInfo } = props;
/**
* This function is responsible for creating the entering view item
* and adding it to the view stack.
*/
const currentRoute = matchRoute(this.ionRouterOutlet?.props.children, routeInfo) as React.ReactElement;
let enteringViewItem = this.context.findViewItemByRouteInfo(routeInfo, id);
if (!enteringViewItem) {
/**
* The route outlet has not mounted yet. We need to wait for it to render
* before we can transition the page.
* If the entering view item does not exist, this is
* the first time we are rendering this route.
*
* Set a flag to indicate that we should transition the page after
* the component has updated.
* We need to create a view item instance and add it to the view stack.
* Later, we will mount the view item and transition the page.
*/
this.pendingPageTransition = true;
} else {
let enteringViewItem = this.context.findViewItemByRouteInfo(routeInfo, this.id);
let leavingViewItem = this.context.findLeavingViewItemByRouteInfo(routeInfo, this.id);
enteringViewItem = this.context.createViewItem(id, currentRoute, routeInfo);
if (!leavingViewItem && routeInfo.prevRouteLastPathname) {
leavingViewItem = this.context.findViewItemByPathname(routeInfo.prevRouteLastPathname, this.id);
}
// Check if leavingViewItem should be unmounted
if (leavingViewItem) {
if (routeInfo.routeAction === 'replace') {
leavingViewItem.mount = false;
} else if (!(routeInfo.routeAction === 'push' && routeInfo.routeDirection === 'forward')) {
if (routeInfo.routeDirection !== 'none' && enteringViewItem !== leavingViewItem) {
leavingViewItem.mount = false;
}
} else if (routeInfo.routeOptions?.unmount) {
leavingViewItem.mount = false;
}
}
const enteringRoute = matchRoute(this.ionRouterOutlet?.props.children, routeInfo) as React.ReactElement;
if (enteringViewItem) {
enteringViewItem.reactElement = enteringRoute;
} else if (enteringRoute) {
enteringViewItem = this.context.createViewItem(this.id, enteringRoute, routeInfo);
this.context.addViewItem(enteringViewItem);
}
if (enteringViewItem && enteringViewItem.ionPageElement) {
/**
* If the entering view item is the same as the leaving view item,
* then we don't need to transition.
*/
if (enteringViewItem === leavingViewItem) {
/**
* If the entering view item is the same as the leaving view item,
* we are either transitioning using parameterized routes to the same view
* or a parent router outlet is re-rendering as a result of React props changing.
*
* If the route data does not match the current path, the parent router outlet
* is attempting to transition and we cancel the operation.
*/
if (enteringViewItem.routeData.match.url !== routeInfo.pathname) {
return;
}
}
/**
* If there isn't a leaving view item, but the route info indicates
* that the user has routed from a previous path, then we need
* to find the leaving view item to transition between.
*/
if (!leavingViewItem && this.props.routeInfo.prevRouteLastPathname) {
leavingViewItem = this.context.findViewItemByPathname(this.props.routeInfo.prevRouteLastPathname, this.id);
}
/**
* If the entering view is already visible and the leaving view is not, the transition does not need to occur.
*/
if (
isViewVisible(enteringViewItem.ionPageElement) &&
leavingViewItem !== undefined &&
!isViewVisible(leavingViewItem.ionPageElement!)
) {
return;
}
/**
* The view should only be transitioned in the following cases:
* 1. Performing a replace or pop action, such as a swipe to go back gesture
* to animation the leaving view off the screen.
*
* 2. Navigating between top-level router outlets, such as /page-1 to /page-2;
* or navigating within a nested outlet, such as /tabs/tab-1 to /tabs/tab-2.
*
* 3. The entering view is an ion-router-outlet containing a page
* matching the current route and that hasn't already transitioned in.
*
* This should only happen when navigating directly to a nested router outlet
* route or on an initial page load (i.e. refreshing). In cases when loading
* /tabs/tab-1, we need to transition the /tabs page element into the view.
*/
this.transitionPage(routeInfo, enteringViewItem, leavingViewItem);
} else if (leavingViewItem && !enteringRoute && !enteringViewItem) {
// If we have a leavingView but no entering view/route, we are probably leaving to
// another outlet, so hide this leavingView. We do it in a timeout to give time for a
// transition to finish.
// setTimeout(() => {
if (leavingViewItem.ionPageElement) {
leavingViewItem.ionPageElement.classList.add('ion-page-hidden');
leavingViewItem.ionPageElement.setAttribute('aria-hidden', 'true');
}
// }, 250);
}
this.forceUpdate();
this.context.addViewItem(enteringViewItem);
}
if (!enteringViewItem.mount) {
/**
* If the entering view item is not mounted,
* that means it has not been rendered yet.
* We need to mount it and then finish the transition
* after the React component has mounted.
*/
enteringViewItem.mount = true;
enteringViewItem.registerCallback = () => {
this.handlePageTransition();
if (enteringViewItem) {
enteringViewItem.registerCallback = undefined;
}
};
} else {
/**
* If the entering view item has already mounted,
* that means the page reference should already
* exist and we can transition immediately.
*/
this.handlePageTransition();
}
this.setState({
components: this.context.getChildrenToRender(id, this.ionRouterOutlet!, routeInfo),
});
}
registerIonPage(page: HTMLElement, routeInfo: RouteInfo) {
const foundView = this.context.findViewItemByRouteInfo(routeInfo, this.id);
if (foundView) {
const oldPageElement = foundView.ionPageElement;
foundView.ionPageElement = page;
foundView.ionRoute = true;
async handlePageTransition() {
const { id, props } = this;
const { routeInfo } = props;
const { prevRouteLastPathname, routeDirection, pushedByRoute, routeAnimation, routeAction, delta } = routeInfo;
const enteringViewItem = this.context.findViewItemByRouteInfo(routeInfo, id)!;
let leavingViewItem = this.context.findLeavingViewItemByRouteInfo(routeInfo, id);
const enteringEl = enteringViewItem?.ionPageElement;
/**
* All views that can be transitioned to must have
* an `<ion-page>` element for transitions and lifecycle
* methods to work properly.
*/
if (enteringEl === undefined) {
console.warn(`[@ionic/react Warning]: The view you are trying to render for path ${routeInfo.pathname} does not have the required <IonPage> component. Transitions and lifecycle methods may not work as expected.
See https://ionicframework.com/docs/react/navigation#ionpage for more information.`);
}
if (enteringViewItem === leavingViewItem) return;
if (!leavingViewItem && prevRouteLastPathname) {
leavingViewItem = this.context.findViewItemByPathname(prevRouteLastPathname, id);
}
/**
* If the entering view is already
* visible, then no transition is needed.
* This is most common when navigating
* from a tabs page to a non-tabs page
* and then back to the tabs page.
* Even when the tabs context navigated away,
* the inner tabs page was still active.
* This also avoids an issue where
* the previous tabs page is incorrectly
* unmounted since it would automatically
* unmount the previous view.
*
* This should also only apply to entering and
* leaving items in the same router outlet (i.e.
* Tab1 and Tab2), otherwise this will
* return early for swipe to go back when
* going from a non-tabs page to a tabs page.
*/
if (
enteringEl !== undefined &&
isViewVisible(enteringEl) &&
leavingViewItem?.ionPageElement !== undefined &&
!isViewVisible(leavingViewItem.ionPageElement)
) {
return;
}
// Vue performs a fireLifeCycle for willEnter here on the enteringViewItem
if (leavingViewItem?.ionPageElement && enteringViewItem !== leavingViewItem) {
const enteringEl = enteringViewItem.ionPageElement;
const leavingEl = leavingViewItem.ionPageElement;
let animationBuilder = routeAnimation;
/**
* React 18 will unmount and remount IonPage
* elements in development mode when using createRoot.
* This can cause duplicate page transitions to occur.
* If we are going back from a page that
* was presented using a custom animation
* we should default to using that
* unless the developer explicitly
* provided another animation.
*/
if (oldPageElement === page) {
return;
const customAnimation = enteringViewItem!.routerAnimation;
if (animationBuilder === undefined && routeDirection === 'back' && customAnimation !== undefined) {
animationBuilder = customAnimation;
}
leavingViewItem.routerAnimation = animationBuilder;
/**
* The view should only be transitioned in the following cases:
* 1. Performing a replace or pop action, such as a swipe to go back gesture
* to animation the leaving view off the screen.
*
* 2. Navigating between top-level router outlets, such as /page-1 to /page-2;
* or navigating within a nested outlet, such as /tabs/tab-1 to /tabs/tab-2.
*
* 3. The entering view is an ion-router-outlet containing a page
* matching the current route and that hasn't already transitioned in.
*
* This should only happen when navigating directly to a nested router outlet
* route or on an initial page load (i.e. refreshing). In cases when loading
* /tabs/tab-1, we need to transition the /tabs page element into the view.
*/
this.transition(enteringEl!, leavingEl, routeDirection!, !!pushedByRoute, false, animationBuilder);
leavingEl.classList.add('ion-page-hidden');
leavingEl.setAttribute('aria-hidden', 'true');
const usingLinearNavigation = this.context.size() === 1;
if (routeAction === 'replace') {
leavingViewItem.mount = false;
leavingViewItem.ionPageElement = undefined;
leavingViewItem.ionRoute = false;
} else if (!(routeAction === 'push' && routeDirection === 'forward')) {
const shouldLeavingViewBeRemoved =
routeDirection !== 'none' && leavingViewItem && enteringViewItem !== leavingViewItem;
if (shouldLeavingViewBeRemoved) {
leavingViewItem.mount = false;
leavingViewItem.ionPageElement = undefined;
leavingViewItem.ionRoute = false;
}
if (usingLinearNavigation) {
this.context.unmountLeavingViews(id, enteringViewItem, delta);
}
} else if (usingLinearNavigation) {
this.context.mountIntermediaryViews(id, leavingViewItem, delta);
}
} else {
/**
* If there is no leaving element, just show
* the entering element. Wrap it in an raf
* in case IonContent's fullscreen callback
* is running. Otherwise we'd have a flicker.
*/
requestAnimationFrame(() => enteringEl?.classList.remove('ion-page-invisible'));
}
// this.forceUpdate();
}
// async handlePageTransition(routeInfo: RouteInfo) {
// if (!this.routerOutletElement || !this.routerOutletElement.commit) {
// /**
// * The route outlet has not mounted yet. We need to wait for it to render
// * before we can transition the page.
// *
// * Set a flag to indicate that we should transition the page after
// * the component has updated.
// */
// this.pendingPageTransition = true;
// } else {
// console.log('handling page transition...');
// let enteringViewItem = this.context.findViewItemByPathname(routeInfo.pathname, this.id);
// let leavingViewItem = this.context.findLeavingViewItemByRouteInfo(routeInfo, this.id);
// if (!leavingViewItem && routeInfo.prevRouteLastPathname) {
// leavingViewItem = this.context.findViewItemByPathname(routeInfo.prevRouteLastPathname, this.id);
// }
// // Check if leavingViewItem should be unmounted
// if (leavingViewItem) {
// if (routeInfo.routeAction === 'replace') {
// leavingViewItem.mount = false;
// } else if (!(routeInfo.routeAction === 'push' && routeInfo.routeDirection === 'forward')) {
// if (routeInfo.routeDirection !== 'none' && enteringViewItem !== leavingViewItem) {
// leavingViewItem.mount = false;
// }
// } else if (routeInfo.routeOptions?.unmount) {
// leavingViewItem.mount = false;
// }
// }
// const enteringRoute = matchRoute(this.ionRouterOutlet?.props.children, routeInfo) as React.ReactElement;
// if (enteringViewItem) {
// enteringViewItem.reactElement = enteringRoute;
// } else if (enteringRoute) {
// enteringViewItem = this.context.createViewItem(this.id, enteringRoute, routeInfo);
// this.context.addViewItem(enteringViewItem);
// }
// if (enteringViewItem && enteringViewItem.ionPageElement) {
// /**
// * If the entering view item is the same as the leaving view item,
// * then we don't need to transition.
// */
// if (enteringViewItem === leavingViewItem) {
// /**
// * If the entering view item is the same as the leaving view item,
// * we are either transitioning using parameterized routes to the same view
// * or a parent router outlet is re-rendering as a result of React props changing.
// *
// * If the route data does not match the current path, the parent router outlet
// * is attempting to transition and we cancel the operation.
// */
// if (enteringViewItem.routeData.match.url !== routeInfo.pathname) {
// return;
// }
// }
// /**
// * If there isn't a leaving view item, but the route info indicates
// * that the user has routed from a previous path, then we need
// * to find the leaving view item to transition between.
// */
// if (!leavingViewItem && this.props.routeInfo.prevRouteLastPathname) {
// leavingViewItem = this.context.findViewItemByPathname(this.props.routeInfo.prevRouteLastPathname, this.id);
// }
// /**
// * If the entering view is already visible and the leaving view is not, the transition does not need to occur.
// */
// if (
// isViewVisible(enteringViewItem.ionPageElement) &&
// leavingViewItem !== undefined &&
// !isViewVisible(leavingViewItem.ionPageElement!)
// ) {
// return;
// }
// /**
// * The view should only be transitioned in the following cases:
// * 1. Performing a replace or pop action, such as a swipe to go back gesture
// * to animation the leaving view off the screen.
// *
// * 2. Navigating between top-level router outlets, such as /page-1 to /page-2;
// * or navigating within a nested outlet, such as /tabs/tab-1 to /tabs/tab-2.
// *
// * 3. The entering view is an ion-router-outlet containing a page
// * matching the current route and that hasn't already transitioned in.
// *
// * This should only happen when navigating directly to a nested router outlet
// * route or on an initial page load (i.e. refreshing). In cases when loading
// * /tabs/tab-1, we need to transition the /tabs page element into the view.
// */
// this.transitionPage(routeInfo, enteringViewItem, leavingViewItem);
// } else if (leavingViewItem && !enteringRoute && !enteringViewItem) {
// // If we have a leavingView but no entering view/route, we are probably leaving to
// // another outlet, so hide this leavingView. We do it in a timeout to give time for a
// // transition to finish.
// // setTimeout(() => {
// if (leavingViewItem.ionPageElement) {
// leavingViewItem.ionPageElement.classList.add('ion-page-hidden');
// leavingViewItem.ionPageElement.setAttribute('aria-hidden', 'true');
// }
// // }, 250);
// }
// this.forceUpdate();
// }
// }
registerIonPage(routeInfo: RouteInfo, ionPageEl: HTMLElement) {
const { id } = this;
const viewItem = this.context.findViewItemByRouteInfo(routeInfo, id)!;
const oldIonPageEl = viewItem.ionPageElement;
this.context.registerIonPage(viewItem, ionPageEl);
/**
* If there is a registerCallback,
* then this component is being registered
* as a result of a navigation change.
*/
if (viewItem.registerCallback) {
/**
* Page should be hidden initially
* to avoid flickering.
*/
ionPageEl.classList.add('ion-page-invisible');
viewItem.registerCallback();
} else if (oldIonPageEl && !oldIonPageEl.classList.contains('ion-page-invisible')) {
/**
* If there is no registerCallback, then
* this component is likely being re-registered
* as a result of a hot module replacement.
* We need to see if the oldIonPageEl has
* .ion-page-invisible. If it does not then we
* need to remove it from the new ionPageEl otherwise
* the page will be hidden when it is replaced.
*/
ionPageEl.classList.remove('ion-page-invisible');
}
this.handlePageTransition(routeInfo);
}
async setupRouterOutlet(routerOutlet: HTMLIonRouterOutletElement) {
@@ -247,24 +465,52 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
};
const onStart = async () => {
const { id } = this;
const { routeInfo } = this.props;
let { routeAnimation: animationBuilder } = routeInfo;
const propsToUse =
this.prevProps && this.prevProps.routeInfo.pathname === routeInfo.pushedByRoute
? this.prevProps.routeInfo
: ({ pathname: routeInfo.pushedByRoute || '' } as any);
const enteringViewItem = this.context.findViewItemByRouteInfo(propsToUse, this.id, false);
const leavingViewItem = this.context.findViewItemByRouteInfo(routeInfo, this.id, false);
// const propsToUse =
// this.prevProps && this.prevProps.routeInfo.pathname === routeInfo.pushedByRoute
// ? this.prevProps.routeInfo
// : ({ pathname: routeInfo.pushedByRoute || '' } as any);
/**
* When the gesture starts, kick off
* a transition that is controlled
* via a swipe gesture.
*/
if (enteringViewItem && leavingViewItem) {
await this.transitionPage(routeInfo, enteringViewItem, leavingViewItem, 'back', true);
const enteringViewItem = this.context.findViewItemByRouteInfo(
{ pathname: routeInfo.pushedByRoute || '' } as RouteInfo,
id,
false
);
const leavingViewItem = this.context.findViewItemByRouteInfo(routeInfo, id, false);
if (leavingViewItem) {
const enteringEl = enteringViewItem?.ionPageElement;
const leavingEl = leavingViewItem?.ionPageElement;
/**
* If we are going back from a page that
* was presented using a custom animation
* we should default to using that
* unless the developer explicitly
* provided another animation.
*/
const customAnimation = enteringViewItem!.routerAnimation;
if (animationBuilder === undefined && customAnimation !== undefined) {
animationBuilder = customAnimation;
}
leavingViewItem.routerAnimation = animationBuilder;
await this.transition(enteringEl!, leavingEl!, 'back', this.context.canGoBack(), true, animationBuilder);
}
// /**
// * When the gesture starts, kick off
// * a transition that is controlled
// * via a swipe gesture.
// */
// if (enteringViewItem && leavingViewItem) {
// await this.transition(enteringViewItem.ionPageElement!, leavingViewItem.ionPageElement!, 'back', true, true);
// }
return Promise.resolve();
};
const onEnd = (shouldContinue: boolean) => {
@@ -311,82 +557,103 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
};
}
async transitionPage(
routeInfo: RouteInfo,
enteringViewItem: ViewItem,
leavingViewItem?: ViewItem,
direction?: 'forward' | 'back',
progressAnimation = false
async transition(
enteringEl: HTMLElement,
leavingEl: HTMLElement,
direction: RouterDirection,
showGoBack: boolean,
progressAnimation: boolean,
animationBuilder?: AnimationBuilder
) {
const runCommit = async (enteringEl: HTMLElement, leavingEl?: HTMLElement) => {
const skipTransition = this.skipTransition;
console.log('transition', {
enteringEl,
leavingEl,
direction,
});
const { skipTransition, routerOutletElement } = this;
/**
* If the transition was handled
* via the swipe to go back gesture,
* then we do not want to perform
* another transition.
*
* We skip adding ion-page or ion-page-invisible
* because the entering view already exists in the DOM.
* If we added the classes, there would be a flicker where
* the view would be briefly hidden.
*/
if (skipTransition) {
/**
* If the transition was handled
* via the swipe to go back gesture,
* then we do not want to perform
* another transition.
*
* We skip adding ion-page or ion-page-invisible
* because the entering view already exists in the DOM.
* If we added the classes, there would be a flicker where
* the view would be briefly hidden.
* We need to reset skipTransition before
* we call routerOutlet.commit otherwise
* the transition triggered by the swipe
* to go back gesture would reset it. In
* that case you would see a duplicate
* transition triggered by handlePageTransition
* in componentDidUpdate.
*/
if (skipTransition) {
/**
* We need to reset skipTransition before
* we call routerOutlet.commit otherwise
* the transition triggered by the swipe
* to go back gesture would reset it. In
* that case you would see a duplicate
* transition triggered by handlePageTransition
* in componentDidUpdate.
*/
this.skipTransition = false;
} else {
enteringEl.classList.add('ion-page');
enteringEl.classList.add('ion-page-invisible');
}
this.skipTransition = false;
await routerOutlet.commit(enteringEl, leavingEl, {
duration: skipTransition || directionToUse === undefined ? 0 : undefined,
direction: directionToUse,
showGoBack: !!routeInfo.pushedByRoute,
progressAnimation,
animationBuilder: routeInfo.routeAnimation,
});
};
const routerOutlet = this.routerOutletElement!;
const routeInfoFallbackDirection =
routeInfo.routeDirection === 'none' || routeInfo.routeDirection === 'root' ? undefined : routeInfo.routeDirection;
const directionToUse = direction ?? routeInfoFallbackDirection;
if (enteringViewItem && enteringViewItem.ionPageElement && this.routerOutletElement) {
if (leavingViewItem && leavingViewItem.ionPageElement && enteringViewItem === leavingViewItem) {
// If a page is transitioning to another version of itself
// we clone it so we can have an animation to show
const match = matchComponent(leavingViewItem.reactElement, routeInfo.pathname, true);
if (match) {
const newLeavingElement = clonePageElement(leavingViewItem.ionPageElement.outerHTML);
if (newLeavingElement) {
this.routerOutletElement.appendChild(newLeavingElement);
await runCommit(enteringViewItem.ionPageElement, newLeavingElement);
this.routerOutletElement.removeChild(newLeavingElement);
}
} else {
await runCommit(enteringViewItem.ionPageElement, undefined);
}
} else {
await runCommit(enteringViewItem.ionPageElement, leavingViewItem?.ionPageElement);
if (leavingViewItem && leavingViewItem.ionPageElement && !progressAnimation) {
leavingViewItem.ionPageElement.classList.add('ion-page-hidden');
leavingViewItem.ionPageElement.setAttribute('aria-hidden', 'true');
}
}
return Promise.resolve(false);
}
if (enteringEl === leavingEl) {
return Promise.resolve(false);
}
enteringEl.classList.add('ion-page-invisible');
const hasRootDirection = direction === undefined || direction === 'root' || direction === 'none';
const result = await routerOutletElement!.commit(enteringEl, leavingEl, {
/**
* replace operations result in a direction of none.
* These typically do not have need animations, so we set
* the duration to 0. However, if a developer explicitly
* passes an animationBuilder, we should assume that
* they want an animation to be played even
* though it is a replace operation.
*/
duration: hasRootDirection && animationBuilder === undefined ? 0 : undefined,
direction: direction as any, // TODO none isn't a valid direction, investigate
showGoBack,
progressAnimation,
animationBuilder,
});
return result;
// };
// const routerOutlet = this.routerOutletElement!;
// const routeInfoFallbackDirection =
// routeInfo.routeDirection === 'none' || routeInfo.routeDirection === 'root' ? undefined : routeInfo.routeDirection;
// const directionToUse = direction ?? routeInfoFallbackDirection;
// if (enteringViewItem && enteringViewItem.ionPageElement && this.routerOutletElement) {
// if (leavingViewItem && leavingViewItem.ionPageElement && enteringViewItem === leavingViewItem) {
// // If a page is transitioning to another version of itself
// // we clone it so we can have an animation to show
// const match = matchComponent(leavingViewItem.reactElement, routeInfo.pathname, true);
// if (match) {
// const newLeavingElement = clonePageElement(leavingViewItem.ionPageElement.outerHTML);
// if (newLeavingElement) {
// this.routerOutletElement.appendChild(newLeavingElement);
// await runCommit(enteringViewItem.ionPageElement, newLeavingElement);
// this.routerOutletElement.removeChild(newLeavingElement);
// }
// } else {
// await runCommit(enteringViewItem.ionPageElement, undefined);
// }
// } else {
// await runCommit(enteringViewItem.ionPageElement, leavingViewItem?.ionPageElement);
// if (leavingViewItem && leavingViewItem.ionPageElement && !progressAnimation) {
// leavingViewItem.ionPageElement.classList.add('ion-page-hidden');
// leavingViewItem.ionPageElement.setAttribute('aria-hidden', 'true');
// }
// }
// }
}
render() {
@@ -394,10 +661,6 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
const ionRouterOutlet = React.Children.only(children) as React.ReactElement;
this.ionRouterOutlet = ionRouterOutlet;
const components = this.context.getChildrenToRender(this.id, this.ionRouterOutlet, this.props.routeInfo, () => {
this.forceUpdate();
});
return (
<StackContext.Provider value={this.stackContextValue}>
{React.cloneElement(
@@ -410,14 +673,17 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
if (ionRouterOutlet.props.forwardedRef) {
ionRouterOutlet.props.forwardedRef.current = node;
}
this.routerOutletElement = node;
if (node) {
this.routerOutletElement = node;
console.log('assigned router outlet element node...', node);
}
const { ref } = ionRouterOutlet as any;
if (typeof ref === 'function') {
ref(node);
}
},
},
components
this.state.components
)}
</StackContext.Provider>
);
@@ -458,13 +724,13 @@ function matchRoute(node: React.ReactNode, routeInfo: RouteInfo) {
return matchedNode;
}
function matchComponent(node: React.ReactElement, pathname: string, forceExact?: boolean) {
const matchProps = {
exact: forceExact ? true : node.props.exact,
path: node.props.path || node.props.from,
component: node.props.component,
};
const match = matchPath(pathname, matchProps);
// function matchComponent(node: React.ReactElement, pathname: string, forceExact?: boolean) {
// const matchProps = {
// exact: forceExact ? true : node.props.exact,
// path: node.props.path || node.props.from,
// component: node.props.component,
// };
// const match = matchPath(pathname, matchProps);
return match;
}
// return match;
// }

View File

@@ -0,0 +1,65 @@
import { matchPath as reactRouterMatchPath } from 'react-router';
interface MatchPathOptions {
/**
* The pathname to match against.
*/
pathname: string;
/**
* The props to match against, they are identical to the matching props `Route` accepts.
*/
componentProps: {
path?: string;
from?: string;
component?: any;
exact?: boolean;
};
}
/**
* @see https://v5.reactrouter.com/web/api/matchPath
*/
export const matchPath = ({ pathname, componentProps }: MatchPathOptions): false | ReturnType<typeof reactRouterMatchPath> => {
const { exact, component } = componentProps;
const path = componentProps.path || componentProps.from;
/***
* The props to match against, they are identical
* to the matching props `Route` accepts. It could also be a string
* or an array of strings as shortcut for `{ path }`.
*/
const matchProps = {
exact,
path,
component
};
const match = reactRouterMatchPath(pathname, matchProps);
if (!match) {
return false;
}
const hasParameter = match.path.includes(':');
if (hasParameter) {
console.log('the match path has a parameter!!', {
pathname,
url: match.url,
match
})
}
if (hasParameter && match.url.includes(':')) {
return false;
}
if (hasParameter && pathname !== match.url) {
console.log('discarding the match because it has a path parameter', {
pathname,
path,
match
})
return false;
}
return match;
}

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [7.3.1](https://github.com/ionic-team/ionic-framework/compare/v7.3.0...v7.3.1) (2023-08-23)
### Bug Fixes
* **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)
**Note:** Version bump only for package @ionic/react

View File

@@ -1,15 +1,15 @@
{
"name": "@ionic/react",
"version": "7.3.0",
"version": "7.3.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/react",
"version": "7.3.0",
"version": "7.3.1",
"license": "MIT",
"dependencies": {
"@ionic/core": "^7.3.0",
"@ionic/core": "^7.3.1",
"ionicons": "^7.0.0",
"tslib": "*"
},
@@ -697,25 +697,25 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.4.tgz",
"integrity": "sha512-FM7r1+drjC6Kf4RVkUjwA7mn8wM4QF1W+YjzjYExUNd3O5XGBYxW7OrNXGX3asnhHVDN9QRs6QLKa59SjMQHPw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.3.1.tgz",
"integrity": "sha512-RdSJsFYx2oJ08duw+DMWVAfzjxlnA+o1saX5gOLSjP5SpwP7FqURFfXrFHIFN2vIKbeqUZTZjQwPHnrFsX04dQ==",
"dependencies": {
"@stencil/core": "^3.4.0",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
}
},
"node_modules/@ionic/core/node_modules/@stencil/core": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.1.tgz",
"integrity": "sha512-7rjOmM0W9K5op2gtOQRLERGH1155rv2fm6ppxOzYqqG8ISct4m9skp5XgUBYPu+GSPsJFdRuCIQs0IuVsG/7+g==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg==",
"bin": {
"stencil": "bin/stencil"
},
"engines": {
"node": ">=14.10.0",
"npm": ">=6.0.0"
"node": ">=16.0.0",
"npm": ">=7.10.0"
}
},
"node_modules/@ionic/eslint-config": {
@@ -11778,19 +11778,19 @@
"dev": true
},
"@ionic/core": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.4.tgz",
"integrity": "sha512-FM7r1+drjC6Kf4RVkUjwA7mn8wM4QF1W+YjzjYExUNd3O5XGBYxW7OrNXGX3asnhHVDN9QRs6QLKa59SjMQHPw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.3.1.tgz",
"integrity": "sha512-RdSJsFYx2oJ08duw+DMWVAfzjxlnA+o1saX5gOLSjP5SpwP7FqURFfXrFHIFN2vIKbeqUZTZjQwPHnrFsX04dQ==",
"requires": {
"@stencil/core": "^3.4.0",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
},
"dependencies": {
"@stencil/core": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.1.tgz",
"integrity": "sha512-7rjOmM0W9K5op2gtOQRLERGH1155rv2fm6ppxOzYqqG8ISct4m9skp5XgUBYPu+GSPsJFdRuCIQs0IuVsG/7+g=="
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg=="
}
}
},

View File

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

View File

@@ -2,8 +2,11 @@
set -e
# Copy core dist
rm -rf node_modules/@ionic/core/dist node_modules/@ionic/core/components
cp -a ../../core/dist node_modules/@ionic/core/dist
cp -a ../../core/components node_modules/@ionic/core/components
cp -a ../../core/package.json node_modules/@ionic/core/package.json
# Delete old packages
rm -f *.tgz
# Pack @ionic/core
npm pack ../../core
# Install Dependencies
npm install *.tgz --no-save

View File

@@ -63,7 +63,14 @@ export const createInlineOverlayComponent = <PropType, ElementType>(
componentDidUpdate(prevProps: IonicReactInternalProps<PropType>) {
const node = this.ref.current! as HTMLElement;
attachProps(node, this.props, prevProps);
/**
* onDidDismiss and onWillPresent have manual implementations that
* will invoke the original handler. We need to filter those out
* so they don't get attached twice and called twice.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { onDidDismiss, onWillPresent, ...cProps } = this.props;
attachProps(node, cProps, prevProps);
}
componentWillUnmount() {

View File

@@ -8,7 +8,11 @@ export interface RouteInfo<TOptions = any> {
lastPathname?: string;
prevRouteLastPathname?: string;
routeAction?: RouteAction;
// In Ionic Vue this is called routerDirection
// TODO we should align the naming
routeDirection?: RouterDirection;
// In Ionic Vue this is called routerAnimation
// TODO we should align the naming
routeAnimation?: AnimationBuilder;
routeOptions?: TOptions;
params?: { [key: string]: string | string[] };
@@ -16,4 +20,5 @@ export interface RouteInfo<TOptions = any> {
pathname: string;
search: string;
tab?: string;
delta?: number;
}

View File

@@ -35,7 +35,9 @@ export class OutletPageManager extends React.Component<OutletPageManagerProps> {
if (!this.outletIsReady) {
componentOnReady(this.ionRouterOutlet, () => {
this.outletIsReady = true;
this.context.registerIonPage(this.ionRouterOutlet!, this.props.routeInfo!);
console.log('registerIonPage from OutletPageManager');
this.context.registerIonPage(this.props.routeInfo!, this.ionRouterOutlet!);
// this.context.registerIonPage(this.ionRouterOutlet!, this.props.routeInfo!);
});
}

View File

@@ -30,7 +30,11 @@ export class PageManager extends React.PureComponent<PageManagerProps> {
if (this.context.isInOutlet()) {
this.ionPageElementRef.current.classList.add('ion-page-invisible');
}
this.context.registerIonPage(this.ionPageElementRef.current, this.props.routeInfo!);
console.log('registerIonPage from PageManager');
this.context.registerIonPage(this.props.routeInfo!, this.ionPageElementRef.current);
// this.context.registerIonPage(this.ionPageElementRef.current, this.props.routeInfo!);
this.ionPageElementRef.current.addEventListener('ionViewWillEnter', this.ionViewWillEnterHandler.bind(this));
this.ionPageElementRef.current.addEventListener('ionViewDidEnter', this.ionViewDidEnterHandler.bind(this));
this.ionPageElementRef.current.addEventListener('ionViewWillLeave', this.ionViewWillLeaveHandler.bind(this));

View File

@@ -14,17 +14,52 @@ export interface RouteManagerContextState {
routeInfo: RouteInfo,
page?: HTMLElement
) => ViewItem;
findViewItemByPathname(pathname: string, outletId?: string): ViewItem | undefined;
findViewItemByPathname(pathname: string, outletId?: string, forceExact?: boolean): ViewItem | undefined;
findLeavingViewItemByRouteInfo: (routeInfo: RouteInfo, outletId?: string) => ViewItem | undefined;
findViewItemByRouteInfo: (routeInfo: RouteInfo, outletId?: string, updateMatch?: boolean) => ViewItem | undefined;
getChildrenToRender: (
outletId: string,
ionRouterOutlet: React.ReactElement,
routeInfo: RouteInfo,
reRender: () => void
routeInfo: RouteInfo
) => React.ReactNode[];
goBack: () => void;
/**
* Returns the number of active stacks.
* This is useful for determining if an app
* is using linear navigation only or non-linear
* navigation. Multiple stacks indicate an app
* is using non-linear navigation.
*/
size: () => number;
/**
* When navigating backwards, we need to clean up and
* leaving pages so that they are re-created if
* we ever navigate back to them. This is especially
* important when using router.go and stepping back
* multiple pages at a time.
*/
unmountLeavingViews: (outletId: string, viewItem: ViewItem, delta?: number) => void;
/**
* When navigating forward it is possible for
* developers to step forward over multiple views.
* The intermediary views need to be remounted so that
* swipe to go back works properly.
* We need to account for the delta value here too because
* we do not want to remount an unrelated view.
* Example:
* /home --> /page2 --> router.back() --> /page3
* Going to /page3 would remount /page2 since we do
* not prune /page2 from the stack. However, /page2
* needs to remain in the stack.
* Example:
* /home --> /page2 --> /page3 --> router.go(-2) --> router.go(2)
* We would end up on /page3, but users need to be able to swipe
* to go back to /page2 and /home, so we need both pages mounted
* in the DOM.
*/
mountIntermediaryViews: (outletId: string, viewItem: ViewItem, delta?: number) => void;
unMountViewItem: (viewItem: ViewItem) => void;
registerIonPage: (viewItem: ViewItem, ionPage: HTMLElement) => void;
}
// TODO(FW-2959): types
@@ -36,7 +71,11 @@ export const RouteManagerContext = /*@__PURE__*/ React.createContext<RouteManage
findViewItemByPathname: () => undefined,
findLeavingViewItemByRouteInfo: () => undefined,
findViewItemByRouteInfo: () => undefined,
getChildrenToRender: () => undefined as any,
getChildrenToRender: () => [],
goBack: () => undefined,
size: () => 0,
unmountLeavingViews: () => undefined,
mountIntermediaryViews: () => undefined,
unMountViewItem: () => undefined,
registerIonPage: () => undefined
});

View File

@@ -3,7 +3,7 @@ import React from 'react';
import type { RouteInfo } from '../models/RouteInfo';
export interface StackContextState {
registerIonPage: (page: HTMLElement, routeInfo: RouteInfo) => void;
registerIonPage: (routeInfo: RouteInfo, page: HTMLElement) => void;
isInOutlet: () => boolean;
}

View File

@@ -1,3 +1,4 @@
import type { AnimationBuilder } from '@ionic/core';
import type { ReactElement } from 'react';
export interface ViewItem<T = any> {
@@ -10,4 +11,9 @@ export interface ViewItem<T = any> {
transitionHtml?: string;
outletId: string;
disableIonPageManagement?: boolean;
routerAnimation?: AnimationBuilder;
/**
* Callback function when the view item is registered.
*/
registerCallback?: () => void;
}

View File

@@ -44,6 +44,69 @@ export abstract class ViewStacks {
}
}
/**
* When navigating backwards, we need to clean up and
* leaving pages so that they are re-created if
* we ever navigate back to them. This is especially
* important when using router.go and stepping back
* multiple pages at a time.
*/
unmountLeavingViews(outletId: string, viewItem: ViewItem, delta = 1) {
const viewStack = this.viewStacks[outletId];
if (!viewStack) return;
const startIndex = viewStack.findIndex(v => v === viewItem);
for (let i = startIndex + 1; i < startIndex - delta; i++) {
const viewItem = viewStack[1];
viewItem.mount = false;
viewItem.ionPageElement = undefined;
viewItem.ionRoute = false;
// This is Vue specific - TODO find if there is a React equivalent needed
// viewItem.matchedRoute.instances = {};
}
}
/**
* When navigating forward it is possible for
* developers to step forward over multiple views.
* The intermediary views need to be remounted so that
* swipe to go back works properly.
* We need to account for the delta value here too because
* we do not want to remount an unrelated view.
* Example:
* /home --> /page2 --> router.back() --> /page3
* Going to /page3 would remount /page2 since we do
* not prune /page2 from the stack. However, /page2
* needs to remain in the stack.
* Example:
* /home --> /page2 --> /page3 --> router.go(-2) --> router.go(2)
* We would end up on /page3, but users need to be able to swipe
* to go back to /page2 and /home, so we need both pages mounted
* in the DOM.
*/
mountIntermediaryViews(outletId: string, viewItem: ViewItem, delta = 1) {
const viewStack = this.viewStacks[outletId];
if (!viewStack) return;
const startIndex = viewStack.findIndex(v => v === viewItem);
for (let i = startIndex + 1; i < startIndex + delta; i++) {
viewStack[i].mount = true;
}
}
/**
* Returns the number of active stacks.
* This is useful for determining if an app
* is using linear navigation only or non-linear
* navigation. Multiple stacks indicate an app
* is using non-linear navigation.
*/
size() {
return Object.keys(this.viewStacks || {}).length;
}
protected getStackIds() {
return Object.keys(this.viewStacks);
}
@@ -63,7 +126,7 @@ export abstract class ViewStacks {
routeInfo: RouteInfo,
page?: HTMLElement
): ViewItem;
abstract findViewItemByPathname(pathname: string, outletId?: string): ViewItem | undefined;
abstract findViewItemByPathname(pathname: string, outletId?: string, forceExact?: boolean): ViewItem | undefined;
abstract findViewItemByRouteInfo(
routeInfo: RouteInfo,
outletId?: string,
@@ -74,7 +137,5 @@ export abstract class ViewStacks {
outletId: string,
ionRouterOutlet: React.ReactElement,
routeInfo: RouteInfo,
reRender: () => void,
setInTransition: () => void
): React.ReactNode[];
}

View File

@@ -1,10 +1,11 @@
import React from 'react';
import { IonButton, IonContent, IonPage, IonActionSheet } from '@ionic/react';
import { useState } from 'react';
import React, { useState } from 'react';
const ActionSheetComponent: React.FC = () => {
const [message, setMessage] = useState('');
const [show, setShow] = useState(false);
const [willPresentCount, setWillPresentCount] = useState(0);
const [didDismissCount, setDidDismissCount] = useState(0);
return (
<IonPage>
@@ -26,7 +27,13 @@ const ActionSheetComponent: React.FC = () => {
},
]}
header="Action Sheet"
onDidDismiss={() => setShow(false)}
onWillPresent={() => {
setWillPresentCount(willPresentCount + 1);
}}
onDidDismiss={() => {
setDidDismissCount(didDismissCount + 1);
setShow(false);
}}
/>
<IonButton expand="block" onClick={() => setShow(true)}>
Show ActionSheet
@@ -41,6 +48,8 @@ const ActionSheetComponent: React.FC = () => {
Show ActionSheet, hide after 250 mss
</IonButton>
<div>{message}</div>
<div>onWillPresent count: {willPresentCount}</div>
<div>onDidDismiss count: {didDismissCount}</div>
</IonContent>
</IonPage>
);

View File

@@ -17,11 +17,19 @@ describe('IonActionSheet', () => {
});
it('display action and call dismiss to close it', () => {
//show action sheet
cy.get('ion-content').contains('onWillPresent count: 0');
cy.get('ion-content').contains('onDidDismiss count: 0');
// show action sheet
cy.get('ion-button').contains('Show ActionSheet, hide after 250 ms').click();
cy.get('ion-action-sheet').contains('Action Sheet');
//verify action sheet is hidden
// verify action sheet is hidden
cy.get('ion-action-sheet').should('not.be.visible');
// verify lifecycle events are called once
cy.get('ion-content').contains('onWillPresent count: 1');
cy.get('ion-content').contains('onDidDismiss count: 0');
});
});

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [7.3.1](https://github.com/ionic-team/ionic-framework/compare/v7.3.0...v7.3.1) (2023-08-23)
**Note:** Version bump only for package @ionic/vue-router
# [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/vue-router

View File

@@ -1,15 +1,15 @@
{
"name": "@ionic/vue-router",
"version": "7.3.0",
"version": "7.3.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/vue-router",
"version": "7.3.0",
"version": "7.3.1",
"license": "MIT",
"dependencies": {
"@ionic/vue": "^7.3.0"
"@ionic/vue": "^7.3.1"
},
"devDependencies": {
"@ionic/eslint-config": "^0.3.0",
@@ -660,11 +660,11 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.4.tgz",
"integrity": "sha512-FM7r1+drjC6Kf4RVkUjwA7mn8wM4QF1W+YjzjYExUNd3O5XGBYxW7OrNXGX3asnhHVDN9QRs6QLKa59SjMQHPw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.3.1.tgz",
"integrity": "sha512-RdSJsFYx2oJ08duw+DMWVAfzjxlnA+o1saX5gOLSjP5SpwP7FqURFfXrFHIFN2vIKbeqUZTZjQwPHnrFsX04dQ==",
"dependencies": {
"@stencil/core": "^3.4.0",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
}
@@ -871,11 +871,11 @@
}
},
"node_modules/@ionic/vue": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-7.2.4.tgz",
"integrity": "sha512-mgtWSBFPSCOvNwiMa1ssu+wZhwBY053DNmmC37VltsJQNVaPS/e26V8Zr4S7V7rarVaY2bKEqQ32Y4QbUEnGag==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-7.3.1.tgz",
"integrity": "sha512-URxNXLYo4gh0riQlHiCoGDo7GqEqbVJi/1gMamM3tGBQHCMNZdm9IDfIDYjHY4L3dVi5pF+szl/b4p2lsIV3hw==",
"dependencies": {
"@ionic/core": "7.2.4",
"@ionic/core": "7.3.1",
"ionicons": "^7.0.0"
}
},
@@ -1323,15 +1323,15 @@
}
},
"node_modules/@stencil/core": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.2.tgz",
"integrity": "sha512-FAUhUVaakCy29nU2GwO/HQBRV1ihPRvncz3PUc8oR+UJLAxGabTmP8PLY7wvHfbw+Cvi4VXfJFTBvdfDu6iKPQ==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg==",
"bin": {
"stencil": "bin/stencil"
},
"engines": {
"node": ">=14.10.0",
"npm": ">=6.0.0"
"node": ">=16.0.0",
"npm": ">=7.10.0"
}
},
"node_modules/@tootallnate/once": {
@@ -6807,9 +6807,9 @@
}
},
"node_modules/tslib": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz",
"integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig=="
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"node_modules/tsutils": {
"version": "3.21.0",
@@ -7697,11 +7697,11 @@
"dev": true
},
"@ionic/core": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.4.tgz",
"integrity": "sha512-FM7r1+drjC6Kf4RVkUjwA7mn8wM4QF1W+YjzjYExUNd3O5XGBYxW7OrNXGX3asnhHVDN9QRs6QLKa59SjMQHPw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.3.1.tgz",
"integrity": "sha512-RdSJsFYx2oJ08duw+DMWVAfzjxlnA+o1saX5gOLSjP5SpwP7FqURFfXrFHIFN2vIKbeqUZTZjQwPHnrFsX04dQ==",
"requires": {
"@stencil/core": "^3.4.0",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
},
@@ -7829,11 +7829,11 @@
"requires": {}
},
"@ionic/vue": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-7.2.4.tgz",
"integrity": "sha512-mgtWSBFPSCOvNwiMa1ssu+wZhwBY053DNmmC37VltsJQNVaPS/e26V8Zr4S7V7rarVaY2bKEqQ32Y4QbUEnGag==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-7.3.1.tgz",
"integrity": "sha512-URxNXLYo4gh0riQlHiCoGDo7GqEqbVJi/1gMamM3tGBQHCMNZdm9IDfIDYjHY4L3dVi5pF+szl/b4p2lsIV3hw==",
"requires": {
"@ionic/core": "7.2.4",
"@ionic/core": "7.3.1",
"ionicons": "^7.0.0"
}
},
@@ -8192,9 +8192,9 @@
}
},
"@stencil/core": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.2.tgz",
"integrity": "sha512-FAUhUVaakCy29nU2GwO/HQBRV1ihPRvncz3PUc8oR+UJLAxGabTmP8PLY7wvHfbw+Cvi4VXfJFTBvdfDu6iKPQ=="
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg=="
},
"@tootallnate/once": {
"version": "2.0.0",
@@ -12220,9 +12220,9 @@
}
},
"tslib": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz",
"integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig=="
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"tsutils": {
"version": "3.21.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/vue-router",
"version": "7.3.0",
"version": "7.3.1",
"description": "Vue Router integration for @ionic/vue",
"scripts": {
"test.spec": "jest",
@@ -45,7 +45,7 @@
},
"homepage": "https://github.com/ionic-team/ionic#readme",
"dependencies": {
"@ionic/vue": "^7.3.0"
"@ionic/vue": "^7.3.1"
},
"devDependencies": {
"@ionic/eslint-config": "^0.3.0",

View File

@@ -11,7 +11,7 @@ export const createViewStacks = (router: Router) => {
* Returns the number of active stacks.
* This is useful for determining if an app
* is using linear navigation only or non-linear
* navigation. Multiple stacks indiciate an app
* navigation. Multiple stacks indicate an app
* is using non-linear navigation.
*/
const size = () => Object.keys(viewStacks).length;

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [7.3.1](https://github.com/ionic-team/ionic-framework/compare/v7.3.0...v7.3.1) (2023-08-23)
**Note:** Version bump only for package @ionic/vue
# [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/vue

View File

@@ -1,15 +1,15 @@
{
"name": "@ionic/vue",
"version": "7.3.0",
"version": "7.3.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/vue",
"version": "7.3.0",
"version": "7.3.1",
"license": "MIT",
"dependencies": {
"@ionic/core": "^7.3.0",
"@ionic/core": "^7.3.1",
"ionicons": "^7.0.0"
},
"devDependencies": {
@@ -207,11 +207,11 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.4.tgz",
"integrity": "sha512-FM7r1+drjC6Kf4RVkUjwA7mn8wM4QF1W+YjzjYExUNd3O5XGBYxW7OrNXGX3asnhHVDN9QRs6QLKa59SjMQHPw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.3.1.tgz",
"integrity": "sha512-RdSJsFYx2oJ08duw+DMWVAfzjxlnA+o1saX5gOLSjP5SpwP7FqURFfXrFHIFN2vIKbeqUZTZjQwPHnrFsX04dQ==",
"dependencies": {
"@stencil/core": "^3.4.0",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
}
@@ -423,15 +423,15 @@
}
},
"node_modules/@stencil/core": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.1.tgz",
"integrity": "sha512-7rjOmM0W9K5op2gtOQRLERGH1155rv2fm6ppxOzYqqG8ISct4m9skp5XgUBYPu+GSPsJFdRuCIQs0IuVsG/7+g==",
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg==",
"bin": {
"stencil": "bin/stencil"
},
"engines": {
"node": ">=14.10.0",
"npm": ">=6.0.0"
"node": ">=16.0.0",
"npm": ">=7.10.0"
}
},
"node_modules/@types/json-schema": {
@@ -3746,11 +3746,11 @@
"dev": true
},
"@ionic/core": {
"version": "7.2.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.4.tgz",
"integrity": "sha512-FM7r1+drjC6Kf4RVkUjwA7mn8wM4QF1W+YjzjYExUNd3O5XGBYxW7OrNXGX3asnhHVDN9QRs6QLKa59SjMQHPw==",
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.3.1.tgz",
"integrity": "sha512-RdSJsFYx2oJ08duw+DMWVAfzjxlnA+o1saX5gOLSjP5SpwP7FqURFfXrFHIFN2vIKbeqUZTZjQwPHnrFsX04dQ==",
"requires": {
"@stencil/core": "^3.4.0",
"@stencil/core": "^4.1.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
},
@@ -3885,9 +3885,9 @@
}
},
"@stencil/core": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.1.tgz",
"integrity": "sha512-7rjOmM0W9K5op2gtOQRLERGH1155rv2fm6ppxOzYqqG8ISct4m9skp5XgUBYPu+GSPsJFdRuCIQs0IuVsG/7+g=="
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.1.0.tgz",
"integrity": "sha512-yIpL+CX02fy5zvFXwXcHZjjEILRm3aiONbucpfLIWPS7zcBAuucdROssartEa+D7E1JRko97ydxn1Ntdu4GoWg=="
},
"@types/json-schema": {
"version": "7.0.11",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/vue",
"version": "7.3.0",
"version": "7.3.1",
"description": "Vue specific wrapper for @ionic/core",
"scripts": {
"eslint": "eslint src",
@@ -66,7 +66,7 @@
"vue-router": "^4.0.16"
},
"dependencies": {
"@ionic/core": "^7.3.0",
"@ionic/core": "^7.3.1",
"ionicons": "^7.0.0"
},
"vetur": {