mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
409 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f4d3a36df | ||
|
|
a633db1688 | ||
|
|
3ec9607281 | ||
|
|
84e306c1a6 | ||
|
|
713ea8adaa | ||
|
|
8f7853c5e9 | ||
|
|
f94300cbb3 | ||
|
|
3a22105375 | ||
|
|
6bbdb80871 | ||
|
|
7cd68b59fc | ||
|
|
d237e808c2 | ||
|
|
e043ea9dae | ||
|
|
f9579bcd1d | ||
|
|
23f327ecb6 | ||
|
|
3e1dfc657b | ||
|
|
32bb1f7230 | ||
|
|
48352533ff | ||
|
|
e3a5d1f93a | ||
|
|
f08ac68e0b | ||
|
|
7951289460 | ||
|
|
9f32d0edd7 | ||
|
|
27a68b3f7f | ||
|
|
962783bfba | ||
|
|
e82648bda2 | ||
|
|
9b85e13493 | ||
|
|
462cee5b2e | ||
|
|
d792560447 | ||
|
|
7272d481bf | ||
|
|
c83edd9329 | ||
|
|
a869ca0bba | ||
|
|
c2348f71a5 | ||
|
|
67ffe2f9cc | ||
|
|
2e02dc7319 | ||
|
|
b7761fe353 | ||
|
|
00891119f7 | ||
|
|
c91819c94f | ||
|
|
3ef6ecf422 | ||
|
|
7ba718c0db | ||
|
|
e8ab0fd317 | ||
|
|
815fa2eb06 | ||
|
|
c52b3b4997 | ||
|
|
23ce6fae9e | ||
|
|
a8455a90ff | ||
|
|
798103bf63 | ||
|
|
bd8ca63451 | ||
|
|
cf27063aae | ||
|
|
fb18f3ba25 | ||
|
|
71137a2ffa | ||
|
|
544e550286 | ||
|
|
854004cf2c | ||
|
|
6b5a59dc43 | ||
|
|
fbfc07688e | ||
|
|
9b075ef529 | ||
|
|
97fec92365 | ||
|
|
26e6d6f115 | ||
|
|
978cc39009 | ||
|
|
53179c475c | ||
|
|
7ae9303a97 | ||
|
|
045bc59b75 | ||
|
|
08af35aad2 | ||
|
|
5b932152f8 | ||
|
|
9993b73355 | ||
|
|
f1eeed1c91 | ||
|
|
ee81907713 | ||
|
|
1f40d8fffd | ||
|
|
723196a157 | ||
|
|
0b0dddf8ec | ||
|
|
e33bfc7f1d | ||
|
|
03c1d19e07 | ||
|
|
8beeff2c52 | ||
|
|
f060db5fe6 | ||
|
|
4a3ff61571 | ||
|
|
908f36f574 | ||
|
|
292cc867a5 | ||
|
|
ab20bf472d | ||
|
|
470615eb05 | ||
|
|
f56fea6a1f | ||
|
|
7fda509333 | ||
|
|
0031ab82b7 | ||
|
|
e059fc8048 | ||
|
|
7953088418 | ||
|
|
1add112be6 | ||
|
|
f16b118794 | ||
|
|
d71c1cd6b0 | ||
|
|
ccd46028f2 | ||
|
|
24840d4d99 | ||
|
|
a656dadf4b | ||
|
|
03c834c647 | ||
|
|
876ab41ba8 | ||
|
|
dfa2b13c3a | ||
|
|
c6bb2730a8 | ||
|
|
08daaeb1a3 | ||
|
|
1f51ab27c4 | ||
|
|
36a58df181 | ||
|
|
4038e0a60c | ||
|
|
6042b39313 | ||
|
|
dc04a4b8a9 | ||
|
|
98499acff9 | ||
|
|
2417d32316 | ||
|
|
735280168f | ||
|
|
ef10f190cd | ||
|
|
b9b60df0a4 | ||
|
|
b69fb69a1a | ||
|
|
92e0f98633 | ||
|
|
0d58101edc | ||
|
|
44c88ad908 | ||
|
|
45b82dc466 | ||
|
|
22ac160021 | ||
|
|
b46a025576 | ||
|
|
7d5477cf22 | ||
|
|
f48dc3dd1a | ||
|
|
301b0fc1d1 | ||
|
|
d4c7b036fc | ||
|
|
8a88dd25b6 | ||
|
|
ad00679da9 | ||
|
|
598a13ecc0 | ||
|
|
34dfc3ce98 | ||
|
|
78e477b2a7 | ||
|
|
832306cf6e | ||
|
|
73599c22aa | ||
|
|
26ecf2b10f | ||
|
|
1e014f0b14 | ||
|
|
b40f7d36d5 | ||
|
|
7f1829eb21 | ||
|
|
eb9bad7a31 | ||
|
|
5bdd16404e | ||
|
|
9e4346bb44 | ||
|
|
eca4121dc6 | ||
|
|
828eaaf3d3 | ||
|
|
335e02aa44 | ||
|
|
0f242fd956 | ||
|
|
8b8bbb8a8d | ||
|
|
36fd78281c | ||
|
|
b85b7c6b9d | ||
|
|
320719b904 | ||
|
|
4ad078a975 | ||
|
|
d075b2c6e2 | ||
|
|
64460b8efc | ||
|
|
a583902f30 | ||
|
|
aac60b1390 | ||
|
|
500edddcb1 | ||
|
|
43ed2b0d1d | ||
|
|
267e733109 | ||
|
|
05a407196f | ||
|
|
bba311653d | ||
|
|
b8b8d697f4 | ||
|
|
577318be15 | ||
|
|
0b78a00afb | ||
|
|
ef29b5fb04 | ||
|
|
b959e0b5ec | ||
|
|
20c146e1e5 | ||
|
|
8d2a47e881 | ||
|
|
055e12570d | ||
|
|
35c143a036 | ||
|
|
a3e23fc9fa | ||
|
|
58672fb221 | ||
|
|
5c5934bc24 | ||
|
|
7610787e09 | ||
|
|
54bdb367c2 | ||
|
|
10de1da948 | ||
|
|
4a0de23cab | ||
|
|
be8dd55c21 | ||
|
|
3a4f475889 | ||
|
|
49eba77c37 | ||
|
|
1899c13385 | ||
|
|
aac7a23fd5 | ||
|
|
5ba0aa9aac | ||
|
|
c8104a29ec | ||
|
|
17ad73ace2 | ||
|
|
b2290a6420 | ||
|
|
fbb76e63ad | ||
|
|
d53e7aa51c | ||
|
|
11cde99e20 | ||
|
|
0fd3e5d4c5 | ||
|
|
f0af70736b | ||
|
|
292b24ad8f | ||
|
|
6ddde3aa98 | ||
|
|
4bba540311 | ||
|
|
454510092e | ||
|
|
7ab9479f2c | ||
|
|
017febed96 | ||
|
|
f7d174f2f8 | ||
|
|
b79f68a776 | ||
|
|
62abb972e9 | ||
|
|
53720dfcc7 | ||
|
|
55fc424835 | ||
|
|
7c7c6e47a9 | ||
|
|
5d8b6a5a70 | ||
|
|
4be8d7e622 | ||
|
|
d13983451a | ||
|
|
788a56c5e9 | ||
|
|
17345efb14 | ||
|
|
805b225876 | ||
|
|
845def82f5 | ||
|
|
4339ec3aa9 | ||
|
|
48553511be | ||
|
|
82a63a972d | ||
|
|
adaaf89737 | ||
|
|
00a27dc37b | ||
|
|
6252458d49 | ||
|
|
04a0e41e67 | ||
|
|
b8d4961483 | ||
|
|
bdd5109dbe | ||
|
|
da38647478 | ||
|
|
fa87e35a05 | ||
|
|
a8f9dfe0e1 | ||
|
|
875d56363c | ||
|
|
051198928e | ||
|
|
578108d586 | ||
|
|
3d04417a05 | ||
|
|
a3644a5420 | ||
|
|
d788a8eac6 | ||
|
|
3cad7787c2 | ||
|
|
ba0def3a38 | ||
|
|
394cf8d28f | ||
|
|
a6cb5f218e | ||
|
|
7bb6a8e8b1 | ||
|
|
9c65d5d65a | ||
|
|
2c24df2578 | ||
|
|
71d540023b | ||
|
|
5eb942f680 | ||
|
|
bb6ae9924c | ||
|
|
85093d6352 | ||
|
|
9030dcc111 | ||
|
|
cc8678ad58 | ||
|
|
669ec0da3d | ||
|
|
ef989779b0 | ||
|
|
f912206af8 | ||
|
|
ddb8ef82f0 | ||
|
|
0c83fd3f1a | ||
|
|
38ffb98421 | ||
|
|
0e4726b62a | ||
|
|
9c2cd31b1c | ||
|
|
ed69d24013 | ||
|
|
d12757f975 | ||
|
|
0fa645b8cc | ||
|
|
2457a23e95 | ||
|
|
ae0ecccd2e | ||
|
|
3cdab10aa0 | ||
|
|
95945c05a5 | ||
|
|
33acd78469 | ||
|
|
ca233b547a | ||
|
|
464ec3b70a | ||
|
|
bd96491d03 | ||
|
|
a5b9066fee | ||
|
|
c178236e32 | ||
|
|
f6524cf8c3 | ||
|
|
8d59d44431 | ||
|
|
8097f578f4 | ||
|
|
446fe29b15 | ||
|
|
6fbb90896b | ||
|
|
73e2123ce8 | ||
|
|
70db7080b1 | ||
|
|
9e63947e3c | ||
|
|
0dd2f34dfa | ||
|
|
07e739a364 | ||
|
|
e5c8c10029 | ||
|
|
38ae3620a2 | ||
|
|
72be80cb58 | ||
|
|
cc60b60135 | ||
|
|
447497427e | ||
|
|
18b347b4e9 | ||
|
|
29bb4fcb05 | ||
|
|
11aa48c83c | ||
|
|
f13722cc20 | ||
|
|
983382c327 | ||
|
|
1b16e1f378 | ||
|
|
494991e9fb | ||
|
|
1199c53437 | ||
|
|
6e1a8f1df2 | ||
|
|
52e5a8d3e3 | ||
|
|
0d17e05edc | ||
|
|
fa1317359a | ||
|
|
d87170db3a | ||
|
|
5756789b42 | ||
|
|
b081ca4dd0 | ||
|
|
77c980b032 | ||
|
|
05eb5ddaf8 | ||
|
|
9de09bd4f5 | ||
|
|
2f34f52536 | ||
|
|
ec3e19e66c | ||
|
|
a512287e4b | ||
|
|
48814ab134 | ||
|
|
28249924a8 | ||
|
|
e620d813b3 | ||
|
|
0db905a871 | ||
|
|
c87e26131a | ||
|
|
06c4233a54 | ||
|
|
9585723dda | ||
|
|
046ca8f732 | ||
|
|
fdd2978774 | ||
|
|
72a47548ce | ||
|
|
db1f90d111 | ||
|
|
9443bfeca6 | ||
|
|
2ceacf9d50 | ||
|
|
06c3048828 | ||
|
|
b47c53df72 | ||
|
|
fdd424a2c0 | ||
|
|
a710fdcb66 | ||
|
|
d9a7c635ee | ||
|
|
f2c8db9a0b | ||
|
|
346ecb2a3c | ||
|
|
51614c1b32 | ||
|
|
f69f221e10 | ||
|
|
29d0d0ef28 | ||
|
|
17e8c73b9d | ||
|
|
dbb39cd00d | ||
|
|
04f931f694 | ||
|
|
352797e932 | ||
|
|
a8a48a4ca4 | ||
|
|
016fa16d44 | ||
|
|
583c43127b | ||
|
|
c47764c670 | ||
|
|
1ecfcd1902 | ||
|
|
dceec07390 | ||
|
|
eaec9ca791 | ||
|
|
fccaaf8a91 | ||
|
|
34f6f1d736 | ||
|
|
0cb7db0599 | ||
|
|
c148d3125b | ||
|
|
c29f5a65b4 | ||
|
|
424879df8e | ||
|
|
71e5994c94 | ||
|
|
ee7167512f | ||
|
|
439b10e10d | ||
|
|
27168d938a | ||
|
|
b6569254a3 | ||
|
|
e915aeda6b | ||
|
|
9273f97a0c | ||
|
|
aca78f5ac6 | ||
|
|
2d77cfe27e | ||
|
|
ca36139c80 | ||
|
|
9b71e47e0b | ||
|
|
807820f31b | ||
|
|
67a913773b | ||
|
|
617453ba43 | ||
|
|
9c48fa715d | ||
|
|
b5a393bd48 | ||
|
|
2225941efb | ||
|
|
14f758ca97 | ||
|
|
29dbd0770c | ||
|
|
f6783dbd2e | ||
|
|
7d0120789c | ||
|
|
f097276388 | ||
|
|
6939f7941f | ||
|
|
c551f891d3 | ||
|
|
ec3acc9ec8 | ||
|
|
bab56e8947 | ||
|
|
eb5494e932 | ||
|
|
817f9197f5 | ||
|
|
f9cd169cc7 | ||
|
|
cd0f03e4b6 | ||
|
|
cabbeb2c2f | ||
|
|
bd38f26f9c | ||
|
|
7e6d511969 | ||
|
|
c8ce12c70b | ||
|
|
8174de1f78 | ||
|
|
0e844a189b | ||
|
|
b424553c0c | ||
|
|
d76a3442d8 | ||
|
|
97174a284a | ||
|
|
3a19329dbd | ||
|
|
dfb0c7f3c1 | ||
|
|
8bb20213a9 | ||
|
|
f8861a5e28 | ||
|
|
98a8c88479 | ||
|
|
2b5fe0c2bf | ||
|
|
f139400ede | ||
|
|
70ea70346b | ||
|
|
1c60a083e5 | ||
|
|
ad20bd6a70 | ||
|
|
c641ae10ed | ||
|
|
28fd75ee6b | ||
|
|
ab57b98e07 | ||
|
|
928b2f7843 | ||
|
|
f205b1023b | ||
|
|
69ecebb159 | ||
|
|
14dd871a85 | ||
|
|
5a197ca1eb | ||
|
|
c475dab00b | ||
|
|
f07ea4f7db | ||
|
|
d023eb5b71 | ||
|
|
3de7795a2c | ||
|
|
2ce49407b7 | ||
|
|
fa4baf3541 | ||
|
|
d66b12b8d4 | ||
|
|
a99d17952b | ||
|
|
484d92cd50 | ||
|
|
9b3a3716df | ||
|
|
e7d41ad710 | ||
|
|
22d1aeebaa | ||
|
|
f44c17e03b | ||
|
|
ceaef7eed2 | ||
|
|
621c79bd40 | ||
|
|
4646f53ec7 | ||
|
|
6bea9d3248 | ||
|
|
3b331b5216 | ||
|
|
c49276c477 | ||
|
|
60aef2e418 | ||
|
|
d9aa318aa4 | ||
|
|
cf607329a3 | ||
|
|
9665e610e1 | ||
|
|
89a908bd6f | ||
|
|
4d56dfe2ee | ||
|
|
a1ec5f607b | ||
|
|
08fe8e470a | ||
|
|
1c9c18b5ea | ||
|
|
f832de5f4a | ||
|
|
15acb4be37 |
@@ -60,7 +60,7 @@ jobs:
|
||||
working_directory: /tmp/workspace/core
|
||||
- save_cache: *save-cache-core
|
||||
- run:
|
||||
command: npm run build -- --max-workers 1
|
||||
command: npm run build -- --ci
|
||||
working_directory: /tmp/workspace/core
|
||||
- save_cache: *save-cache-core-stencil
|
||||
- persist_to_workspace:
|
||||
@@ -91,6 +91,23 @@ jobs:
|
||||
paths:
|
||||
- "*"
|
||||
|
||||
build-angular-server:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm install
|
||||
working_directory: /tmp/workspace/packages/angular-server
|
||||
- run:
|
||||
command: npm run build.prod
|
||||
working_directory: /tmp/workspace/packages/angular-server
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
paths:
|
||||
- "*"
|
||||
|
||||
test-core-clean-build:
|
||||
<<: *defaults
|
||||
steps:
|
||||
@@ -119,7 +136,7 @@ jobs:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm run test.spec
|
||||
command: npm run test.spec --ci
|
||||
working_directory: /tmp/workspace/core
|
||||
|
||||
test-core-treeshake:
|
||||
@@ -129,7 +146,7 @@ jobs:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm run test.treeshake
|
||||
command: npm run test.treeshake --ci
|
||||
working_directory: /tmp/workspace/core
|
||||
|
||||
test-core-screenshot:
|
||||
@@ -173,6 +190,9 @@ jobs:
|
||||
- run:
|
||||
command: npm install
|
||||
working_directory: /tmp/workspace/angular/test/test-app
|
||||
- run:
|
||||
command: npm run sync
|
||||
working_directory: /tmp/workspace/angular/test/test-app
|
||||
- run:
|
||||
command: npm test
|
||||
working_directory: /tmp/workspace/angular/test/test-app
|
||||
@@ -205,7 +225,11 @@ workflows:
|
||||
|
||||
- build-angular:
|
||||
requires: [build-core]
|
||||
- build-angular-server:
|
||||
requires: [build-angular]
|
||||
- test-angular-lint:
|
||||
requires: [build-angular]
|
||||
- test-angular-e2e:
|
||||
requires: [build-angular]
|
||||
requires:
|
||||
- build-angular
|
||||
- build-angular-server
|
||||
|
||||
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,4 +1,4 @@
|
||||
<!-- Before submitting an issue, please consult our docs (https://beta.ionicframework.com/docs/) and API reference (https://beta.ionicframework.com/docs/api/) -->
|
||||
<!-- Before submitting an issue, please consult our docs (https://ionicframework.com/docs/) and API reference (https://ionicframework.com/docs/api/) -->
|
||||
|
||||
<!-- Please make sure you are posting an issue pertaining to the Ionic Framework. If you are having an issue with the Ionic Appflow services (Ionic View, Ionic Deploy, etc.) please consult the Ionic Appflow support portal (https://ionic.zendesk.com/hc/en-us) -->
|
||||
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
3
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
title: 'bug: '
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
@@ -37,6 +37,7 @@ assignees: ''
|
||||
A sample application via GitHub
|
||||
|
||||
StackBlitz (https://stackblitz.com)
|
||||
Ionic Angular StackBlitz: https://stackblitz.com/edit/ionic-v4-angular-tabs
|
||||
|
||||
Plunker (http://plnkr.co/edit/cpeRJs?p=preview)
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
title: 'feat: '
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/support_question.md
vendored
2
.github/ISSUE_TEMPLATE/support_question.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Support Question
|
||||
about: Question on how to use this project
|
||||
title: ''
|
||||
title: 'support: '
|
||||
labels: 'ionitron: support'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
126
.github/PROCESS.md
vendored
126
.github/PROCESS.md
vendored
@@ -1,11 +1,12 @@
|
||||
# Process
|
||||
|
||||
This document is to describe the internal process that the Ionic team uses for issue management and project planning.
|
||||
This document is to describe the internal process that the Ionic team uses for issue management, project planning and the development workflow.
|
||||
|
||||
## Table of contents
|
||||
* [Project Boards](#project-boards)
|
||||
* [Managing Issues](#managing-issues)
|
||||
* [Workflow](#workflow)
|
||||
* [Releasing](#releasing)
|
||||
|
||||
## Project Boards
|
||||
|
||||
@@ -63,7 +64,7 @@ If the issue is a support question, the submitter should be redirected to our [f
|
||||
|
||||
### Incomplete Template
|
||||
|
||||
If the issue template has not been filled out completely, the issue should be closed and locked. The submitter should be informed top re-submit the issue making sure they fill the form out completely. Use the `ionitron: missing template` label to accomplish this.
|
||||
If the issue template has not been filled out completely, the issue should be closed and locked. The submitter should be informed to re-submit the issue making sure they fill the form out completely. Use the `ionitron: missing template` label to accomplish this.
|
||||
|
||||
### Issues with Open Questions
|
||||
|
||||
@@ -76,15 +77,83 @@ NOTE: be sure to perform those actions in the order stated. If you add the comme
|
||||
|
||||
If there is a response to the question, the bot will remove the `needs: reply` and apply the `triage` label. The issue will then go through the triage handling again.
|
||||
|
||||
if there is no response within 30 days, the issue will be closed and locked.
|
||||
If there is no response within 30 days, the issue will be closed and locked.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Overview
|
||||
|
||||

|
||||
|
||||
We have two long-living branches:
|
||||
|
||||
- `master`: completed features, bug fixes, refactors, chores
|
||||
- `stable`: the latest release
|
||||
|
||||
The overall flow:
|
||||
|
||||
1. Feature, refactor, and bug fix branches are created from `master`
|
||||
1. When a feature, refactor, or fix is complete it is merged into `master`
|
||||
1. A release branch is created from `master`
|
||||
1. When the release branch is done it is merged into `master` and `stable`
|
||||
1. If an issue in `stable` is detected a hotfix branch is created from `stable`
|
||||
1. Once the hotfix is complete it is merged to both `master` and `stable`
|
||||
1. All branches should follow the syntax of `{type}-{details}` where `{type}` is the type of branch (`hotfix`, `release`, or one of the [commit types](https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format)) and `{details}` is a few hyphen separated words explaining the branch
|
||||
|
||||
### Stable and Master Branches
|
||||
|
||||
#### Stable Branch
|
||||
|
||||
Branches created from `stable`:
|
||||
|
||||
The following branch should be merged back to **both** `master` and `stable`:
|
||||
|
||||
- A `hotfix` branch (e.g. `hotfix-missing-export`): a bug fix that is fixing a regression or issue with a published release
|
||||
|
||||
A `hotfix` branch should be the **only** branch that is created from stable.
|
||||
|
||||
#### Master Branch
|
||||
|
||||
Branches created from `master`:
|
||||
|
||||
The following branches should be merged back to `master` via a pull request:
|
||||
|
||||
1. A feature branch (e.g. `feat-desktop-support`): an addition to the API that is not a bug fix or regression fix
|
||||
1. A bug fix branch (e.g. `fix-tab-color`): a bug fix that is not fixing a regression or issue with a published release
|
||||
1. All other types listed in the [commit message types](https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format): `docs`, `style`, `refactor`, `perf`, `test`, `chore`
|
||||
|
||||
The following branch should be merged back to **both** `master` and `stable`:
|
||||
|
||||
1. A `release` branch (e.g. `release-4.1.x`): contains all fixes and (optionally) features that are tested and should go into the release
|
||||
|
||||
|
||||
### Feature Branches
|
||||
|
||||
Each new feature should reside in its own branch, based on the `master` branch. When a feature is complete, it should go into a pull request that gets merged back into `master`. A pull request adding a feature should be approved by two team members. Features should never interact directly with `stable`.
|
||||
|
||||
|
||||
### Release Branches
|
||||
|
||||
Once `master` has acquired enough features for a release (or a predetermined release date is approaching), fork a release branch off of `master`. Creating this branch starts the next release cycle, so no new features can be added after this point - only bug fixes, documentation generation, and other release-oriented tasks should go in this branch.
|
||||
|
||||
Once the release is ready to ship, it will get merged into `stable` and `master`, then the release branch will be deleted. It’s important to merge back into `master` because critical updates may have been added to the release branch and they need to be accessible to new features. This should be done in a pull request after review.
|
||||
|
||||
See the [steps for releasing](#releasing) below for detailed information on how to publish a release.
|
||||
|
||||
### Version Branches
|
||||
|
||||
Once a release has shipped and the release branch has been merged into `stable` and `master` it should also be merged into its corrsponding version branch. These version branches allow us to ship updates for specific versions of the framework (i.e. Lets us ship a bug fix that only affects 4.2.x).
|
||||
|
||||
Patch releases should be merged into their corresponding version branches. For example, a `release-4.1.1` branch should be merged into the `4.1.x` version branch and a `release-5.0.1` branch should be merged into the `5.0.x` version branch.
|
||||
|
||||
When releasing a major version such as `5.0.0 ` or a minor version such as `4.1.0` , the version branch will not exist. The version branch should be created once the release branch has been merged into `stable` and `master`. For example, when releasing `4.1.0`, the `release-4.1.0` release branch should be merged into `stable` and `master` and then the `4.1.x` version branch should be created off the latest `stable`.
|
||||
|
||||
|
||||
### Hotfix Branches
|
||||
|
||||
Maintenance or “hotfix” branches are used to quickly patch production releases. This is the only branch that should fork directly off of `stable`. As soon as the fix is complete, it should be merged into both `stable` and `master` (or the current release branch).
|
||||
|
||||
|
||||
### Examples
|
||||
|
||||
#### Making a Change
|
||||
@@ -111,7 +180,7 @@ We have two long-living branches:
|
||||
|
||||
1. Confirm squash and merge into `master`.
|
||||
|
||||
#### Merging Changes from `master` into your Branch
|
||||
#### Updating from `master`
|
||||
|
||||
1. Pull the latest changes locally.
|
||||
1. Merge the changes, fixing any conflicts.
|
||||
@@ -125,23 +194,6 @@ OR
|
||||
|
||||
1. Pull the merged changes locally.
|
||||
|
||||
#### Making a Release
|
||||
|
||||
1. Freeze `master`. Only the person doing the release should be modifying `master`.
|
||||
1. Follow the [Making a Change](#making-a-change) steps to prepare the release.
|
||||
|
||||
- Run `npm run release.prepare`
|
||||
- Version changes
|
||||
- `CHANGELOG.MD` tweaks
|
||||
|
||||
1. Create a PR to merge `master` into `stable`.
|
||||
1. Click **Merge pull request**. Use the dropdown to select this option if necessary. This will preserve the commit history from `master` by creating a merge commit.
|
||||
|
||||
<img width="191" alt="Merge pull request button" src="https://user-images.githubusercontent.com/236501/47032669-8be1b980-d138-11e8-9a90-d1518c223184.png">
|
||||
|
||||
1. CI builds `stable`, performing the release.
|
||||
1. Unfreeze `master`.
|
||||
|
||||
#### Hotfixes
|
||||
|
||||
Hotfixes bypass `master` and should only be used for urgent fixes that can't wait for the next release to be ready.
|
||||
@@ -165,3 +217,35 @@ Hotfixes bypass `master` and should only be used for urgent fixes that can't wai
|
||||
1. Click **Merge pull request**. Use the dropdown to select this option if necessary.
|
||||
|
||||
<img width="191" alt="Merge pull request button" src="https://user-images.githubusercontent.com/236501/47032669-8be1b980-d138-11e8-9a90-d1518c223184.png">
|
||||
|
||||
|
||||
## Releasing
|
||||
|
||||
1. Create the release branch from `master`, for example: `release-4.5.0`.
|
||||
|
||||
1. For major or minor releases, create a version branch based off the latest version branch. For example, if releasing 4.5.0, create a branch called `4.5.x` based off `4.4.x`.
|
||||
|
||||
1. Submit a pull request from the release branch into the version branch. Do not merge this pull request yet.
|
||||
|
||||
1. Verify all tests are passing, fix any bugs if needed and make sure no undesired commits are in.
|
||||
|
||||
1. Navigate to the root of the repository while on the release branch.
|
||||
|
||||
1. Run `npm i` if it hasn't already been done.
|
||||
|
||||
1. Run `npm run release.prepare`
|
||||
- Select the version based on the type of commits and the [Ionic Versioning](https://ionicframework.com/docs/intro/versioning)
|
||||
- After the process completes, verify the version number in all packages (`core`, `docs`, `angular`)
|
||||
- Verify the changelog commits are accurate and follow the [proper format]((https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format))
|
||||
- For major or minor releases, ensure that the version number has an associated title (for example: `4.5.0 Boron`)
|
||||
- Commit these changes with the version number as the message, e.g. `git commit -m "4.5.0"`
|
||||
|
||||
1. Run `npm run release`
|
||||
|
||||
1. Click **Merge pull request**. Use the dropdown to select this option if necessary.
|
||||
|
||||
<img width="191" alt="Merge pull request button" src="https://user-images.githubusercontent.com/236501/47032669-8be1b980-d138-11e8-9a90-d1518c223184.png">
|
||||
|
||||
1. Rewrite the commit message to `merge release-[VERSION]` with the proper release branch. For example, if this release is for `4.5.0`, the message would be `merge release-4.5.0`.
|
||||
|
||||
1. Submit a pull request from the release branch into `master`. Merge this pull request using the same commit format in the last step, to ensure any changes made on the release branch get added to future releases.
|
||||
|
||||
47
.github/PULL_REQUEST_TEMPLATE.md
vendored
47
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,12 +1,51 @@
|
||||
#### Short description of what this resolves:
|
||||
<!-- Please refer to our contributing documentation for any questions on submitting a pull request, or let us know here if you need any help: https://ionicframework.com/docs/building/contributing -->
|
||||
|
||||
## Pull request checklist
|
||||
|
||||
Please check if your PR fulfills the following requirements:
|
||||
- [ ] Tests for the changes have been added (for bug fixes / features)
|
||||
- [ ] Docs have been reviewed and added / updated if needed (for bug fixes / features)
|
||||
- [ ] Build (`npm run build`) was run locally and any changes were pushed
|
||||
- [ ] Lint (`npm run lint`) has passed locally and any fixes were made for failures
|
||||
|
||||
|
||||
#### Changes proposed in this pull request:
|
||||
## Pull request type
|
||||
|
||||
<!-- Please do not submit updates to dependencies unless it fixes an issue. -->
|
||||
|
||||
<!-- Please try to limit your pull request to one type, submit multiple pull requests if needed. -->
|
||||
|
||||
Please check the type of change your PR introduces:
|
||||
- [ ] Bugfix
|
||||
- [ ] Feature
|
||||
- [ ] Code style update (formatting, renaming)
|
||||
- [ ] Refactoring (no functional changes, no api changes)
|
||||
- [ ] Build related changes
|
||||
- [ ] Documentation content changes
|
||||
- [ ] Other (please describe):
|
||||
|
||||
|
||||
## What is the current behavior?
|
||||
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
|
||||
|
||||
Issue Number: N/A
|
||||
|
||||
|
||||
## What is the new behavior?
|
||||
<!-- Please describe the behavior or changes that are being added by this PR. -->
|
||||
|
||||
-
|
||||
-
|
||||
-
|
||||
|
||||
**Ionic Version**:
|
||||
## Does this introduce a breaking change?
|
||||
|
||||
**Fixes**: #
|
||||
- [ ] Yes
|
||||
- [ ] 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. -->
|
||||
|
||||
29
.github/ionic-issue-bot.yml
vendored
29
.github/ionic-issue-bot.yml
vendored
@@ -3,6 +3,26 @@ triage:
|
||||
removeLabelWhenProjectAssigned: true
|
||||
dryRun: false
|
||||
|
||||
comment:
|
||||
labels:
|
||||
- label: "help wanted"
|
||||
message: >
|
||||
This issue has been labeled as `help wanted`. This label is added to issues
|
||||
that we believe would be good for contributors.
|
||||
|
||||
|
||||
If you'd like to work on this issue, please comment here letting us know that
|
||||
you would like to submit a pull request for it. This helps us to keep track of
|
||||
the pull request and make sure there isn't duplicated effort.
|
||||
|
||||
|
||||
For a guide on how to create a pull request and test this project locally to see
|
||||
your changes, see our [contributing documentation](https://ionicframework.com/docs/building/contributing).
|
||||
|
||||
|
||||
Thank you!
|
||||
dryRun: false
|
||||
|
||||
closeAndLock:
|
||||
labels:
|
||||
- label: "ionitron: support"
|
||||
@@ -98,6 +118,15 @@ labelPullRequest:
|
||||
|
||||
wrongRepo:
|
||||
repos:
|
||||
- label: "ionitron: capacitor"
|
||||
repo: capacitor
|
||||
message: >
|
||||
Thanks for the issue! We use this issue tracker exclusively for bug reports and feature requests
|
||||
associated with the Ionic Framework. It appears that this issue is associated with Capacitor.
|
||||
I am moving this issue to the Capacitor repository. Please track this issue over there.
|
||||
|
||||
|
||||
Thank you for using Ionic!
|
||||
- label: "ionitron: v3"
|
||||
repo: ionic-v3
|
||||
message: >
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -15,6 +15,7 @@ log.txt
|
||||
coverage/
|
||||
collection/
|
||||
dist/
|
||||
dist-transpiled/
|
||||
node_modules/
|
||||
tmp/
|
||||
temp/
|
||||
@@ -49,10 +50,16 @@ demos/src/**/*.ngfactory.ts
|
||||
demos/src/**/*.d.ts
|
||||
demos/src/**/*.metadata.json
|
||||
demos/src/**/*.css.shim.ts
|
||||
prerender.html
|
||||
prerender-domino.html
|
||||
prerender-hydrated.html
|
||||
prerender-static.html
|
||||
|
||||
# stencil
|
||||
angular/css/
|
||||
core/css/
|
||||
core/hydrate/
|
||||
core/loader/
|
||||
core/www/
|
||||
.stencil/
|
||||
angular/build/
|
||||
|
||||
@@ -37,9 +37,9 @@ function checkGit(tasks) {
|
||||
{
|
||||
title: 'Check current branch',
|
||||
task: () => execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']).then(branch => {
|
||||
// if (branch !== 'master') {
|
||||
// throw new Error(`Not on "master" branch`);
|
||||
// }
|
||||
if (branch.indexOf('release') === -1 && branch.indexOf('hotfix') === -1) {
|
||||
throw new Error(`Must be on a "release" or "hotfix" branch.`);
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -258,11 +258,18 @@ function isVersionGreater(oldVersion, newVersion) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function copyCDNLoader(tasks, version) {
|
||||
tasks.push({
|
||||
title: `Copy CDN loader`,
|
||||
task: () => execa('node', ['copy-cdn-loader.js', version], { cwd: path.join(rootDir, 'core', 'scripts') }),
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
checkGit,
|
||||
isValidVersion,
|
||||
isVersionGreater,
|
||||
copyCDNLoader,
|
||||
packages,
|
||||
packagePath,
|
||||
prepareDevPackage,
|
||||
|
||||
@@ -116,6 +116,7 @@ async function preparePackages(packages, version, install) {
|
||||
|
||||
// update core readme with version number
|
||||
updateCoreReadme(tasks, version);
|
||||
common.copyCDNLoader(tasks, version);
|
||||
|
||||
const listr = new Listr(tasks, { showSubtasks: true });
|
||||
await listr.run();
|
||||
@@ -171,7 +172,6 @@ function updateCoreReadme(tasks, version) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const SEMVER_INCREMENTS = ['patch', 'minor', 'major'];
|
||||
|
||||
const isValidVersionInput = input => SEMVER_INCREMENTS.indexOf(input) !== -1 || common.isValidVersion(input);
|
||||
|
||||
@@ -7,6 +7,7 @@ const fs = require('fs-extra');
|
||||
|
||||
const common = require('./common');
|
||||
|
||||
const DIST_NPM_TAG = 'dev';
|
||||
const DIST_TAG = 'dev';
|
||||
|
||||
async function main() {
|
||||
@@ -37,7 +38,8 @@ async function main() {
|
||||
packages.forEach(package => {
|
||||
common.prepareDevPackage(tasks, package, devVersion);
|
||||
});
|
||||
common.publishPackages(tasks, packages, devVersion, DIST_TAG);
|
||||
common.copyCDNLoader(tasks, devVersion);
|
||||
common.publishPackages(tasks, packages, devVersion, DIST_NPM_TAG);
|
||||
|
||||
const listr = new Listr(tasks);
|
||||
await listr.run();
|
||||
|
||||
@@ -54,7 +54,7 @@ function publishGit(tasks, version, changelog) {
|
||||
},
|
||||
{
|
||||
title: 'Push tags to remove',
|
||||
task: () => execa('git', ['push', '--tags'], { cwd: common.rootDir })
|
||||
task: () => execa('git', ['push', '--follow-tags'], { cwd: common.rootDir })
|
||||
},
|
||||
{
|
||||
title: 'Publish Github release',
|
||||
|
||||
393
CHANGELOG.md
393
CHANGELOG.md
@@ -1,3 +1,385 @@
|
||||
## [4.7.2](https://github.com/ionic-team/ionic/compare/v4.7.1...v4.7.2) (2019-08-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** hardware back button subscribeWithPriority triggers change detection ([#18962](https://github.com/ionic-team/ionic/issues/18962)) ([3a22105](https://github.com/ionic-team/ionic/commit/3a22105)), closes [#18959](https://github.com/ionic-team/ionic/issues/18959)
|
||||
* **angular:** nested inputs no longer conflict with each other ([#18976](https://github.com/ionic-team/ionic/issues/18976)) ([6bbdb80](https://github.com/ionic-team/ionic/commit/6bbdb80)), closes [#18248](https://github.com/ionic-team/ionic/issues/18248)
|
||||
* **range:** ion-range value now submitted with form ([#19008](https://github.com/ionic-team/ionic/issues/19008)) ([8f7853c](https://github.com/ionic-team/ionic/commit/8f7853c))
|
||||
* **reorder:** only move item if reorder happens ([#19007](https://github.com/ionic-team/ionic/issues/19007)) ([d237e80](https://github.com/ionic-team/ionic/commit/d237e80))
|
||||
* **router:** partial swipe to go back gesture no longer breaks view([#18977](https://github.com/ionic-team/ionic/issues/18977)) ([713ea8a](https://github.com/ionic-team/ionic/commit/713ea8a)), closes [#18462](https://github.com/ionic-team/ionic/issues/18462)
|
||||
* **toast:** allow loading ion-icon from asset path ([#18969](https://github.com/ionic-team/ionic/issues/18969)) ([23f327e](https://github.com/ionic-team/ionic/commit/23f327e))
|
||||
* **vue:** rename swipeEnable to swipeGesture ([#17346](https://github.com/ionic-team/ionic/issues/17346)) ([c2348f7](https://github.com/ionic-team/ionic/commit/c2348f7)), closes [#16002](https://github.com/ionic-team/ionic/issues/16002)
|
||||
|
||||
|
||||
|
||||
## [4.7.1](https://github.com/ionic-team/ionic/compare/v4.7.0...v4.7.1) (2019-07-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** ensure change detection fires properly ([#18896](https://github.com/ionic-team/ionic/issues/18896)) ([962783b](https://github.com/ionic-team/ionic/commit/962783b)), closes [#18894](https://github.com/ionic-team/ionic/issues/18894)
|
||||
|
||||
|
||||
|
||||
# [4.7.0 Nitrogen](https://github.com/ionic-team/ionic/compare/v4.6.2...v4.7.0) (2019-07-24)
|
||||
|
||||
|
||||
### Angular 8 Support
|
||||
|
||||
With this version comes support for Angular 8! Follow the below steps to update.
|
||||
|
||||
1. Update `@ionic/angular` and `@ionic/angular-toolkit` to the latest releases:
|
||||
|
||||
```shell
|
||||
$ npm install @ionic/angular@4.7.0
|
||||
$ npm install @ionic/angular-toolkit@2.0.0 -D
|
||||
```
|
||||
|
||||
1. Update `@angular/core` and `@angular/cli`:
|
||||
|
||||
```shell
|
||||
$ npx ng update @angular/core @angular/cli
|
||||
```
|
||||
|
||||
1. Update `@angular-devkit` dependencies:
|
||||
|
||||
```shell
|
||||
$ npm i @angular-devkit/architect@latest @angular-devkit/build-angular@latest @angular-devkit/core@latest @angular-devkit/schematics@latest
|
||||
```
|
||||
|
||||
View our [Angular 8 Update Guide](https://docs.google.com/document/d/1QOpQeDifPSg6F9WycDLcbQnpqjN96ew-Ap0_CB7CcCQ/edit?usp=sharing) for tips on potential issues!
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** copy input form classes to parent ion-item ([#18820](https://github.com/ionic-team/ionic/issues/18820)) ([53179c4](https://github.com/ionic-team/ionic/commit/53179c4)), closes [#18800](https://github.com/ionic-team/ionic/issues/18800)
|
||||
* **angular:** add the swipeGesture method for enabling or disabling the ability to swipe open a menu ([#18806](https://github.com/ionic-team/ionic/issues/18806)) ([fbfc076](https://github.com/ionic-team/ionic/commit/fbfc076)), closes [#16002](https://github.com/ionic-team/ionic/issues/16002)
|
||||
* **angular:** webview "pause", "resume", and "resize" events now trigger change detection ([#18853](https://github.com/ionic-team/ionic/issues/18853)) ([544e550](https://github.com/ionic-team/ionic/commit/544e550)), closes [#18831](https://github.com/ionic-team/ionic/issues/18831)
|
||||
* **core:** apply translucent if backdrop-filter is supported ([#18832](https://github.com/ionic-team/ionic/issues/18832)) ([6b5a59d](https://github.com/ionic-team/ionic/commit/6b5a59d)), closes [ionic-team/ionic-docs#666](https://github.com/ionic-team/ionic-docs/issues/666)
|
||||
* **datetime:** allow AM/PM to be changed ([#18684](https://github.com/ionic-team/ionic/issues/18684)) ([b7761fe](https://github.com/ionic-team/ionic/commit/b7761fe)), closes [#18585](https://github.com/ionic-team/ionic/issues/18585)
|
||||
* **datetime:** properly apply disabled classes when updating columns ([#18875](https://github.com/ionic-team/ionic/issues/18875)) ([7ba718c](https://github.com/ionic-team/ionic/commit/7ba718c)), closes [#18793](https://github.com/ionic-team/ionic/issues/18793)
|
||||
* **hardware-back-button:** hardware back button no longer erroneously restarts app ([#18794](https://github.com/ionic-team/ionic/issues/18794)) ([978cc39](https://github.com/ionic-team/ionic/commit/978cc39)), closes [#18792](https://github.com/ionic-team/ionic/issues/18792)
|
||||
* **ripple-effect:** ensure ripple is removed from components after pointer release ([#18854](https://github.com/ionic-team/ionic/issues/18854)) ([71137a2](https://github.com/ionic-team/ionic/commit/71137a2)), closes [#18836](https://github.com/ionic-team/ionic/issues/18836)
|
||||
* **searchbar:** add aria and role for improved accessibility ([#18797](https://github.com/ionic-team/ionic/issues/18797)) ([798103b](https://github.com/ionic-team/ionic/commit/798103b)), closes [#18796](https://github.com/ionic-team/ionic/issues/18796)
|
||||
* **ssr:** avoid window reference ([#18865](https://github.com/ionic-team/ionic/issues/18865)) ([23ce6fa](https://github.com/ionic-team/ionic/commit/23ce6fa))
|
||||
* **ssr:** check for client runtime method ([#18866](https://github.com/ionic-team/ionic/issues/18866)) ([c52b3b4](https://github.com/ionic-team/ionic/commit/c52b3b4))
|
||||
* **textarea:** autogrow now resets textarea back to original number of rows when text is cleared ([#18822](https://github.com/ionic-team/ionic/issues/18822)) ([26e6d6f](https://github.com/ionic-team/ionic/commit/26e6d6f)), closes [#18744](https://github.com/ionic-team/ionic/issues/18744)
|
||||
* **theming:** update components to use the proper colors for dark themes ([#18735](https://github.com/ionic-team/ionic/issues/18735)) ([045bc59](https://github.com/ionic-team/ionic/commit/045bc59)), closes [#18713](https://github.com/ionic-team/ionic/issues/18713)
|
||||
* **virtual-scroll:** card rendering is no longer distorted ([#18877](https://github.com/ionic-team/ionic/issues/18877)) ([3ef6ecf](https://github.com/ionic-team/ionic/commit/3ef6ecf)), closes [#18870](https://github.com/ionic-team/ionic/issues/18870)
|
||||
* **virtual-scroll:** element dimensions are recalculated on resize ([#18878](https://github.com/ionic-team/ionic/issues/18878)) ([c91819c](https://github.com/ionic-team/ionic/commit/c91819c))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** add support for Stackblitz ([#18846](https://github.com/ionic-team/ionic/issues/18846)) ([fb18f3b](https://github.com/ionic-team/ionic/commit/fb18f3b))
|
||||
* **ssr:** add @ionic/core/hydrate app ([#18867](https://github.com/ionic-team/ionic/issues/18867)) ([815fa2e](https://github.com/ionic-team/ionic/commit/815fa2e))
|
||||
* **navigation:** add experimental shadow to iOS page transitions ([#18695](https://github.com/ionic-team/ionic/issues/18695)) ([9b075ef](https://github.com/ionic-team/ionic/commit/9b075ef)), closes [#18661](https://github.com/ionic-team/ionic/issues/18661)
|
||||
* **virtual-scroll:** adds headerHeight and footerHeight to help prevent flickering ([#18851](https://github.com/ionic-team/ionic/issues/18851)) ([0089111](https://github.com/ionic-team/ionic/commit/0089111)), closes [#17540](https://github.com/ionic-team/ionic/issues/17540)
|
||||
|
||||
|
||||
### Performance
|
||||
|
||||
* **angular:** attach entering view before first change detection and detach leaving page ([#18821](https://github.com/ionic-team/ionic/issues/18821)) ([97fec92](https://github.com/ionic-team/ionic/commit/97fec92))
|
||||
|
||||
|
||||
## [4.6.2](https://github.com/ionic-team/ionic/compare/v4.6.1...v4.6.2) (2019-07-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **menu-button:** hide menu button when auto hide or split pane ([#18702](https://github.com/ionic-team/ionic/issues/18702)) ([24840d4](https://github.com/ionic-team/ionic/commit/24840d4)), closes [#18666](https://github.com/ionic-team/ionic/issues/18666)
|
||||
* **menu-button:** move font-size to host for easier customization ([#18699](https://github.com/ionic-team/ionic/issues/18699)) ([876ab41](https://github.com/ionic-team/ionic/commit/876ab41)), closes [#18667](https://github.com/ionic-team/ionic/issues/18667)
|
||||
* **overlays:** fallback to step color if overlay background variable is unset ([#18709](https://github.com/ionic-team/ionic/issues/18709)) ([f16b118](https://github.com/ionic-team/ionic/commit/f16b118)), closes [#18658](https://github.com/ionic-team/ionic/issues/18658)
|
||||
* **virtual-scroll:** remove runOutsideAngular error ([#18752](https://github.com/ionic-team/ionic/issues/18752)) ([8beeff2](https://github.com/ionic-team/ionic/commit/8beeff2)), closes [#18746](https://github.com/ionic-team/ionic/issues/18746)
|
||||
* **vue:** update imports for types and ionicons ([f56fea6](https://github.com/ionic-team/ionic/commit/f56fea6)), closes [#18701](https://github.com/ionic-team/ionic/issues/18701)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **all:** minify better by using arrow functions ([#18730](https://github.com/ionic-team/ionic/issues/18730)) ([03c1d19](https://github.com/ionic-team/ionic/commit/03c1d19))
|
||||
|
||||
|
||||
|
||||
## [4.6.1](https://github.com/ionic-team/ionic/compare/v4.6.0...v4.6.1) (2019-07-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **app:** add hydrated to hide white screen with multiple ionic dependencies ([#18649](https://github.com/ionic-team/ionic/issues/18649))
|
||||
* **datetime:** datetime no longer reports having a value if none is set ([#18541](https://github.com/ionic-team/ionic/issues/18541)) ([92e0f98](https://github.com/ionic-team/ionic/commit/92e0f98)), closes [#17979](https://github.com/ionic-team/ionic/issues/17979) [#18540](https://github.com/ionic-team/ionic/issues/18540)
|
||||
* **fab-button:** set opacity on disabled fab button ([#18685](https://github.com/ionic-team/ionic/issues/18685)) ([6042b39](https://github.com/ionic-team/ionic/commit/6042b39)), closes [#18682](https://github.com/ionic-team/ionic/issues/18682)
|
||||
* **icon:** load icons properly with baseHref ([#18650](https://github.com/ionic-team/ionic/issues/18650)), ([#18637](https://github.com/ionic-team/ionic/issues/18637))
|
||||
* **icon:** bind icon name properly ([#18707](https://github.com/ionic-team/ionic/issues/18707))
|
||||
* **infinite-scroll:** fix scroll listener ([0d58101](https://github.com/ionic-team/ionic/commit/0d58101))
|
||||
* **item:** do not disable entire item if there are multiple inputs ([#18696](https://github.com/ionic-team/ionic/issues/18696)) ([dfa2b13](https://github.com/ionic-team/ionic/commit/dfa2b13)), closes [#18655](https://github.com/ionic-team/ionic/issues/18655) [#18670](https://github.com/ionic-team/ionic/issues/18670)
|
||||
* **router-link:** add missing target prop ([#18659](https://github.com/ionic-team/ionic/issues/18659)) ([1f51ab2](https://github.com/ionic-team/ionic/commit/1f51ab2)), closes [#18655](https://github.com/ionic-team/ionic/issues/18655)
|
||||
* **router-outlet:** fix swipe to go back ([b69fb69](https://github.com/ionic-team/ionic/commit/b69fb69))
|
||||
* **scss:** copy all scss files ([36a58df](https://github.com/ionic-team/ionic/commit/36a58df))
|
||||
* **searchbar:** proper styling after navigating ([#18642](https://github.com/ionic-team/ionic/issues/18642))
|
||||
* **slides:** use correct order for pushing slides dynamically ([#18633](https://github.com/ionic-team/ionic/issues/18633))
|
||||
* **tabs:** select proper tab by default and do not emit tab change if selectedTab is undefined ([03c834c](https://github.com/ionic-team/ionic/commit/03c834c))
|
||||
* **overlay:** make create opts optional ([44c88ad](https://github.com/ionic-team/ionic/commit/44c88ad))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **angular:** skip zone ([e059fc8](https://github.com/ionic-team/ionic/commit/e059fc8))
|
||||
|
||||
|
||||
|
||||
# [4.6.0 Carbon](https://github.com/ionic-team/ionic/compare/v4.5.0...v4.6.0) (2019-06-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **button:** default opacity for disabled clear buttons ([#18560](https://github.com/ionic-team/ionic/issues/18560)) ([f48dc3d](https://github.com/ionic-team/ionic/commit/f48dc3d)), closes [#18555](https://github.com/ionic-team/ionic/issues/18555)
|
||||
* **button:** update solid buttons to use tint and shade colors ([#18537](https://github.com/ionic-team/ionic/issues/18537)) ([26ecf2b](https://github.com/ionic-team/ionic/commit/26ecf2b))
|
||||
* **menu:** change ARIA role from complementary to navigation ([#18330](https://github.com/ionic-team/ionic/issues/18330)) ([9e4346b](https://github.com/ionic-team/ionic/commit/9e4346b)), closes [#18318](https://github.com/ionic-team/ionic/issues/18318)
|
||||
* **segment:** apply hover properly for segment with color ([#18549](https://github.com/ionic-team/ionic/issues/18549)) ([78e477b](https://github.com/ionic-team/ionic/commit/78e477b))
|
||||
* **segment:** default ripple to currentColor ([#18547](https://github.com/ionic-team/ionic/issues/18547)) ([832306c](https://github.com/ionic-team/ionic/commit/832306c))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **components:** add missing button/a props to components that render them ([#17883](https://github.com/ionic-team/ionic/issues/17883)) ([eca4121](https://github.com/ionic-team/ionic/commit/eca4121)), closes [#16848](https://github.com/ionic-team/ionic/issues/16848) [#16889](https://github.com/ionic-team/ionic/issues/16889)
|
||||
* **fab-button:** add hover state using tint colors ([#18536](https://github.com/ionic-team/ionic/issues/18536)) ([ad00679](https://github.com/ionic-team/ionic/commit/ad00679)), closes [#17624](https://github.com/ionic-team/ionic/issues/17624)
|
||||
* **item:** add hover and focused states ([#18606](https://github.com/ionic-team/ionic/issues/18606)) ([8a88dd2](https://github.com/ionic-team/ionic/commit/8a88dd2)), closes [#18279](https://github.com/ionic-team/ionic/issues/18279) [#17624](https://github.com/ionic-team/ionic/issues/17624)
|
||||
* **router-link:** add router-link and deprecate anchor ([#18620](https://github.com/ionic-team/ionic/issues/18620)) ([d4c7b03](https://github.com/ionic-team/ionic/commit/d4c7b03))
|
||||
|
||||
|
||||
### Enhancements
|
||||
|
||||
* **stencil:** update to Stencil One to improve app performance 🎉🎊 ([b40f7d3](https://github.com/ionic-team/ionic/commit/b40f7d3))
|
||||
|
||||
|
||||
|
||||
# [4.5.0 Boron](https://github.com/ionic-team/ionic/compare/v4.4.2...v4.5.0) (2019-06-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** ensure all NavigationExtras values are preserved when navigating ([#18468](https://github.com/ionic-team/ionic/issues/18468)) ([7610787](https://github.com/ionic-team/ionic/commit/7610787)), closes [#18469](https://github.com/ionic-team/ionic/issues/18469)
|
||||
* **button:** set opacity on the host element for disabled button ([#18509](https://github.com/ionic-team/ionic/issues/18509)) ([320719b](https://github.com/ionic-team/ionic/commit/320719b)), closes [#16965](https://github.com/ionic-team/ionic/issues/16965)
|
||||
* **button:** use correct border-radius on menu and back button ([#18501](https://github.com/ionic-team/ionic/issues/18501)) ([055e125](https://github.com/ionic-team/ionic/commit/055e125)), closes [#17624](https://github.com/ionic-team/ionic/issues/17624)
|
||||
* **button:** use correct size on a dynamic button in an item ([#18395](https://github.com/ionic-team/ionic/issues/18395)) ([a3e23fc](https://github.com/ionic-team/ionic/commit/a3e23fc)), closes [#18085](https://github.com/ionic-team/ionic/issues/18085)
|
||||
* **card:** remove white space from bottom of card ([#18328](https://github.com/ionic-team/ionic/issues/18328)) ([d53e7aa](https://github.com/ionic-team/ionic/commit/d53e7aa))
|
||||
* **content:** prevent ion-searchbar from receiving padding adjustment when keyboard is open ([#18008](https://github.com/ionic-team/ionic/issues/18008)) ([b2290a6](https://github.com/ionic-team/ionic/commit/b2290a6)), closes [#18007](https://github.com/ionic-team/ionic/issues/18007)
|
||||
* **datetime:** recalculate time columns on change ([#18380](https://github.com/ionic-team/ionic/issues/18380)) ([292b24a](https://github.com/ionic-team/ionic/commit/292b24a))
|
||||
* **item:** start align the content under stacked and floating labels ([#18379](https://github.com/ionic-team/ionic/issues/18379)) ([f0af707](https://github.com/ionic-team/ionic/commit/f0af707)), closes [#16375](https://github.com/ionic-team/ionic/issues/16375)
|
||||
* **item:** inherit overflow to allow better customization ([#18502](https://github.com/ionic-team/ionic/issues/18502)) ([8d2a47e](https://github.com/ionic-team/ionic/commit/8d2a47e)), closes [#17670](https://github.com/ionic-team/ionic/issues/17670)
|
||||
* **item:** use a step color if the activated background is not set ([#18450](https://github.com/ionic-team/ionic/issues/18450)) ([1899c13](https://github.com/ionic-team/ionic/commit/1899c13)), closes [#18449](https://github.com/ionic-team/ionic/issues/18449)
|
||||
* **item-sliding:** use the correct gesture direction and side for rtl ([#18366](https://github.com/ionic-team/ionic/issues/18366)) ([4545100](https://github.com/ionic-team/ionic/commit/4545100)), closes [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **label:** include the ion-text-wrap class styles for larger font ([#18374](https://github.com/ionic-team/ionic/issues/18374)) ([4bba540](https://github.com/ionic-team/ionic/commit/4bba540))
|
||||
* **platform:** prevent error with Platform.is on Android 4.4 ([#18387](https://github.com/ionic-team/ionic/issues/18387)) ([54bdb36](https://github.com/ionic-team/ionic/commit/54bdb36))
|
||||
* **react:** ensure element exists in controller before dismissing it ([0fd3e5d](https://github.com/ionic-team/ionic/commit/0fd3e5d))
|
||||
* **slides:** resolve issue where double tap to zoom was enabled by default ([10de1da](https://github.com/ionic-team/ionic/commit/10de1da)), closes [#18035](https://github.com/ionic-team/ionic/issues/18035)
|
||||
* **tabs:** allow selection on enter and spacebar press ([#18381](https://github.com/ionic-team/ionic/issues/18381)) ([11cde99](https://github.com/ionic-team/ionic/commit/11cde99)), closes [#18363](https://github.com/ionic-team/ionic/issues/18363)
|
||||
* **textarea:** inherit white-space for better customization ([#18508](https://github.com/ionic-team/ionic/issues/18508)) ([a583902](https://github.com/ionic-team/ionic/commit/a583902)), closes [#18495](https://github.com/ionic-team/ionic/issues/18495)
|
||||
* **virtual-scroll:** do not crash with an empty cell list ([#17799](https://github.com/ionic-team/ionic/issues/17799)) ([20c146e](https://github.com/ionic-team/ionic/commit/20c146e))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **back-button:** add variables and support for focused and hover states ([#18451](https://github.com/ionic-team/ionic/issues/18451)) ([58672fb](https://github.com/ionic-team/ionic/commit/58672fb)), closes [#18465](https://github.com/ionic-team/ionic/issues/18465)
|
||||
* **button:** add variables for customizing hover state ([#18499](https://github.com/ionic-team/ionic/issues/18499)) ([5c5934b](https://github.com/ionic-team/ionic/commit/5c5934b)), closes [#17974](https://github.com/ionic-team/ionic/issues/17974)
|
||||
* **item-divider:** add inner padding CSS variables ([#18490](https://github.com/ionic-team/ionic/issues/18490)) ([35c143a](https://github.com/ionic-team/ionic/commit/35c143a)), closes [#18484](https://github.com/ionic-team/ionic/issues/18484)
|
||||
* **menu-button:** add variables for hover and focused states ([#18434](https://github.com/ionic-team/ionic/issues/18434)) ([5ba0aa9](https://github.com/ionic-team/ionic/commit/5ba0aa9)), closes [#18279](https://github.com/ionic-team/ionic/issues/18279)
|
||||
* **searchbar:** add cancel button options ([b959e0b](https://github.com/ionic-team/ionic/commit/b959e0b))
|
||||
* **toast:** allow html content ([#18423](https://github.com/ionic-team/ionic/issues/18423)) ([c8104a2](https://github.com/ionic-team/ionic/commit/c8104a2))
|
||||
|
||||
|
||||
|
||||
## [4.4.2](https://github.com/ionic-team/ionic/compare/v4.4.1...v4.4.2) (2019-05-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** account for query params and fragments within a string when navigating ([#18356](https://github.com/ionic-team/ionic/issues/18356)) ([b79f68a](https://github.com/ionic-team/ionic/commit/b79f68a))
|
||||
|
||||
|
||||
|
||||
## [4.4.1](https://github.com/ionic-team/ionic/compare/v4.4.0...v4.4.1) (2019-05-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** ensure active page is not removed from change detection ([#18299](https://github.com/ionic-team/ionic/issues/18299)) ([b8d4961](https://github.com/ionic-team/ionic/commit/b8d4961)), closes [#18293](https://github.com/ionic-team/ionic/issues/18293)
|
||||
* **angular:** preserve queryParams and fragment when going back ([#18298](https://github.com/ionic-team/ionic/issues/18298)) ([bdd5109](https://github.com/ionic-team/ionic/commit/bdd5109)), closes [#16744](https://github.com/ionic-team/ionic/issues/16744)
|
||||
* **buttons:** use theme/color toolbar colors for buttons ([#18191](https://github.com/ionic-team/ionic/issues/18191)) ([0511989](https://github.com/ionic-team/ionic/commit/0511989)), closes [#18184](https://github.com/ionic-team/ionic/issues/18184) [#17840](https://github.com/ionic-team/ionic/issues/17840)
|
||||
* **datetime:** update label direction in RTL ([#18340](https://github.com/ionic-team/ionic/issues/18340)) ([17345ef](https://github.com/ionic-team/ionic/commit/17345ef))
|
||||
* **fab:** position fab buttons properly in RTL ([#18325](https://github.com/ionic-team/ionic/issues/18325)) ([845def8](https://github.com/ionic-team/ionic/commit/845def8)), references [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **icon:** remove stroke and move fill to host element ([#18241](https://github.com/ionic-team/ionic/issues/18241)) ([394cf8d](https://github.com/ionic-team/ionic/commit/394cf8d)), closes [#16483](https://github.com/ionic-team/ionic/issues/16483)
|
||||
* **input:** keep entire input in view when scrolling with keyboard open ([#18253](https://github.com/ionic-team/ionic/issues/18253)) ([3cad778](https://github.com/ionic-team/ionic/commit/3cad778)), closes [#17457](https://github.com/ionic-team/ionic/issues/17457)
|
||||
* **label:** position floating/stacked labels properly in RTL ([#18315](https://github.com/ionic-team/ionic/issues/18315)) ([00a27dc](https://github.com/ionic-team/ionic/commit/00a27dc)), references [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **loading:** allow html content ([#18242](https://github.com/ionic-team/ionic/issues/18242)) ([a6cb5f2](https://github.com/ionic-team/ionic/commit/a6cb5f2)), closes [#18135](https://github.com/ionic-team/ionic/issues/18135)
|
||||
* **overlay:** hide scrollbars on non-scrollable content ([#16767](https://github.com/ionic-team/ionic/issues/16767)) ([875d563](https://github.com/ionic-team/ionic/commit/875d563)), closes [#14178](https://github.com/ionic-team/ionic/issues/14178)
|
||||
* **picker:** update the column positions in RTL ([#18339](https://github.com/ionic-team/ionic/issues/18339)) ([788a56c](https://github.com/ionic-team/ionic/commit/788a56c)), closes [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **range:** update border-radius on range pin for RTL ([#18321](https://github.com/ionic-team/ionic/issues/18321)) ([4855351](https://github.com/ionic-team/ionic/commit/4855351)), closes [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **searchbar:** position buttons properly in RTL ([#18325](https://github.com/ionic-team/ionic/issues/18325)) ([845def8](https://github.com/ionic-team/ionic/commit/845def8)), references [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **segment:** update segment border for RTL ([#18326](https://github.com/ionic-team/ionic/issues/18326)) ([805b225](https://github.com/ionic-team/ionic/commit/805b225)), closes [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **slides:** allow clicks to propagate to buttons ([#16728](https://github.com/ionic-team/ionic/issues/16728)) ([a8f9dfe](https://github.com/ionic-team/ionic/commit/a8f9dfe))
|
||||
* **tab-button:** apply background-focused when tabbing into tab button ([#17502](https://github.com/ionic-team/ionic/issues/17502)) ([d788a8e](https://github.com/ionic-team/ionic/commit/d788a8e)), closes [#17042](https://github.com/ionic-team/ionic/issues/17042)
|
||||
* **tabs:** position badge properly in RTL (only in Chrome) ([#18325](https://github.com/ionic-team/ionic/issues/18325)) ([845def8](https://github.com/ionic-team/ionic/commit/845def8)), references [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **tabs:** select the tab called by the select method after initialization ([#18300](https://github.com/ionic-team/ionic/issues/18300)) ([da38647](https://github.com/ionic-team/ionic/commit/da38647)), closes [#17957](https://github.com/ionic-team/ionic/issues/17957)
|
||||
* **toggle:** position toggle icon properly in RTL ([#18325](https://github.com/ionic-team/ionic/issues/18325)) ([845def8](https://github.com/ionic-team/ionic/commit/845def8)), references [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **toolbar:** update md toolbar button spacing and padding to match spec ([#17537](https://github.com/ionic-team/ionic/issues/17537)) ([fa87e35](https://github.com/ionic-team/ionic/commit/fa87e35)), closes [#16950](https://github.com/ionic-team/ionic/issues/16950) [#14444](https://github.com/ionic-team/ionic/issues/14444)
|
||||
|
||||
|
||||
|
||||
# [4.4.0 Beryllium](https://github.com/ionic-team/ionic/compare/v4.3.1...v4.4.0) (2019-05-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **button:** apply round property to button sizes in iOS ([#18125](https://github.com/ionic-team/ionic/issues/18125)) ([ae0eccc](https://github.com/ionic-team/ionic/commit/ae0eccc)), closes [#18108](https://github.com/ionic-team/ionic/issues/18108)
|
||||
* **datetime:** default to current date when value is null ([#18105](https://github.com/ionic-team/ionic/issues/18105)) ([ca233b5](https://github.com/ionic-team/ionic/commit/ca233b5)), closes [#18099](https://github.com/ionic-team/ionic/issues/18099)
|
||||
* **input:** clear on edit from inside native input ([#17115](https://github.com/ionic-team/ionic/issues/17115)) ([85093d6](https://github.com/ionic-team/ionic/commit/85093d6)), closes [#17055](https://github.com/ionic-team/ionic/issues/17055)
|
||||
* **item:** use the global activated background for md ripple color ([#16752](https://github.com/ionic-team/ionic/issues/16752)) ([95945c0](https://github.com/ionic-team/ionic/commit/95945c0)), closes [#16585](https://github.com/ionic-team/ionic/issues/16585)
|
||||
* **label:** use primary color on focus for md input labels ([#18183](https://github.com/ionic-team/ionic/issues/18183)) ([ddb8ef8](https://github.com/ionic-team/ionic/commit/ddb8ef8)), closes [#15602](https://github.com/ionic-team/ionic/issues/15602)
|
||||
* **menu** add prefixed transform for side menu animation ([#18128](https://github.com/ionic-team/ionic/issues/18128)) ([2457a23](https://github.com/ionic-team/ionic/commit/2457a23)), closes [#17729](https://github.com/ionic-team/ionic/issues/17729)
|
||||
* **reorder-group:** remove required parameter for the complete method ([#18084](https://github.com/ionic-team/ionic/issues/18084)) ([bd96491](https://github.com/ionic-team/ionic/commit/bd96491)), closes [#16302](https://github.com/ionic-team/ionic/issues/16302)
|
||||
* **segment:** decrease icon size on ios and stretch segment buttons to fill height ([#17751](https://github.com/ionic-team/ionic/issues/17751)) ([0fa645b](https://github.com/ionic-team/ionic/commit/0fa645b)), closes [#17069](https://github.com/ionic-team/ionic/issues/17069)
|
||||
* **textarea:** reposition textarea when keyboard appears ([#18098](https://github.com/ionic-team/ionic/issues/18098)) ([3cdab10](https://github.com/ionic-team/ionic/commit/3cdab10)), closes [#17847](https://github.com/ionic-team/ionic/issues/17847)
|
||||
* **toast:** allow button-color CSS variable to be overridden ([#18133](https://github.com/ionic-team/ionic/issues/18133)) ([0c83fd3](https://github.com/ionic-team/ionic/commit/0c83fd3)), closes [#18127](https://github.com/ionic-team/ionic/issues/18127)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **card:** add button functionality ([#17997](https://github.com/ionic-team/ionic/issues/17997)) ([669ec0d](https://github.com/ionic-team/ionic/commit/669ec0d)), closes [#17773](https://github.com/ionic-team/ionic/issues/17773)
|
||||
* **img:** add ionImgWillLoad event and emit ionImgDidLoad when image is loaded ([#18159](https://github.com/ionic-team/ionic/issues/18159)) ([38ffb98](https://github.com/ionic-team/ionic/commit/38ffb98)), closes [#17652](https://github.com/ionic-team/ionic/issues/17652) [#18161](https://github.com/ionic-team/ionic/issues/18161)
|
||||
* **item-sliding:** add open method ([#17964](https://github.com/ionic-team/ionic/issues/17964)) ([f912206](https://github.com/ionic-team/ionic/commit/f912206)), closes [#17899](https://github.com/ionic-team/ionic/issues/17899)
|
||||
* **menu-button:** add css variables for padding ([#18188](https://github.com/ionic-team/ionic/issues/18188)) ([ef98977](https://github.com/ionic-team/ionic/commit/ef98977)), closes [#18187](https://github.com/ionic-team/ionic/issues/18187)
|
||||
* **refresher:** add pullFactor property to control speed ([#16697](https://github.com/ionic-team/ionic/issues/16697)) ([9030dcc](https://github.com/ionic-team/ionic/commit/9030dcc)), closes [#15425](https://github.com/ionic-team/ionic/issues/15425)
|
||||
* **searchbar:** add disabled property ([#17935](https://github.com/ionic-team/ionic/issues/17935)) ([a5b9066](https://github.com/ionic-team/ionic/commit/a5b9066)), closes [#17921](https://github.com/ionic-team/ionic/issues/17921)
|
||||
* **textarea:** add option to expand textarea as value changes ([#16916](https://github.com/ionic-team/ionic/issues/16916)) ([cc8678a](https://github.com/ionic-team/ionic/commit/cc8678a))
|
||||
|
||||
|
||||
|
||||
## [4.3.1](https://github.com/ionic-team/ionic/compare/v4.3.0...v4.3.1) (2019-04-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** support replaceUrl with angular <7.2 ([#18106](https://github.com/ionic-team/ionic/issues/18106)) ([eb3cbe4](https://github.com/ionic-team/ionic/commit/eb3cbe4))
|
||||
* **security:** sanitize components using innerHTML ([#18146](https://github.com/ionic-team/ionic/issues/18146)) ([b839e6f](https://github.com/ionic-team/ionic/commit/b839e6f))
|
||||
|
||||
|
||||
|
||||
# [4.3.0 Lithium](https://github.com/ionic-team/ionic/compare/v4.2.0...v4.3.0) (2019-04-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **action-sheet:** default buttons to empty array ([9e63947](https://github.com/ionic-team/ionic/commit/9e63947))
|
||||
* **angular:** back button correctly goes back to proper tab ([#18005](https://github.com/ionic-team/ionic/issues/18005)) ([52e5a8d](https://github.com/ionic-team/ionic/commit/52e5a8d)), closes [#17278](https://github.com/ionic-team/ionic/issues/17278) [#15216](https://github.com/ionic-team/ionic/issues/15216)
|
||||
* **components:** add mode classes to components for use in shadow elements ([#17838](https://github.com/ionic-team/ionic/issues/17838)) ([e5c8c10](https://github.com/ionic-team/ionic/commit/e5c8c10)), closes [#17608](https://github.com/ionic-team/ionic/issues/17608)
|
||||
* **datetime:** date strings no longer revert to previous day ([#18018](https://github.com/ionic-team/ionic/issues/18018)) ([cc60b60](https://github.com/ionic-team/ionic/commit/cc60b60)), closes [#17977](https://github.com/ionic-team/ionic/issues/17977)
|
||||
* **input:** prevent input from losing focus when tapping clear button ([#18004](https://github.com/ionic-team/ionic/issues/18004)) ([29bb4fc](https://github.com/ionic-team/ionic/commit/29bb4fc)), closes [#18002](https://github.com/ionic-team/ionic/issues/18002)
|
||||
* **item:** use the correct input highlight for an inset line item ([#18052](https://github.com/ionic-team/ionic/issues/18052)) ([72be80c](https://github.com/ionic-team/ionic/commit/72be80c)), closes [#18051](https://github.com/ionic-team/ionic/issues/18051)
|
||||
* **item-sliding:** hide closed side options while dragging side options open ([#17986](https://github.com/ionic-team/ionic/issues/17986)) ([f13722c](https://github.com/ionic-team/ionic/commit/f13722c)), closes [#17822](https://github.com/ionic-team/ionic/issues/17822)
|
||||
* **slides:** allow zoom to work ([18b347b](https://github.com/ionic-team/ionic/commit/18b347b)), closes [#17981](https://github.com/ionic-team/ionic/issues/17981)
|
||||
* **slides:** expose interface to provide custom animations ([#17959](https://github.com/ionic-team/ionic/issues/17959)) ([4474974](https://github.com/ionic-team/ionic/commit/4474974)), closes [#16616](https://github.com/ionic-team/ionic/issues/16616)
|
||||
* **textarea:** float label when a value is changed async ([#18024](https://github.com/ionic-team/ionic/issues/18024)) ([494991e](https://github.com/ionic-team/ionic/commit/494991e)), closes [#17555](https://github.com/ionic-team/ionic/issues/17555) [#17559](https://github.com/ionic-team/ionic/issues/17559)
|
||||
* **textarea:** update label alignment for inputs and textareas ([#18042](https://github.com/ionic-team/ionic/issues/18042)) ([38ae362](https://github.com/ionic-team/ionic/commit/38ae362)), closes [#16187](https://github.com/ionic-team/ionic/issues/16187)
|
||||
* **vue:** use direction type from core ([#17901](https://github.com/ionic-team/ionic/issues/17901)) ([fa13173](https://github.com/ionic-team/ionic/commit/fa13173))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **toast:** add header and additional custom toast buttons ([#17147](https://github.com/ionic-team/ionic/issues/17147)) ([6e1a8f1](https://github.com/ionic-team/ionic/commit/6e1a8f1)), closes [#16791](https://github.com/ionic-team/ionic/issues/16791) [#16237](https://github.com/ionic-team/ionic/issues/16237) [#17611](https://github.com/ionic-team/ionic/issues/17611)
|
||||
* **toast:** add variables to change position start/end of toast ([#17961](https://github.com/ionic-team/ionic/issues/17961)) ([07e739a](https://github.com/ionic-team/ionic/commit/07e739a)), closes [#17854](https://github.com/ionic-team/ionic/issues/17854)
|
||||
|
||||
|
||||
|
||||
# [4.2.0 Helium](https://github.com/ionic-team/ionic/compare/v4.1.2...v4.2.0) (2019-04-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** properly check for replaceUrl in routing ([#17879](https://github.com/ionic-team/ionic/issues/17879)) ([f2c8db9](https://github.com/ionic-team/ionic/commit/f2c8db9)), closes [#15181](https://github.com/ionic-team/ionic/issues/15181)
|
||||
* **angular:** return proper types in the overlay.getTop method ([#17802](https://github.com/ionic-team/ionic/issues/17802)) ([439b10e](https://github.com/ionic-team/ionic/commit/439b10e))
|
||||
* **angular:** support relative router links ([d9a7c63](https://github.com/ionic-team/ionic/commit/d9a7c63)), closes [#17888](https://github.com/ionic-team/ionic/issues/17888) [#16534](https://github.com/ionic-team/ionic/issues/16534) [#16736](https://github.com/ionic-team/ionic/issues/16736) [#16954](https://github.com/ionic-team/ionic/issues/16954)
|
||||
* **angular:** update ng-add schematic ([9443bfe](https://github.com/ionic-team/ionic/commit/9443bfe))
|
||||
* **css:** resolve spinner and checkbox issues on older chrome versions ([#17896](https://github.com/ionic-team/ionic/issues/17896)) ([dbb39cd](https://github.com/ionic-team/ionic/commit/dbb39cd)), closes [#17524](https://github.com/ionic-team/ionic/issues/17524) [#17501](https://github.com/ionic-team/ionic/issues/17501)
|
||||
* **datetime:** default to local date when no date given ([#17706](https://github.com/ionic-team/ionic/issues/17706)) ([bab56e8](https://github.com/ionic-team/ionic/commit/bab56e8))
|
||||
* **datetime:** recalculate day column when month or year is changed ([#17815](https://github.com/ionic-team/ionic/issues/17815)) ([9273f97](https://github.com/ionic-team/ionic/commit/9273f97)), closes [#14233](https://github.com/ionic-team/ionic/issues/14233) [#14732](https://github.com/ionic-team/ionic/issues/14732) [#15452](https://github.com/ionic-team/ionic/issues/15452) [#15794](https://github.com/ionic-team/ionic/issues/15794) [#17633](https://github.com/ionic-team/ionic/issues/16733) [#17060](https://github.com/ionic-team/ionic/issues/17060) [#17510](https://github.com/ionic-team/ionic/issues/17510) [#17521](https://github.com/ionic-team/ionic/issues/17521)
|
||||
* **item-option:** add styling and behaviour for disabled item-option ([#17909](https://github.com/ionic-team/ionic/issues/17909)) ([346ecb2](https://github.com/ionic-team/ionic/commit/346ecb2)), closes [#17905](https://github.com/ionic-team/ionic/issues/17905)
|
||||
* **progress-bar:** flip rtl using the existing reversed property ([#17464](https://github.com/ionic-team/ionic/issues/17464)) ([fccaaf8](https://github.com/ionic-team/ionic/commit/fccaaf8)), closes [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **react:** ensure tabs are resilient to optional tabs ([#17862](https://github.com/ionic-team/ionic/issues/17862)) ([c29f5a6](https://github.com/ionic-team/ionic/commit/c29f5a6))
|
||||
* **reorder-group:** add ability to reorder items inside shadow ([#17747](https://github.com/ionic-team/ionic/issues/17747)) ([352797e](https://github.com/ionic-team/ionic/commit/352797e)), closes [#17746](https://github.com/ionic-team/ionic/issues/17746)
|
||||
* **select:** update overlay options when added asynchronously ([#17860](https://github.com/ionic-team/ionic/issues/17860)) ([1ecfcd1](https://github.com/ionic-team/ionic/commit/1ecfcd1)), closes [#15716](https://github.com/ionic-team/ionic/issues/15716) [#17851](https://github.com/ionic-team/ionic/issues/17851)
|
||||
* **virtual-scroll:** use correct item top calculation with header or footer function ([#15948](https://github.com/ionic-team/ionic/issues/15948)) ([#17345](https://github.com/ionic-team/ionic/issues/17345)) ([a8a48a4](https://github.com/ionic-team/ionic/commit/a8a48a4)), closes [#17298](https://github.com/ionic-team/ionic/issues/17298)
|
||||
* **vue:** back button event handling ([#17877](https://github.com/ionic-team/ionic/issues/17877)) ([dceec07](https://github.com/ionic-team/ionic/commit/dceec07))
|
||||
* **vue:** update popover to use correct controller ([#17865](https://github.com/ionic-team/ionic/issues/17865)) ([0cb7db0](https://github.com/ionic-team/ionic/commit/0cb7db0))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **img:** add ionError event ([#17134](https://github.com/ionic-team/ionic/issues/17134)) ([04f931f](https://github.com/ionic-team/ionic/commit/04f931f)), closes [#16947](https://github.com/ionic-team/ionic/issues/16947)
|
||||
* **range:** add ticks attribute ([#17718](https://github.com/ionic-team/ionic/issues/17718)) ([016fa16](https://github.com/ionic-team/ionic/commit/016fa16)), closes [#17717](https://github.com/ionic-team/ionic/issues/17717)
|
||||
* **vue:** support for ion-tabs ([#17678](https://github.com/ionic-team/ionic/issues/17678)) ([ee71675](https://github.com/ionic-team/ionic/commit/ee71675))
|
||||
* **vue:** support ion-vue-router ([#17821](https://github.com/ionic-team/ionic/issues/17821)) ([71e5994](https://github.com/ionic-team/ionic/commit/71e5994))
|
||||
|
||||
|
||||
|
||||
## [4.1.2](https://github.com/ionic-team/ionic/compare/v4.1.1...v4.1.2) (2019-03-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **chip:** use transparent outline on color chips ([#17719](https://github.com/ionic-team/ionic/issues/17719)) ([f6783db](https://github.com/ionic-team/ionic/commit/f6783db))
|
||||
* **datetime:** default to local date ([#17706](https://github.com/ionic-team/ionic/issues/17706)) ([bab56e8](https://github.com/ionic-team/ionic/commit/bab56e8))
|
||||
* **input:** use max-height for input for consistency across browsers ([#17394](https://github.com/ionic-team/ionic/issues/17394)) ([67a9137](https://github.com/ionic-team/ionic/commit/67a9137))
|
||||
* **item:** add missing ripple color CSS property ([#17814](https://github.com/ionic-team/ionic/issues/17814)) ([807820f](https://github.com/ionic-team/ionic/commit/807820f)), closes [#17523](https://github.com/ionic-team/ionic/issues/17523)
|
||||
* **item-option:** add styling for slots other than icon-only ([#17711](https://github.com/ionic-team/ionic/issues/17711)) ([14f758c](https://github.com/ionic-team/ionic/commit/14f758c)), closes [#17737](https://github.com/ionic-team/ionic/issues/17737) [#17402](https://github.com/ionic-team/ionic/issues/17402)
|
||||
* **platform:** account for larger tablets ([#17630](https://github.com/ionic-team/ionic/issues/17630)) ([29dbd07](https://github.com/ionic-team/ionic/commit/29dbd07))
|
||||
* **popover:** update animation origin in RTL/MD ([#17645](https://github.com/ionic-team/ionic/issues/17645)) ([617453b](https://github.com/ionic-team/ionic/commit/617453b)), closes [#17012](https://github.com/ionic-team/ionic/issues/17012)
|
||||
* **spinner:** fix default spinner logic for relevant components ([#17660](https://github.com/ionic-team/ionic/issues/17660)) ([9c48fa7](https://github.com/ionic-team/ionic/commit/9c48fa7)), closes [#17659](https://github.com/ionic-team/ionic/issues/17659)
|
||||
* **toggle:** do not use the contrast color for toggle inner color ([#17798](https://github.com/ionic-team/ionic/issues/17798)) ([2225941](https://github.com/ionic-team/ionic/commit/2225941)), closes [#17536](https://github.com/ionic-team/ionic/issues/17536)
|
||||
* **transition:** animate all toolbars in iOS page transitions ([#17224](https://github.com/ionic-team/ionic/issues/17224)) ([7d01207](https://github.com/ionic-team/ionic/commit/7d01207))
|
||||
|
||||
|
||||
|
||||
## [4.1.1](https://github.com/ionic-team/ionic/compare/v4.1.0...v4.1.1) (2019-03-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **display:** update to correct css classes ([cabbeb2](https://github.com/ionic-team/ionic/commit/cabbeb2))
|
||||
|
||||
|
||||
|
||||
# [4.1.0 Hydrogen](https://github.com/ionic-team/ionic/compare/v4.0.2...v4.1.0) (2019-03-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** fix adding [@ionic](https://github.com/ionic)/angular when using ng add ([#17597](https://github.com/ionic-team/ionic/issues/17597)) ([484d92c](https://github.com/ionic-team/ionic/commit/484d92c)), closes [#17596](https://github.com/ionic-team/ionic/issues/17596)
|
||||
* **animation:** fix header flicker on ios ([#17422](https://github.com/ionic-team/ionic/issues/17422)) ([ad20bd6](https://github.com/ionic-team/ionic/commit/ad20bd6))
|
||||
* **css:** add the missing css classes to flex and float utils ([#17570](https://github.com/ionic-team/ionic/issues/17570)) ([c49276c](https://github.com/ionic-team/ionic/commit/c49276c)), closes [#17569](https://github.com/ionic-team/ionic/issues/17569)
|
||||
* **fab:** disabled fab button no longer opens fab list ([#17620](https://github.com/ionic-team/ionic/issues/17620)) ([c475dab](https://github.com/ionic-team/ionic/commit/c475dab))
|
||||
* **img:** remove space under img ([#17582](https://github.com/ionic-team/ionic/issues/17582)) ([621c79b](https://github.com/ionic-team/ionic/commit/621c79b))
|
||||
* **item:** slotted ion-icon receives custom color ([#17585](https://github.com/ionic-team/ionic/issues/17585)) ([14dd871](https://github.com/ionic-team/ionic/commit/14dd871))
|
||||
* **item-sliding:** no longer close all open items when deleting an item ([#17579](https://github.com/ionic-team/ionic/issues/17579)) ([3de7795](https://github.com/ionic-team/ionic/commit/3de7795))
|
||||
* **loading:** add backdropDismiss to the interface ([#17668](https://github.com/ionic-team/ionic/issues/17668)) ([28fd75e](https://github.com/ionic-team/ionic/commit/28fd75e)), closes [#17369](https://github.com/ionic-team/ionic/issues/17369)
|
||||
* **popover:** update placement per md spec ([#17429](https://github.com/ionic-team/ionic/issues/17429)) ([a99d179](https://github.com/ionic-team/ionic/commit/a99d179))
|
||||
* **range:** clamp values that are out of bounds ([#17623](https://github.com/ionic-team/ionic/issues/17623)) ([5a197ca](https://github.com/ionic-team/ionic/commit/5a197ca))
|
||||
* **refresher:** check for cancelable before preventing default ([#17351](https://github.com/ionic-team/ionic/issues/17351)) ([f205b10](https://github.com/ionic-team/ionic/commit/f205b10)), closes [#15256](https://github.com/ionic-team/ionic/issues/15256)
|
||||
* **reorder:** allow sliding items to be reordered ([#17568](https://github.com/ionic-team/ionic/issues/17568)) ([3b331b5](https://github.com/ionic-team/ionic/commit/3b331b5))
|
||||
* **ssr:** fix angular global window and document references ([f44c17e](https://github.com/ionic-team/ionic/commit/f44c17e))
|
||||
* **ssr:** fix global window and document references ([#17590](https://github.com/ionic-team/ionic/issues/17590)) ([4646f53](https://github.com/ionic-team/ionic/commit/4646f53))
|
||||
* **vue:** correct passing of props and data to components ([#17188](https://github.com/ionic-team/ionic/issues/17188)) ([2ce4940](https://github.com/ionic-team/ionic/commit/2ce4940))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **checkbox:** implement indeterminate state ([#16951](https://github.com/ionic-team/ionic/issues/16951)) ([c641ae1](https://github.com/ionic-team/ionic/commit/c641ae1)), closes [#16943](https://github.com/ionic-team/ionic/issues/16943)
|
||||
* **css:** add CSS display utilities ([#17359](https://github.com/ionic-team/ionic/issues/17359)) ([6bea9d3](https://github.com/ionic-team/ionic/commit/6bea9d3)), closes [#10904](https://github.com/ionic-team/ionic/issues/10904)
|
||||
* **select:** add compareWith property ([#17358](https://github.com/ionic-team/ionic/issues/17358)) ([69ecebb](https://github.com/ionic-team/ionic/commit/69ecebb))
|
||||
* **skeleton-text:** adds animated prop and support for CSS styling ([#17612](https://github.com/ionic-team/ionic/issues/17612)) ([d66b12b](https://github.com/ionic-team/ionic/commit/d66b12b)), closes [ionic-team/ionic-docs#407](https://github.com/ionic-team/ionic-docs/issues/407)
|
||||
|
||||
|
||||
|
||||
## [4.0.2](https://github.com/ionic-team/ionic/compare/v4.0.1...v4.0.2) (2019-02-20)
|
||||
|
||||
|
||||
@@ -420,7 +802,7 @@ Tabs got the last bit of changes needed in order to support lazy loading of comp
|
||||
pathMatch: 'full'
|
||||
}
|
||||
];
|
||||
```
|
||||
```
|
||||
|
||||
</detail>
|
||||
|
||||
@@ -551,14 +933,14 @@ Segment Button now requires the text to be wrapped in an `ion-label` element for
|
||||
<ion-segment-button>
|
||||
Item One
|
||||
</ion-segment-button>
|
||||
```
|
||||
```
|
||||
*New usage:*
|
||||
|
||||
```html
|
||||
<ion-segment-button>
|
||||
<ion-label>Item One</ion-label>
|
||||
</ion-segment-button>
|
||||
```
|
||||
```
|
||||
|
||||
#### Simplifying Chip
|
||||
|
||||
@@ -717,8 +1099,8 @@ Here, we have an `ion-tab` element that accepts an icon, a label, and a link to
|
||||
<ion-tab-bar>
|
||||
|
||||
<!-- No ion-tab, just a link that looks like a tab -->
|
||||
<ion-tab-button href="https://beta.ionicframework.com">
|
||||
<!-- <a href="https://beta.ionicframework.com"> -->
|
||||
<ion-tab-button href="https://ionicframework.com">
|
||||
<!-- <a href="https://ionicframework.com"> -->
|
||||
<ion-icon name="globe"></ion-icon>
|
||||
<ion-label>Schedule</ion-label>
|
||||
</ion-tab-button>
|
||||
@@ -2074,4 +2456,5 @@ The following dependencies need to be updated to resolve build errors
|
||||
|
||||
|
||||
<a name="0.1.0"></a>
|
||||
|
||||
## [0.1.0](https://github.com/ionic-team/ionic/commit/43a8c4c7a719169336a84964fc1c737562d764a6) (2018-03-01)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
### Migration Guide
|
||||
|
||||
If you aren't sure where to start in upgrading to v4, we recommend reading through our [migration guide](https://beta.ionicframework.com/docs/building/migration) first.
|
||||
If you aren't sure where to start in upgrading to v4, we recommend reading through our [migration guide](https://ionicframework.com/docs/building/migration) first.
|
||||
|
||||
### Migration Linter
|
||||
|
||||
@@ -182,13 +182,14 @@ These have been renamed to the following, and moved from the button element to t
|
||||
|
||||
In addition, several sets of mutually exclusive boolean attributes have been combined into a single string attribute.
|
||||
|
||||
The `small` and `large` attributes are now combined under the `size` attribute. The `clear`, `outline`, and `solid` attributes have been combined under `fill`. And, lastly, the `full` and `block` attributes have been combined under `expand`.
|
||||
The `small` and `large` attributes are now combined under the `size` attribute. The `clear`, `outline`, and `solid` attributes have been combined under `fill`. The `full` and `block` attributes have been combined under `expand`. And, lastly, the `round` attribute is now used under `shape`.
|
||||
|
||||
| Old Property | New Property | Property Behavior |
|
||||
| --------------------------- | ------------ | --------------------------- |
|
||||
| `small`, `large` | `size` | Sets the button size. |
|
||||
| `clear`, `outline`, `solid` | `fill` | Sets the button fill style. |
|
||||
| `full`, `block` | `expand` | Sets the button width. |
|
||||
| `round` | `shape` | Sets the button shape. |
|
||||
|
||||
|
||||
**Old Usage Example:**
|
||||
@@ -225,6 +226,10 @@ The `small` and `large` attributes are now combined under the `size` attribute.
|
||||
<ion-button full>
|
||||
Full-width Button
|
||||
</ion-button>
|
||||
|
||||
<ion-button round>
|
||||
Round Button
|
||||
</ion-button>
|
||||
```
|
||||
|
||||
**New Usage Example:**
|
||||
@@ -251,6 +256,10 @@ The `small` and `large` attributes are now combined under the `size` attribute.
|
||||
<ion-button expand="full">
|
||||
Full-width Button
|
||||
</ion-button>
|
||||
|
||||
<ion-button shape="round">
|
||||
Round Button
|
||||
</ion-button>
|
||||
```
|
||||
|
||||
|
||||
@@ -774,6 +783,26 @@ The option component should now be written as an `ion-item-option`. Previously i
|
||||
|
||||
The `getSlidingPercent` method has been renamed to `getSlidingRatio` since the function is returning a ratio of the open amount of the item compared to the width of the options.
|
||||
|
||||
### Arguments Changed
|
||||
|
||||
The `ionDrag` event no longer gets the sliding item as an argument. It now takes an event with a property `details` which contains two properties `amount` and `ratio` reflecting the absolute and ratio values of the sliding action respectively.
|
||||
|
||||
**Old Usage Example:**
|
||||
|
||||
```typescript
|
||||
dragged(item: ItemSliding) {
|
||||
console.log(item.getSlidingPercent());
|
||||
console.log(item.getOpenAmount());
|
||||
}
|
||||
```
|
||||
|
||||
**New Usage Example:**
|
||||
```typescript
|
||||
dragged(ev: { details: { amount: number, ratio: number } }) {
|
||||
console.log(ev.details.ratio);
|
||||
console.log(ev.details.amount);
|
||||
}
|
||||
```
|
||||
|
||||
## Label
|
||||
|
||||
@@ -1065,11 +1094,11 @@ async openLoading() {
|
||||
let loading = this.loadingCtrl.create({
|
||||
content: 'Loading...'
|
||||
});
|
||||
|
||||
|
||||
await loading.present();
|
||||
|
||||
|
||||
const { role, data } = await loading.onDidDismiss();
|
||||
|
||||
|
||||
console.log('Loading dismissed!');
|
||||
}
|
||||
```
|
||||
@@ -1740,19 +1769,19 @@ Changes the `font-family` of the whole page based on the mode selected (iOS or M
|
||||
The following set of CSS files are optional and can safely be commented out if the application is not using any of the features.
|
||||
|
||||
- **padding.css**
|
||||
Adds utility attributes that allow adding `padding` and `margin` attributes to any element. See [content space](https://beta.ionicframework.com/docs/layout/css-utilities#content-space) for what this includes.
|
||||
Adds utility attributes that allow adding `padding` and `margin` attributes to any element. See [content space](https://ionicframework.com/docs/layout/css-utilities#content-space) for what this includes.
|
||||
|
||||
- **float-elements.css**
|
||||
Adds utility attributes that allow adding `float` attributes to any element. See [element placement](https://beta.ionicframework.com/docs/layout/css-utilities/#element-placement) for what this includes.
|
||||
Adds utility attributes that allow adding `float` attributes to any element. See [element placement](https://ionicframework.com/docs/layout/css-utilities/#element-placement) for what this includes.
|
||||
|
||||
- **text-alignment.css**
|
||||
Adds utility attributes that allow adding text alignment attributes to any element. See [text alignment](https://beta.ionicframework.com/docs/layout/css-utilities/#text-alignment) for what this includes.
|
||||
Adds utility attributes that allow adding text alignment attributes to any element. See [text alignment](https://ionicframework.com/docs/layout/css-utilities/#text-alignment) for what this includes.
|
||||
|
||||
- **text-transformation.css**
|
||||
Adds utility attributes that allow adding text transformation attributes to any element. See [text transformation](https://beta.ionicframework.com/docs/layout/css-utilities/#text-transformation) for what this includes.
|
||||
Adds utility attributes that allow adding text transformation attributes to any element. See [text transformation](https://ionicframework.com/docs/layout/css-utilities/#text-transformation) for what this includes.
|
||||
|
||||
- **flex-utils.css**
|
||||
Adds utility attributes that allow adding flex container and item attributes to any element. See [flex properties](https://beta.ionicframework.com/docs/layout/css-utilities/#flex-properties) for what this includes.
|
||||
Adds utility attributes that allow adding flex container and item attributes to any element. See [flex properties](https://ionicframework.com/docs/layout/css-utilities/#flex-properties) for what this includes.
|
||||
|
||||
|
||||
#### Including the CSS Files
|
||||
@@ -1831,7 +1860,7 @@ p {
|
||||
|
||||
Sass variables should no longer be used to change Ionic components. We have built Ionic to be customizable using CSS variables, instead.
|
||||
|
||||
For more information on theming, check out the [theming documentation](https://beta.ionicframework.com/docs/theming/basics).
|
||||
For more information on theming, check out the [theming documentation](https://ionicframework.com/docs/theming/basics).
|
||||
|
||||
|
||||
## Toast
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "4.0.2",
|
||||
"version": "4.7.2",
|
||||
"description": "Angular specific wrappers for @ionic/core",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -19,6 +19,10 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/ionic-team/ionic.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/ionic-team/ionic/issues"
|
||||
},
|
||||
"homepage": "https://ionicframework.com/",
|
||||
"scripts": {
|
||||
"build": "npm run clean && npm run build.core && npm run build.ng && npm run clean-generated",
|
||||
"build.core": "node scripts/build-core.js",
|
||||
@@ -38,29 +42,29 @@
|
||||
"validate": "npm i && npm run lint && npm run test && npm run build"
|
||||
},
|
||||
"module": "dist/fesm5.js",
|
||||
"main": "dist/fesm5.js",
|
||||
"main": "dist/fesm5.cjs.js",
|
||||
"types": "dist/core.d.ts",
|
||||
"files": [
|
||||
"dist/",
|
||||
"css/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@ionic/core": "4.0.2",
|
||||
"@ionic/core": "4.7.2",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular-devkit/core": "^7.2.1",
|
||||
"@angular-devkit/schematics": "^7.2.1",
|
||||
"@angular/core": "^7.2.1",
|
||||
"@angular/common": "^7.2.1",
|
||||
"@angular/forms": "^7.2.1",
|
||||
"@angular/router": "^7.2.1",
|
||||
"@angular/compiler": "^7.2.1",
|
||||
"@angular/compiler-cli": "^7.2.1",
|
||||
"@angular/platform-browser": "^7.2.1",
|
||||
"@angular/platform-browser-dynamic": "^7.2.1",
|
||||
"@angular-devkit/core": "7.2.1 - 8",
|
||||
"@angular-devkit/schematics": "7.2.1 - 8",
|
||||
"@angular/core": "7.2.1 - 8",
|
||||
"@angular/common": "7.2.1 - 8",
|
||||
"@angular/forms": "7.2.1 - 8",
|
||||
"@angular/router": "7.2.1 - 8",
|
||||
"@angular/compiler": "7.2.1 - 8",
|
||||
"@angular/compiler-cli": "7.2.1 - 8",
|
||||
"@angular/platform-browser": "7.2.1 - 8",
|
||||
"@angular/platform-browser-dynamic": "7.2.1 - 8",
|
||||
"rxjs": ">=6.2.0",
|
||||
"zone.js": "^0.8.26"
|
||||
"zone.js": ">=0.8.26"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/core": "^7.2.1",
|
||||
@@ -73,17 +77,17 @@
|
||||
"@angular/platform-browser": "^7.2.1",
|
||||
"@angular/platform-browser-dynamic": "^7.2.1",
|
||||
"@angular/router": "^7.2.1",
|
||||
"@types/node": "~10.12.0",
|
||||
"@types/node": "~12.0.12",
|
||||
"fs-extra": "^7.0.0",
|
||||
"glob": "^7.1.3",
|
||||
"rollup": "^1.1.2",
|
||||
"rollup-plugin-node-resolve": "^4.0.0",
|
||||
"rxjs": "^6.2.0",
|
||||
"glob": "^7.1.4",
|
||||
"rollup": "~1.17.0",
|
||||
"rollup-plugin-node-resolve": "~5.2.0",
|
||||
"rxjs": "^6.5.2",
|
||||
"tsickle": "^0.34.0",
|
||||
"tslint": "^5.12.1",
|
||||
"tslint-ionic-rules": "0.0.21",
|
||||
"typescript": "3.2.4",
|
||||
"zone.js": "^0.8.28"
|
||||
"typescript": "~3.2.2",
|
||||
"zone.js": "~0.8.26"
|
||||
},
|
||||
"schematics": "./dist/schematics/collection.json"
|
||||
}
|
||||
|
||||
@@ -15,3 +15,7 @@
|
||||
npm run build.link ../ionic-conference-app
|
||||
|
||||
When the command above is ran from the `angular` directory, it will build `@ionic/angular` and copy the `dist` directory to the correct location of another local project. In the example above, the end result is that it copies the `dist` directory to `../ionic-conference-app/node_modules/@ionic/angular/dist`. The path given should be relative to the root of this mono repo.
|
||||
|
||||
## package.json note
|
||||
|
||||
The `package.json` file in this directory references __Ionic 3__ and is in here to get GitHub to properly show the Used By counts on the repo. __Do not remove it!__
|
||||
|
||||
6
angular/scripts/clean.js
vendored
6
angular/scripts/clean.js
vendored
@@ -1,12 +1,12 @@
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT_DIR = path.join(__dirname, '..');
|
||||
|
||||
const cleanDirs = [
|
||||
'dist'
|
||||
path.join(ROOT_DIR, 'dist')
|
||||
];
|
||||
|
||||
cleanDirs.forEach(dir => {
|
||||
const cleanDir = path.join(__dirname, '../', dir);
|
||||
fs.removeSync(cleanDir);
|
||||
fs.emptyDirSync(dir);
|
||||
});
|
||||
|
||||
16
angular/scripts/package.json
Normal file
16
angular/scripts/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "ionic-angular",
|
||||
"version": "`gulp package` generates dist/package.json from this template using the root ./package.json",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ionic-team/ionic.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "umd/index.js",
|
||||
"module": "index.js",
|
||||
"es2015": "es2015/index.js",
|
||||
"peerDependencies": {
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,13 @@ import resolve from 'rollup-plugin-node-resolve';
|
||||
|
||||
export default {
|
||||
input: 'build/es2015/core.js',
|
||||
output: {
|
||||
file: 'dist/fesm2015.js',
|
||||
format: 'es'
|
||||
},
|
||||
external: (id) => {
|
||||
// inline @ionic/core deps
|
||||
if (id === '@ionic/core') {
|
||||
return false;
|
||||
output: [
|
||||
{
|
||||
file: 'dist/fesm2015.js',
|
||||
format: 'es'
|
||||
}
|
||||
],
|
||||
external: (id) => {
|
||||
// anything else is external
|
||||
// Windows: C:\xxxxxx\xxx
|
||||
const colonPosition = 1;
|
||||
|
||||
@@ -4,6 +4,15 @@ const newConfig = {
|
||||
...config,
|
||||
input: 'build/es5/core.js',
|
||||
};
|
||||
newConfig.output.file = 'dist/fesm5.js';
|
||||
newConfig.output = [
|
||||
{
|
||||
file: 'dist/fesm5.js',
|
||||
format: 'es'
|
||||
},
|
||||
{
|
||||
file: 'dist/fesm5.cjs.js',
|
||||
format: 'cjs'
|
||||
}
|
||||
];
|
||||
|
||||
export { newConfig as default };
|
||||
|
||||
@@ -1,55 +1,39 @@
|
||||
import { defineCustomElements } from '@ionic/core/loader';
|
||||
import { NgZone } from '@angular/core';
|
||||
import { applyPolyfills, defineCustomElements } from '@ionic/core/loader';
|
||||
|
||||
import { Config } from './providers/config';
|
||||
import { IonicWindow } from './types/interfaces';
|
||||
import { raf } from './util/util';
|
||||
|
||||
export function appInitialize(config: Config) {
|
||||
export const appInitialize = (config: Config, doc: Document, zone: NgZone) => {
|
||||
return (): any => {
|
||||
const win: IonicWindow | undefined = window as any;
|
||||
if (typeof win !== 'undefined') {
|
||||
const win: IonicWindow | undefined = doc.defaultView as any;
|
||||
if (win) {
|
||||
const Ionic = win.Ionic = win.Ionic || {};
|
||||
|
||||
Ionic.config = config;
|
||||
Ionic.asyncQueue = false;
|
||||
|
||||
Ionic.ael = (elm, eventName, cb, opts) => {
|
||||
if (elm.__zone_symbol__addEventListener && skipZone(eventName)) {
|
||||
elm.__zone_symbol__addEventListener(eventName, cb, opts);
|
||||
} else {
|
||||
elm.addEventListener(eventName, cb, opts);
|
||||
}
|
||||
Ionic.config = {
|
||||
...config,
|
||||
_zoneGate: (h: any) => zone.run(h)
|
||||
};
|
||||
|
||||
Ionic.rel = (elm, eventName, cb, opts) => {
|
||||
if (elm.__zone_symbol__removeEventListener && skipZone(eventName)) {
|
||||
elm.__zone_symbol__removeEventListener(eventName, cb, opts);
|
||||
} else {
|
||||
elm.removeEventListener(eventName, cb, opts);
|
||||
}
|
||||
};
|
||||
const aelFn = '__zone_symbol__addEventListener' in (doc.body as any)
|
||||
? '__zone_symbol__addEventListener'
|
||||
: 'addEventListener';
|
||||
|
||||
return defineCustomElements(win, {
|
||||
exclude: ['ion-tabs', 'ion-tab']
|
||||
return applyPolyfills().then(() => {
|
||||
return defineCustomElements(win, {
|
||||
exclude: ['ion-tabs', 'ion-tab'],
|
||||
syncQueue: true,
|
||||
raf,
|
||||
jmp: (h: any) => zone.runOutsideAngular(h),
|
||||
ael(elm, eventName, cb, opts) {
|
||||
(elm as any)[aelFn](eventName, cb, opts);
|
||||
},
|
||||
rel(elm, eventName, cb, opts) {
|
||||
elm.removeEventListener(eventName, cb, opts);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const SKIP_ZONE = [
|
||||
'scroll',
|
||||
'resize',
|
||||
|
||||
'touchstart',
|
||||
'touchmove',
|
||||
'touchend',
|
||||
|
||||
'mousedown',
|
||||
'mousemove',
|
||||
'mouseup',
|
||||
|
||||
'ionStyle',
|
||||
];
|
||||
|
||||
function skipZone(eventName: string) {
|
||||
return SKIP_ZONE.indexOf(eventName) >= 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -25,8 +25,8 @@ export class BooleanValueAccessor extends ValueAccessor {
|
||||
setIonicClasses(this.el);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target.checked'])
|
||||
_handleIonChange(value: any) {
|
||||
this.handleChangeEvent(value);
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
_handleIonChange(el: any) {
|
||||
this.handleChangeEvent(el, el.checked);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ export class NumericValueAccessor extends ValueAccessor {
|
||||
super(el);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target.value'])
|
||||
_handleIonChange(value: any) {
|
||||
this.handleChangeEvent(value);
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
_handleIonChange(el: any) {
|
||||
this.handleChangeEvent(el, el.value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: number | null) => void) {
|
||||
|
||||
@@ -20,8 +20,8 @@ export class RadioValueAccessor extends ValueAccessor {
|
||||
super(el);
|
||||
}
|
||||
|
||||
@HostListener('ionSelect', ['$event.target.checked'])
|
||||
_handleIonSelect(value: any) {
|
||||
this.handleChangeEvent(value);
|
||||
@HostListener('ionSelect', ['$event.target'])
|
||||
_handleIonSelect(el: any) {
|
||||
this.handleChangeEvent(el, el.checked);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ export class SelectValueAccessor extends ValueAccessor {
|
||||
super(el);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target.value'])
|
||||
_handleChangeEvent(value: any) {
|
||||
this.handleChangeEvent(value);
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
_handleChangeEvent(el: any) {
|
||||
this.handleChangeEvent(el, el.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ export class TextValueAccessor extends ValueAccessor {
|
||||
super(el);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target.value'])
|
||||
_handleInputEvent(value: any) {
|
||||
this.handleChangeEvent(value);
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
_handleInputEvent(el: any) {
|
||||
this.handleChangeEvent(el, el.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { ElementRef, HostListener } from '@angular/core';
|
||||
import { ControlValueAccessor } from '@angular/forms';
|
||||
|
||||
import { raf } from '../../util/util';
|
||||
|
||||
export class ValueAccessor implements ControlValueAccessor {
|
||||
|
||||
private onChange: (value: any) => void = () => {/**/};
|
||||
@@ -14,18 +16,22 @@ export class ValueAccessor implements ControlValueAccessor {
|
||||
setIonicClasses(this.el);
|
||||
}
|
||||
|
||||
handleChangeEvent(value: any) {
|
||||
if (value !== this.lastValue) {
|
||||
this.lastValue = value;
|
||||
this.onChange(value);
|
||||
handleChangeEvent(el: HTMLElement, value: any) {
|
||||
if (el === this.el.nativeElement) {
|
||||
if (value !== this.lastValue) {
|
||||
this.lastValue = value;
|
||||
this.onChange(value);
|
||||
}
|
||||
setIonicClasses(this.el);
|
||||
}
|
||||
setIonicClasses(this.el);
|
||||
}
|
||||
|
||||
@HostListener('ionBlur')
|
||||
_handleBlurEvent() {
|
||||
this.onTouched();
|
||||
setIonicClasses(this.el);
|
||||
@HostListener('ionBlur', ['$event.target'])
|
||||
_handleBlurEvent(el: any) {
|
||||
if (el === this.el.nativeElement) {
|
||||
this.onTouched();
|
||||
setIonicClasses(this.el);
|
||||
}
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: any) => void) {
|
||||
@@ -41,8 +47,8 @@ export class ValueAccessor implements ControlValueAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
export function setIonicClasses(element: ElementRef) {
|
||||
requestAnimationFrame(() => {
|
||||
export const setIonicClasses = (element: ElementRef) => {
|
||||
raf(() => {
|
||||
const input = element.nativeElement as HTMLElement;
|
||||
const classes = getClasses(input);
|
||||
setClasses(input, classes);
|
||||
@@ -52,9 +58,9 @@ export function setIonicClasses(element: ElementRef) {
|
||||
setClasses(item, classes);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function getClasses(element: HTMLElement) {
|
||||
const getClasses = (element: HTMLElement) => {
|
||||
const classList = element.classList;
|
||||
const classes = [];
|
||||
for (let i = 0; i < classList.length; i++) {
|
||||
@@ -64,9 +70,9 @@ function getClasses(element: HTMLElement) {
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
};
|
||||
|
||||
function setClasses(element: HTMLElement, classes: string[]) {
|
||||
const setClasses = (element: HTMLElement, classes: string[]) => {
|
||||
const classList = element.classList;
|
||||
|
||||
classList.remove(
|
||||
@@ -78,8 +84,8 @@ function setClasses(element: HTMLElement, classes: string[]) {
|
||||
'ion-pristine'
|
||||
);
|
||||
classList.add(...classes);
|
||||
}
|
||||
};
|
||||
|
||||
function startsWith(input: string, search: string): boolean {
|
||||
const startsWith = (input: string, search: string): boolean => {
|
||||
return input.substr(0, search.length) === search;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Attribute, ChangeDetectorRef, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, NgZone, OnDestroy, OnInit, Optional, Output, SkipSelf, ViewContainerRef } from '@angular/core';
|
||||
import { Location } from '@angular/common';
|
||||
import { Attribute, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, NgZone, OnDestroy, OnInit, Optional, Output, SkipSelf, ViewContainerRef } from '@angular/core';
|
||||
import { ActivatedRoute, ChildrenOutletContexts, OutletContext, PRIMARY_OUTLET, Router } from '@angular/router';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { Config } from '../../providers/config';
|
||||
import { NavController } from '../../providers/nav-controller';
|
||||
@@ -13,7 +16,6 @@ import { RouteView, getUrl } from './stack-utils';
|
||||
inputs: ['animated', 'swipeGesture']
|
||||
})
|
||||
export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
|
||||
private activated: ComponentRef<any> | null = null;
|
||||
private activatedView: RouteView | null = null;
|
||||
|
||||
@@ -23,6 +25,12 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
private stackCtrl: StackController;
|
||||
private nativeEl: HTMLIonRouterOutletElement;
|
||||
|
||||
// Maintain map of activated route proxies for each component instance
|
||||
private proxyMap = new WeakMap<any, ActivatedRoute>();
|
||||
|
||||
// Keep the latest activated route in a subject for the proxy routes to switch map to
|
||||
private currentActivatedRoute$ = new BehaviorSubject<{ component: any; activatedRoute: ActivatedRoute } | null>(null);
|
||||
|
||||
tabsPrefix: string | undefined;
|
||||
|
||||
@Output() stackEvents = new EventEmitter<any>();
|
||||
@@ -49,9 +57,9 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
private resolver: ComponentFactoryResolver,
|
||||
@Attribute('name') name: string,
|
||||
@Optional() @Attribute('tabs') tabs: string,
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
private config: Config,
|
||||
private navCtrl: NavController,
|
||||
commonLocation: Location,
|
||||
elementRef: ElementRef,
|
||||
router: Router,
|
||||
zone: NgZone,
|
||||
@@ -61,7 +69,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
this.nativeEl = elementRef.nativeElement;
|
||||
this.name = name || PRIMARY_OUTLET;
|
||||
this.tabsPrefix = tabs === 'true' ? getUrl(router, activatedRoute) : undefined;
|
||||
this.stackCtrl = new StackController(this.tabsPrefix, this.nativeEl, router, navCtrl, zone);
|
||||
this.stackCtrl = new StackController(this.tabsPrefix, this.nativeEl, router, navCtrl, zone, commonLocation);
|
||||
parentContexts.onChildOutletCreated(this.name, this as any);
|
||||
}
|
||||
|
||||
@@ -82,11 +90,13 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
this.activateWith(context.route, context.resolver || null);
|
||||
}
|
||||
}
|
||||
this.nativeEl.componentOnReady().then(() => {
|
||||
if (this._swipeGesture === undefined) {
|
||||
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', this.nativeEl.mode === 'ios');
|
||||
}
|
||||
});
|
||||
if ((this.nativeEl as any).componentOnReady) {
|
||||
this.nativeEl.componentOnReady().then(() => {
|
||||
if (this._swipeGesture === undefined) {
|
||||
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get isActivated(): boolean {
|
||||
@@ -132,6 +142,20 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
if (this.activated) {
|
||||
if (this.activatedView) {
|
||||
this.activatedView.savedData = new Map(this.getContext()!.children['contexts']);
|
||||
|
||||
/**
|
||||
* Ensure we are saving the NavigationExtras
|
||||
* data otherwise it will be lost
|
||||
*/
|
||||
this.activatedView.savedExtras = {};
|
||||
const context = this.getContext()!;
|
||||
|
||||
if (context.route) {
|
||||
const contextSnapshot = context.route.snapshot;
|
||||
|
||||
this.activatedView.savedExtras.queryParams = contextSnapshot.queryParams;
|
||||
this.activatedView.savedExtras.fragment = contextSnapshot.fragment;
|
||||
}
|
||||
}
|
||||
const c = this.component;
|
||||
this.activatedView = null;
|
||||
@@ -157,6 +181,8 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
const context = this.getContext()!;
|
||||
context.children['contexts'] = saved;
|
||||
}
|
||||
// Updated activated route proxy for this component
|
||||
this.updateActivatedRouteProxy(cmpRef.instance, activatedRoute);
|
||||
} else {
|
||||
const snapshot = (activatedRoute as any)._futureSnapshot;
|
||||
const component = snapshot.routeConfig!.component as any;
|
||||
@@ -165,13 +191,24 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
const factory = resolver.resolveComponentFactory(component);
|
||||
const childContexts = this.parentContexts.getOrCreateContext(this.name).children;
|
||||
|
||||
const injector = new OutletInjector(activatedRoute, childContexts, this.location.injector);
|
||||
// We create an activated route proxy object that will maintain future updates for this component
|
||||
// over its lifecycle in the stack.
|
||||
const component$ = new BehaviorSubject<any>(null);
|
||||
const activatedRouteProxy = this.createActivatedRouteProxy(component$, activatedRoute);
|
||||
|
||||
const injector = new OutletInjector(activatedRouteProxy, childContexts, this.location.injector);
|
||||
cmpRef = this.activated = this.location.createComponent(factory, this.location.length, injector);
|
||||
|
||||
// Once the component is created we can push it to our local subject supplied to the proxy
|
||||
component$.next(cmpRef.instance);
|
||||
|
||||
// Calling `markForCheck` to make sure we will run the change detection when the
|
||||
// `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component.
|
||||
enteringView = this.stackCtrl.createView(this.activated, activatedRoute);
|
||||
this.changeDetector.markForCheck();
|
||||
|
||||
// Store references to the proxy by component
|
||||
this.proxyMap.set(cmpRef.instance, activatedRouteProxy);
|
||||
this.currentActivatedRoute$.next({ component: cmpRef.instance, activatedRoute });
|
||||
}
|
||||
|
||||
this.activatedView = enteringView;
|
||||
@@ -210,6 +247,66 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
getActiveStackId(): string | undefined {
|
||||
return this.stackCtrl.getActiveStackId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the activated route can change over the life time of a component in an ion router outlet, we create
|
||||
* a proxy so that we can update the values over time as a user navigates back to components already in the stack.
|
||||
*/
|
||||
private createActivatedRouteProxy(component$: Observable<any>, activatedRoute: ActivatedRoute): ActivatedRoute {
|
||||
const proxy: any = new ActivatedRoute();
|
||||
|
||||
proxy._futureSnapshot = (activatedRoute as any)._futureSnapshot;
|
||||
proxy._routerState = (activatedRoute as any)._routerState;
|
||||
proxy.snapshot = activatedRoute.snapshot;
|
||||
proxy.outlet = activatedRoute.outlet;
|
||||
proxy.component = activatedRoute.component;
|
||||
|
||||
// Setup wrappers for the observables so consumers don't have to worry about switching to new observables as the state updates
|
||||
(proxy as any)._paramMap = this.proxyObservable(component$, 'paramMap');
|
||||
(proxy as any)._queryParamMap = this.proxyObservable(component$, 'queryParamMap');
|
||||
proxy.url = this.proxyObservable(component$, 'url');
|
||||
proxy.params = this.proxyObservable(component$, 'params');
|
||||
proxy.queryParams = this.proxyObservable(component$, 'queryParams');
|
||||
proxy.fragment = this.proxyObservable(component$, 'fragment');
|
||||
proxy.data = this.proxyObservable(component$, 'data');
|
||||
|
||||
return proxy as ActivatedRoute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a wrapped observable that will switch to the latest activated route matched by the given component
|
||||
*/
|
||||
private proxyObservable(component$: Observable<any>, path: string): Observable<any> {
|
||||
return component$.pipe(
|
||||
// First wait until the component instance is pushed
|
||||
filter(component => !!component),
|
||||
switchMap(component =>
|
||||
this.currentActivatedRoute$.pipe(
|
||||
filter(current => current !== null && current.component === component),
|
||||
switchMap(current => current && (current.activatedRoute as any)[path]),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the activated route proxy for the given component to the new incoming router state
|
||||
*/
|
||||
private updateActivatedRouteProxy(component: any, activatedRoute: ActivatedRoute): void {
|
||||
const proxy = this.proxyMap.get(component);
|
||||
if (!proxy) {
|
||||
throw new Error(`Could not find activated route proxy for view`);
|
||||
}
|
||||
|
||||
(proxy as any)._futureSnapshot = (activatedRoute as any)._futureSnapshot;
|
||||
(proxy as any)._routerState = (activatedRoute as any)._routerState;
|
||||
proxy.snapshot = activatedRoute.snapshot;
|
||||
proxy.outlet = activatedRoute.outlet;
|
||||
proxy.component = activatedRoute.component;
|
||||
|
||||
this.currentActivatedRoute$.next({ component, activatedRoute });
|
||||
}
|
||||
}
|
||||
|
||||
class OutletInjector implements Injector {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { ComponentRef, NgZone } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { RouterDirection } from '@ionic/core';
|
||||
@@ -22,6 +23,7 @@ export class StackController {
|
||||
private router: Router,
|
||||
private navCtrl: NavController,
|
||||
private zone: NgZone,
|
||||
private location: Location
|
||||
) {
|
||||
this.tabsPrefix = tabsPrefix !== undefined ? toSegments(tabsPrefix) : undefined;
|
||||
}
|
||||
@@ -29,7 +31,7 @@ export class StackController {
|
||||
createView(ref: ComponentRef<any>, activatedRoute: ActivatedRoute): RouteView {
|
||||
const url = getUrl(this.router, activatedRoute);
|
||||
const element = (ref && ref.location && ref.location.nativeElement) as HTMLElement;
|
||||
const unlistenEvents = bindLifecycleEvents(ref.instance, element);
|
||||
const unlistenEvents = bindLifecycleEvents(this.zone, ref.instance, element);
|
||||
return {
|
||||
id: this.nextId++,
|
||||
stackId: computeStackId(this.tabsPrefix, url),
|
||||
@@ -57,17 +59,72 @@ export class StackController {
|
||||
direction = 'back';
|
||||
animation = undefined;
|
||||
}
|
||||
|
||||
const viewsSnapshot = this.views.slice();
|
||||
|
||||
let currentNavigation;
|
||||
|
||||
const router = (this.router as any);
|
||||
|
||||
// Angular >= 7.2.0
|
||||
if (router.getCurrentNavigation) {
|
||||
currentNavigation = router.getCurrentNavigation();
|
||||
|
||||
// Angular < 7.2.0
|
||||
} else if (
|
||||
router.navigations &&
|
||||
router.navigations.value
|
||||
) {
|
||||
currentNavigation = router.navigations.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the navigation action
|
||||
* sets `replaceUrl: true`
|
||||
* then we need to make sure
|
||||
* we remove the last item
|
||||
* from our views stack
|
||||
*/
|
||||
if (
|
||||
currentNavigation &&
|
||||
currentNavigation.extras &&
|
||||
currentNavigation.extras.replaceUrl
|
||||
) {
|
||||
if (this.views.length > 0) {
|
||||
this.views.splice(-1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const reused = this.views.includes(enteringView);
|
||||
const views = this.insertView(enteringView, direction);
|
||||
return this.wait(async () => {
|
||||
await this.transition(enteringView, leavingView, animation, this.canGoBack(1), false);
|
||||
await cleanupAsync(enteringView, views, viewsSnapshot);
|
||||
return {
|
||||
enteringView,
|
||||
direction,
|
||||
animation,
|
||||
tabSwitch
|
||||
};
|
||||
|
||||
// Trigger change detection before transition starts
|
||||
// This will call ngOnInit() the first time too, just after the view
|
||||
// was attached to the dom, but BEFORE the transition starts
|
||||
if (!reused) {
|
||||
enteringView.ref.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
// Wait until previous transitions finish
|
||||
return this.zone.runOutsideAngular(() => {
|
||||
return this.wait(() => {
|
||||
// disconnect leaving page from change detection to
|
||||
// reduce jank during the page transition
|
||||
if (leavingView) {
|
||||
leavingView.ref.changeDetectorRef.detach();
|
||||
}
|
||||
// In case the enteringView is the same as the leavingPage we need to reattach()
|
||||
enteringView.ref.changeDetectorRef.reattach();
|
||||
|
||||
return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false)
|
||||
.then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location))
|
||||
.then(() => ({
|
||||
enteringView,
|
||||
direction,
|
||||
animation,
|
||||
tabSwitch
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -82,32 +139,50 @@ export class StackController {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
const view = views[views.length - deep - 1];
|
||||
return this.navCtrl.navigateBack(view.url).then(() => true);
|
||||
let url = view.url;
|
||||
|
||||
const viewSavedData = view.savedData;
|
||||
if (viewSavedData) {
|
||||
const primaryOutlet = viewSavedData.get('primary');
|
||||
if (
|
||||
primaryOutlet &&
|
||||
primaryOutlet.route &&
|
||||
primaryOutlet.route._routerState &&
|
||||
primaryOutlet.route._routerState.snapshot &&
|
||||
primaryOutlet.route._routerState.snapshot.url
|
||||
) {
|
||||
url = primaryOutlet.route._routerState.snapshot.url;
|
||||
}
|
||||
}
|
||||
|
||||
return this.navCtrl.navigateBack(url, view.savedExtras).then(() => true);
|
||||
});
|
||||
}
|
||||
|
||||
async startBackTransition() {
|
||||
startBackTransition() {
|
||||
const leavingView = this.activeView;
|
||||
if (leavingView) {
|
||||
const views = this.getStack(leavingView.stackId);
|
||||
const enteringView = views[views.length - 2];
|
||||
enteringView.ref.changeDetectorRef.reattach();
|
||||
await this.wait(() => {
|
||||
return this.wait(() => {
|
||||
return this.transition(
|
||||
enteringView, // entering view
|
||||
leavingView, // leaving view
|
||||
'back',
|
||||
true,
|
||||
this.canGoBack(2),
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
endBackTransition(shouldComplete: boolean) {
|
||||
if (shouldComplete) {
|
||||
this.skipTransition = true;
|
||||
this.pop(1);
|
||||
} else if (this.activeView) {
|
||||
cleanup(this.activeView, this.views, this.views, this.location);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +212,7 @@ export class StackController {
|
||||
return this.views.slice();
|
||||
}
|
||||
|
||||
private async transition(
|
||||
private transition(
|
||||
enteringView: RouteView | undefined,
|
||||
leavingView: RouteView | undefined,
|
||||
direction: 'forward' | 'back' | undefined,
|
||||
@@ -146,7 +221,10 @@ export class StackController {
|
||||
) {
|
||||
if (this.skipTransition) {
|
||||
this.skipTransition = false;
|
||||
return;
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
if (leavingView === enteringView) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
const enteringEl = enteringView ? enteringView.element : undefined;
|
||||
const leavingEl = leavingView ? leavingView.element : undefined;
|
||||
@@ -157,15 +235,17 @@ export class StackController {
|
||||
containerEl.appendChild(enteringEl);
|
||||
}
|
||||
|
||||
await containerEl.componentOnReady();
|
||||
await containerEl.commit(enteringEl, leavingEl, {
|
||||
deepWait: true,
|
||||
duration: direction === undefined ? 0 : undefined,
|
||||
direction,
|
||||
showGoBack,
|
||||
progressAnimation
|
||||
});
|
||||
if ((containerEl as any).commit) {
|
||||
return containerEl.commit(enteringEl, leavingEl, {
|
||||
deepWait: true,
|
||||
duration: direction === undefined ? 0 : undefined,
|
||||
direction,
|
||||
showGoBack,
|
||||
progressAnimation
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
private async wait<T>(task: () => Promise<T>): Promise<T> {
|
||||
@@ -178,26 +258,41 @@ export class StackController {
|
||||
}
|
||||
}
|
||||
|
||||
function cleanupAsync(activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[]) {
|
||||
return new Promise(resolve => {
|
||||
requestAnimationFrame(() => {
|
||||
cleanup(activeRoute, views, viewsSnapshot);
|
||||
resolve();
|
||||
const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
|
||||
if (typeof (requestAnimationFrame as any) === 'function') {
|
||||
return new Promise<any>(resolve => {
|
||||
requestAnimationFrame(() => {
|
||||
cleanup(activeRoute, views, viewsSnapshot, location);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
function cleanup(activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[]) {
|
||||
const cleanup = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
|
||||
viewsSnapshot
|
||||
.filter(view => !views.includes(view))
|
||||
.forEach(destroyView);
|
||||
|
||||
views.forEach(view => {
|
||||
if (view !== activeRoute) {
|
||||
/**
|
||||
* In the event that a user navigated multiple
|
||||
* times in rapid succession, we want to make sure
|
||||
* we don't pre-emptively detach a view while
|
||||
* it is in mid-transition.
|
||||
*
|
||||
* In this instance we also do not care about query
|
||||
* params or fragments as it will be the same view regardless
|
||||
*/
|
||||
const locationWithoutParams = location.path().split('?')[0];
|
||||
const locationWithoutFragment = locationWithoutParams.split('#')[0];
|
||||
|
||||
if (view !== activeRoute && view.url !== locationWithoutFragment) {
|
||||
const element = view.element;
|
||||
element.setAttribute('aria-hidden', 'true');
|
||||
element.classList.add('ion-page-hidden');
|
||||
view.ref.changeDetectorRef.detach();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ComponentRef } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
|
||||
import { NavDirection, RouterDirection } from '@ionic/core';
|
||||
|
||||
export function insertView(views: RouteView[], view: RouteView, direction: RouterDirection) {
|
||||
export const insertView = (views: RouteView[], view: RouteView, direction: RouterDirection) => {
|
||||
if (direction === 'root') {
|
||||
return setRoot(views, view);
|
||||
} else if (direction === 'forward') {
|
||||
@@ -10,15 +10,15 @@ export function insertView(views: RouteView[], view: RouteView, direction: Route
|
||||
} else {
|
||||
return setBack(views, view);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function setRoot(views: RouteView[], view: RouteView) {
|
||||
const setRoot = (views: RouteView[], view: RouteView) => {
|
||||
views = views.filter(v => v.stackId !== view.stackId);
|
||||
views.push(view);
|
||||
return views;
|
||||
}
|
||||
};
|
||||
|
||||
function setForward(views: RouteView[], view: RouteView) {
|
||||
const setForward = (views: RouteView[], view: RouteView) => {
|
||||
const index = views.indexOf(view);
|
||||
if (index >= 0) {
|
||||
views = views.filter(v => v.stackId !== view.stackId || v.id <= view.id);
|
||||
@@ -26,30 +26,30 @@ function setForward(views: RouteView[], view: RouteView) {
|
||||
views.push(view);
|
||||
}
|
||||
return views;
|
||||
}
|
||||
};
|
||||
|
||||
function setBack(views: RouteView[], view: RouteView) {
|
||||
const setBack = (views: RouteView[], view: RouteView) => {
|
||||
const index = views.indexOf(view);
|
||||
if (index >= 0) {
|
||||
return views.filter(v => v.stackId !== view.stackId || v.id <= view.id);
|
||||
} else {
|
||||
return setRoot(views, view);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function getUrl(router: Router, activatedRoute: ActivatedRoute) {
|
||||
export const getUrl = (router: Router, activatedRoute: ActivatedRoute) => {
|
||||
const urlTree = router.createUrlTree(['.'], { relativeTo: activatedRoute });
|
||||
return router.serializeUrl(urlTree);
|
||||
}
|
||||
};
|
||||
|
||||
export function isTabSwitch(enteringView: RouteView, leavingView: RouteView | undefined) {
|
||||
export const isTabSwitch = (enteringView: RouteView, leavingView: RouteView | undefined) => {
|
||||
if (!leavingView) {
|
||||
return true;
|
||||
}
|
||||
return enteringView.stackId !== leavingView.stackId;
|
||||
}
|
||||
};
|
||||
|
||||
export function computeStackId(prefixUrl: string[] | undefined, url: string) {
|
||||
export const computeStackId = (prefixUrl: string[] | undefined, url: string) => {
|
||||
if (!prefixUrl) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -63,22 +63,22 @@ export function computeStackId(prefixUrl: string[] | undefined, url: string) {
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export function toSegments(path: string): string[] {
|
||||
export const toSegments = (path: string) => {
|
||||
return path
|
||||
.split('/')
|
||||
.map(s => s.trim())
|
||||
.filter(s => s !== '');
|
||||
}
|
||||
};
|
||||
|
||||
export function destroyView(view: RouteView | undefined) {
|
||||
export const destroyView = (view: RouteView | undefined) => {
|
||||
if (view) {
|
||||
// TODO lifecycle event
|
||||
view.ref.destroy();
|
||||
view.unlistenEvents();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export interface StackEvent {
|
||||
enteringView: RouteView;
|
||||
@@ -94,5 +94,6 @@ export interface RouteView {
|
||||
element: HTMLElement;
|
||||
ref: ComponentRef<any>;
|
||||
savedData?: any;
|
||||
savedExtras?: NavigationExtras;
|
||||
unlistenEvents: () => void;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@ export function proxyInputs(Cmp: any, inputs: string[]) {
|
||||
inputs.forEach(item => {
|
||||
Object.defineProperty(Prototype, item, {
|
||||
get() { return this.el[item]; },
|
||||
set(val: any) { this.el[item] = val; },
|
||||
set(val: any) {
|
||||
this.z.runOutsideAngular(() => this.el[item] = val);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -16,7 +18,7 @@ export function proxyMethods(Cmp: any, methods: string[]) {
|
||||
methods.forEach(methodName => {
|
||||
Prototype[methodName] = function() {
|
||||
const args = arguments;
|
||||
return this.el.componentOnReady().then((el: any) => el[methodName].apply(el, args));
|
||||
return this.z.runOutsideAngular(() => this.el[methodName].apply(this.el, args));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, EmbeddedViewRef, IterableDiffer, IterableDiffers, NgZone, SimpleChanges, TrackByFunction } from '@angular/core';
|
||||
import { Cell, CellType, HeaderFn, ItemHeightFn } from '@ionic/core';
|
||||
import { Cell, CellType, FooterHeightFn, HeaderFn, HeaderHeightFn, ItemHeightFn } from '@ionic/core';
|
||||
|
||||
import { proxyInputs, proxyMethods } from '../proxies-utils';
|
||||
|
||||
@@ -75,7 +75,7 @@ export declare interface IonVirtualScroll {
|
||||
|
||||
/**
|
||||
* An optional function that maps each item within their height.
|
||||
* When this function is provides, heavy optimizations and fast path can be taked by
|
||||
* When this function is provided, heavy optimizations and fast path can be taked by
|
||||
* `ion-virtual-scroll` leading to massive performance improvements.
|
||||
*
|
||||
* This function allows to skip all DOM reads, which can be Doing so leads
|
||||
@@ -83,6 +83,16 @@ export declare interface IonVirtualScroll {
|
||||
*/
|
||||
itemHeight?: ItemHeightFn;
|
||||
|
||||
/**
|
||||
* An optional function that maps each item header within their height.
|
||||
*/
|
||||
headerHeight?: HeaderHeightFn;
|
||||
|
||||
/**
|
||||
* An optional function that maps each item footer within their height.
|
||||
*/
|
||||
footerHeight?: FooterHeightFn;
|
||||
|
||||
/**
|
||||
* Same as `ngForTrackBy` which can be used on `ngFor`.
|
||||
*/
|
||||
@@ -114,6 +124,8 @@ export declare interface IonVirtualScroll {
|
||||
'footerFn',
|
||||
'items',
|
||||
'itemHeight',
|
||||
'headerHeight',
|
||||
'footerHeight',
|
||||
'trackBy'
|
||||
]
|
||||
})
|
||||
@@ -128,7 +140,7 @@ export class IonVirtualScroll {
|
||||
@ContentChild(VirtualFooter) ftrTmp!: VirtualFooter;
|
||||
|
||||
constructor(
|
||||
private zone: NgZone,
|
||||
private z: NgZone,
|
||||
private iterableDiffers: IterableDiffers,
|
||||
elementRef: ElementRef,
|
||||
) {
|
||||
@@ -162,7 +174,7 @@ export class IonVirtualScroll {
|
||||
}
|
||||
|
||||
private nodeRender(el: HTMLElement | null, cell: Cell, index: number): HTMLElement {
|
||||
return this.zone.run(() => {
|
||||
return this.z.run(() => {
|
||||
let node: EmbeddedViewRef<VirtualContext>;
|
||||
if (!el) {
|
||||
node = this.itmTmp.viewContainer.createEmbeddedView(
|
||||
@@ -194,7 +206,7 @@ export class IonVirtualScroll {
|
||||
}
|
||||
}
|
||||
|
||||
function getElement(view: EmbeddedViewRef<VirtualContext>): HTMLElement {
|
||||
const getElement = (view: EmbeddedViewRef<VirtualContext>): HTMLElement => {
|
||||
const rootNodes = view.rootNodes;
|
||||
for (let i = 0; i < rootNodes.length; i++) {
|
||||
if (rootNodes[i].nodeType === 1) {
|
||||
@@ -202,7 +214,7 @@ function getElement(view: EmbeddedViewRef<VirtualContext>): HTMLElement {
|
||||
}
|
||||
}
|
||||
throw new Error('virtual element was not created');
|
||||
}
|
||||
};
|
||||
|
||||
proxyInputs(IonVirtualScroll, [
|
||||
'approxItemHeight',
|
||||
@@ -211,7 +223,9 @@ proxyInputs(IonVirtualScroll, [
|
||||
'headerFn',
|
||||
'footerFn',
|
||||
'items',
|
||||
'itemHeight'
|
||||
'itemHeight',
|
||||
'headerHeight',
|
||||
'footerHeight'
|
||||
]);
|
||||
|
||||
proxyMethods(IonVirtualScroll, [
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
|
||||
import { CommonModule, DOCUMENT } from '@angular/common';
|
||||
import { APP_INITIALIZER, ModuleWithProviders, NgModule, NgZone } from '@angular/core';
|
||||
import { IonicConfig } from '@ionic/core';
|
||||
|
||||
import { appInitialize } from './app-initialize';
|
||||
@@ -141,7 +141,9 @@ export class IonicModule {
|
||||
useFactory: appInitialize,
|
||||
multi: true,
|
||||
deps: [
|
||||
ConfigToken
|
||||
ConfigToken,
|
||||
DOCUMENT,
|
||||
NgZone
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActionSheetOptions } from '@ionic/core';
|
||||
import { ActionSheetOptions, actionSheetController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@@ -8,6 +8,6 @@ import { OverlayBaseController } from '../util/overlay';
|
||||
})
|
||||
export class ActionSheetController extends OverlayBaseController<ActionSheetOptions, HTMLIonActionSheetElement> {
|
||||
constructor() {
|
||||
super('ion-action-sheet-controller');
|
||||
super(actionSheetController);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AlertOptions } from '@ionic/core';
|
||||
import { AlertOptions, alertController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@@ -8,6 +8,6 @@ import { OverlayBaseController } from '../util/overlay';
|
||||
})
|
||||
export class AlertController extends OverlayBaseController<AlertOptions, HTMLIonAlertElement> {
|
||||
constructor() {
|
||||
super('ion-alert-controller');
|
||||
super(alertController);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,10 +34,10 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
|
||||
) {}
|
||||
|
||||
attachViewToDom(container: any, component: any, params?: any, cssClasses?: string[]): Promise<any> {
|
||||
return new Promise(resolve => {
|
||||
this.zone.run(() => {
|
||||
return this.zone.run(() => {
|
||||
return new Promise(resolve => {
|
||||
const el = attachView(
|
||||
this.resolver, this.injector, this.location, this.appRef,
|
||||
this.zone, this.resolver, this.injector, this.location, this.appRef,
|
||||
this.elRefMap, this.elEventsMap,
|
||||
container, component, params, cssClasses
|
||||
);
|
||||
@@ -47,8 +47,8 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
|
||||
}
|
||||
|
||||
removeViewFromDom(_container: any, component: any): Promise<void> {
|
||||
return new Promise(resolve => {
|
||||
this.zone.run(() => {
|
||||
return this.zone.run(() => {
|
||||
return new Promise(resolve => {
|
||||
const componentRef = this.elRefMap.get(component);
|
||||
if (componentRef) {
|
||||
componentRef.destroy();
|
||||
@@ -65,7 +65,8 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
export function attachView(
|
||||
export const attachView = (
|
||||
zone: NgZone,
|
||||
resolver: ComponentFactoryResolver,
|
||||
injector: Injector,
|
||||
location: ViewContainerRef | undefined,
|
||||
@@ -73,7 +74,7 @@ export function attachView(
|
||||
elRefMap: WeakMap<HTMLElement, any>,
|
||||
elEventsMap: WeakMap<HTMLElement, () => void>,
|
||||
container: any, component: any, params: any, cssClasses: string[] | undefined
|
||||
) {
|
||||
) => {
|
||||
const factory = resolver.resolveComponentFactory(component);
|
||||
const childInjector = Injector.create({
|
||||
providers: getProviders(params),
|
||||
@@ -93,7 +94,7 @@ export function attachView(
|
||||
hostElement.classList.add(clazz);
|
||||
}
|
||||
}
|
||||
const unbindEvents = bindLifecycleEvents(instance, hostElement);
|
||||
const unbindEvents = bindLifecycleEvents(zone, instance, hostElement);
|
||||
container.appendChild(hostElement);
|
||||
|
||||
if (!location) {
|
||||
@@ -103,7 +104,7 @@ export function attachView(
|
||||
elRefMap.set(hostElement, componentRef);
|
||||
elEventsMap.set(hostElement, unbindEvents);
|
||||
return hostElement;
|
||||
}
|
||||
};
|
||||
|
||||
const LIFECYCLES = [
|
||||
LIFECYCLE_WILL_ENTER,
|
||||
@@ -113,26 +114,22 @@ const LIFECYCLES = [
|
||||
LIFECYCLE_WILL_UNLOAD
|
||||
];
|
||||
|
||||
export function bindLifecycleEvents(instance: any, element: HTMLElement) {
|
||||
const unregisters = LIFECYCLES.map(eventName => {
|
||||
const handler = (ev: any) => {
|
||||
if (typeof instance[eventName] === 'function') {
|
||||
instance[eventName](ev.detail);
|
||||
}
|
||||
};
|
||||
element.addEventListener(eventName, handler);
|
||||
return () => {
|
||||
element.removeEventListener(eventName, handler);
|
||||
};
|
||||
export const bindLifecycleEvents = (zone: NgZone, instance: any, element: HTMLElement) => {
|
||||
return zone.run(() => {
|
||||
const unregisters = LIFECYCLES
|
||||
.filter(eventName => typeof instance[eventName] === 'function')
|
||||
.map(eventName => {
|
||||
const handler = (ev: any) => instance[eventName](ev.detail);
|
||||
element.addEventListener(eventName, handler);
|
||||
return () => element.removeEventListener(eventName, handler);
|
||||
});
|
||||
return () => unregisters.forEach(fn => fn());
|
||||
});
|
||||
return () => {
|
||||
unregisters.forEach(fn => fn());
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const NavParamsToken = new InjectionToken<any>('NavParamsToken');
|
||||
|
||||
function getProviders(params: {[key: string]: any}) {
|
||||
const getProviders = (params: {[key: string]: any}) => {
|
||||
return [
|
||||
{
|
||||
provide: NavParamsToken, useValue: params
|
||||
@@ -141,8 +138,8 @@ function getProviders(params: {[key: string]: any}) {
|
||||
provide: NavParams, useFactory: provideNavParamsInjectable, deps: [NavParamsToken]
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
function provideNavParamsInjectable(params: {[key: string]: any}) {
|
||||
const provideNavParamsInjectable = (params: {[key: string]: any}) => {
|
||||
return new NavParams(params);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -42,13 +42,12 @@ export class Config {
|
||||
|
||||
export const ConfigToken = new InjectionToken<any>('USERCONFIG');
|
||||
|
||||
function getConfig(): CoreConfig | null {
|
||||
const win: IonicWindow | undefined = window as any;
|
||||
if (typeof win !== 'undefined') {
|
||||
const Ionic = win.Ionic;
|
||||
const getConfig = (): CoreConfig | null => {
|
||||
if (typeof (window as any) !== 'undefined') {
|
||||
const Ionic = (window as IonicWindow).Ionic;
|
||||
if (Ionic && Ionic.config) {
|
||||
return Ionic.config;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -22,16 +22,25 @@ export class DomController {
|
||||
}
|
||||
}
|
||||
|
||||
function getQueue() {
|
||||
const Ionic = (window as any).Ionic;
|
||||
if (Ionic && Ionic.queue) {
|
||||
return Ionic.queue;
|
||||
const getQueue = () => {
|
||||
const win = typeof (window as any) !== 'undefined' ? window : null as any;
|
||||
|
||||
if (win != null) {
|
||||
const Ionic = win.Ionic;
|
||||
if (Ionic && Ionic.queue) {
|
||||
return Ionic.queue;
|
||||
}
|
||||
|
||||
return {
|
||||
read: (cb: any) => win.requestAnimationFrame(cb),
|
||||
write: (cb: any) => win.requestAnimationFrame(cb)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
read: (cb: any) => window.requestAnimationFrame(cb),
|
||||
write: (cb: any) => window.requestAnimationFrame(cb)
|
||||
read: (cb: any) => cb(),
|
||||
write: (cb: any) => cb()
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export type RafCallback = (timeStamp?: number) => void;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { LoadingOptions } from '@ionic/core';
|
||||
import { LoadingOptions, loadingController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@@ -8,6 +8,6 @@ import { OverlayBaseController } from '../util/overlay';
|
||||
})
|
||||
export class LoadingController extends OverlayBaseController<LoadingOptions, HTMLIonLoadingElement> {
|
||||
constructor() {
|
||||
super('ion-loading-controller');
|
||||
super(loadingController);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
|
||||
import { proxyMethod } from '../util/util';
|
||||
|
||||
@@ -8,13 +9,16 @@ const CTRL = 'ion-menu-controller';
|
||||
})
|
||||
export class MenuController {
|
||||
|
||||
constructor(@Inject(DOCUMENT) private doc: any) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Programmatically open the Menu.
|
||||
* @param [menuId] Optionally get the menu by its id, or side.
|
||||
* @return returns a promise when the menu is fully opened
|
||||
*/
|
||||
open(menuId?: string): Promise<boolean> {
|
||||
return proxyMethod(CTRL, 'open', menuId);
|
||||
return proxyMethod(CTRL, this.doc, 'open', menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,7 +29,7 @@ export class MenuController {
|
||||
* @return returns a promise when the menu is fully closed
|
||||
*/
|
||||
close(menuId?: string): Promise<boolean> {
|
||||
return proxyMethod(CTRL, 'close', menuId);
|
||||
return proxyMethod(CTRL, this.doc, 'close', menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,7 +39,7 @@ export class MenuController {
|
||||
* @return returns a promise when the menu has been toggled
|
||||
*/
|
||||
toggle(menuId?: string): Promise<boolean> {
|
||||
return proxyMethod(CTRL, 'toggle', menuId);
|
||||
return proxyMethod(CTRL, this.doc, 'toggle', menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,7 +51,19 @@ export class MenuController {
|
||||
* @return Returns the instance of the menu, which is useful for chaining.
|
||||
*/
|
||||
enable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement> {
|
||||
return proxyMethod(CTRL, 'enable', shouldEnable, menuId);
|
||||
return proxyMethod(CTRL, this.doc, 'enable', shouldEnable, menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to enable or disable the ability to swipe open the menu.
|
||||
* @param shouldEnable True if it should be swipe-able, false if not.
|
||||
* @param [menuId] Optionally get the menu by its id, or side.
|
||||
* @return Returns the instance of the menu, which is useful for chaining.
|
||||
* @deprecated Use swipeGesture() instead
|
||||
*/
|
||||
swipeEnable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement> {
|
||||
console.warn('MenuController.swipeEnable is deprecated. Use MenuController.swipeGesture() instead');
|
||||
return this.swipeGesture(shouldEnable, menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,8 +72,8 @@ export class MenuController {
|
||||
* @param [menuId] Optionally get the menu by its id, or side.
|
||||
* @return Returns the instance of the menu, which is useful for chaining.
|
||||
*/
|
||||
swipeEnable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement> {
|
||||
return proxyMethod(CTRL, 'swipeEnable', shouldEnable, menuId);
|
||||
swipeGesture(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement> {
|
||||
return proxyMethod(CTRL, this.doc, 'swipeGesture', shouldEnable, menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +82,7 @@ export class MenuController {
|
||||
* If the menuId is not specified, it returns true if ANY menu is currenly open.
|
||||
*/
|
||||
isOpen(menuId?: string): Promise<boolean> {
|
||||
return proxyMethod(CTRL, 'isOpen', menuId);
|
||||
return proxyMethod(CTRL, this.doc, 'isOpen', menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +90,7 @@ export class MenuController {
|
||||
* @return Returns true if the menu is currently enabled, otherwise false.
|
||||
*/
|
||||
isEnabled(menuId?: string): Promise<boolean> {
|
||||
return proxyMethod(CTRL, 'isEnabled', menuId);
|
||||
return proxyMethod(CTRL, this.doc, 'isEnabled', menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,20 +103,20 @@ export class MenuController {
|
||||
* @return Returns the instance of the menu if found, otherwise `null`.
|
||||
*/
|
||||
get(menuId?: string): Promise<HTMLIonMenuElement> {
|
||||
return proxyMethod(CTRL, 'get', menuId);
|
||||
return proxyMethod(CTRL, this.doc, 'get', menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the instance of the menu already opened, otherwise `null`.
|
||||
*/
|
||||
getOpen(): Promise<HTMLIonMenuElement> {
|
||||
return proxyMethod(CTRL, 'getOpen');
|
||||
return proxyMethod(CTRL, this.doc, 'getOpen');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns an array of all menu instances.
|
||||
*/
|
||||
getMenus(): Promise<HTMLIonMenuElement[]> {
|
||||
return proxyMethod(CTRL, 'getMenus');
|
||||
return proxyMethod(CTRL, this.doc, 'getMenus');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
|
||||
import { ModalOptions } from '@ionic/core';
|
||||
import { ModalOptions, modalController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@@ -13,7 +13,7 @@ export class ModalController extends OverlayBaseController<ModalOptions, HTMLIon
|
||||
private resolver: ComponentFactoryResolver,
|
||||
private injector: Injector,
|
||||
) {
|
||||
super('ion-modal-controller');
|
||||
super(modalController);
|
||||
}
|
||||
|
||||
create(opts: ModalOptions): Promise<HTMLIonModalElement> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { Injectable, Optional } from '@angular/core';
|
||||
import { NavigationExtras, NavigationStart, Router, UrlTree } from '@angular/router';
|
||||
import { NavigationExtras, NavigationStart, Router, UrlSerializer, UrlTree } from '@angular/router';
|
||||
import { NavDirection, RouterDirection } from '@ionic/core';
|
||||
|
||||
import { IonRouterOutlet } from '../directives/navigation/ion-router-outlet';
|
||||
@@ -29,6 +29,7 @@ export class NavController {
|
||||
constructor(
|
||||
platform: Platform,
|
||||
private location: Location,
|
||||
private serializer: UrlSerializer,
|
||||
@Optional() private router?: Router,
|
||||
) {
|
||||
// Subscribe to router events to detect direction
|
||||
@@ -49,12 +50,12 @@ export class NavController {
|
||||
|
||||
/**
|
||||
* This method uses Angular's [Router](https://angular.io/api/router/Router) under the hood,
|
||||
* it's equivalent to call `this.router.navigateByUrl()`, but it's explicit about the **direction** of the transition.
|
||||
* it's equivalent to calling `this.router.navigateByUrl()`, but it's explicit about the **direction** of the transition.
|
||||
*
|
||||
* Going **forward** means that a new page it's going to be pushed to the stack of the outlet (ion-router-outlet),
|
||||
* Going **forward** means that a new page is going to be pushed to the stack of the outlet (ion-router-outlet),
|
||||
* and that it will show a "forward" animation by default.
|
||||
*
|
||||
* Navigating forward can also be trigger in a declarative manner by using the `[routerDirection]` directive:
|
||||
* Navigating forward can also be triggered in a declarative manner by using the `[routerDirection]` directive:
|
||||
*
|
||||
* ```html
|
||||
* <a routerLink="/path/to/page" routerDirection="forward">Link</a>
|
||||
@@ -67,17 +68,17 @@ export class NavController {
|
||||
|
||||
/**
|
||||
* This method uses Angular's [Router](https://angular.io/api/router/Router) under the hood,
|
||||
* it's equivalent to call:
|
||||
* it's equivalent to calling:
|
||||
*
|
||||
* ```ts
|
||||
* this.navController.setDirection('back');
|
||||
* this.router.navigateByUrl(path);
|
||||
* ```
|
||||
*
|
||||
* Going **back** means that all the pages in the stack until the navigated page is found will be pop,
|
||||
* Going **back** means that all the pages in the stack until the navigated page is found will be popped,
|
||||
* and that it will show a "back" animation by default.
|
||||
*
|
||||
* Navigating back can also be trigger in a declarative manner by using the `[routerDirection]` directive:
|
||||
* Navigating back can also be triggered in a declarative manner by using the `[routerDirection]` directive:
|
||||
*
|
||||
* ```html
|
||||
* <a routerLink="/path/to/page" routerDirection="back">Link</a>
|
||||
@@ -90,7 +91,7 @@ export class NavController {
|
||||
|
||||
/**
|
||||
* This method uses Angular's [Router](https://angular.io/api/router/Router) under the hood,
|
||||
* it's equivalent to call:
|
||||
* it's equivalent to calling:
|
||||
*
|
||||
* ```ts
|
||||
* this.navController.setDirection('root');
|
||||
@@ -100,7 +101,7 @@ export class NavController {
|
||||
* Going **root** means that all existing pages in the stack will be removed,
|
||||
* and the navigated page will become the single page in the stack.
|
||||
*
|
||||
* Navigating root can also be trigger in a declarative manner by using the `[routerDirection]` directive:
|
||||
* Navigating root can also be triggered in a declarative manner by using the `[routerDirection]` directive:
|
||||
*
|
||||
* ```html
|
||||
* <a routerLink="/path/to/page" routerDirection="root">Link</a>
|
||||
@@ -113,7 +114,8 @@ export class NavController {
|
||||
|
||||
/**
|
||||
* Same as [Location](https://angular.io/api/common/Location)'s back() method.
|
||||
* It will use the standard `window.history.back()` under the hood, but featuring a `back` animation.
|
||||
* It will use the standard `window.history.back()` under the hood, but featuring a `back` animation
|
||||
* by default.
|
||||
*/
|
||||
back(options: AnimationOptions = { animated: true, animationDirection: 'back' }) {
|
||||
this.setDirection('back', options.animated, options.animationDirection);
|
||||
@@ -121,9 +123,9 @@ export class NavController {
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods goes back in the context of ionic's stack navigation.
|
||||
* This methods goes back in the context of Ionic's stack navigation.
|
||||
*
|
||||
* It recursivelly finds the top active `ion-router-outlet` and calls `pop()`.
|
||||
* It recursively finds the top active `ion-router-outlet` and calls `pop()`.
|
||||
* This is the recommended way to go back when you are using `ion-router-outlet`.
|
||||
*/
|
||||
async pop() {
|
||||
@@ -139,11 +141,11 @@ export class NavController {
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods specifies the direction of the next navigation performed by the angular router.
|
||||
* This methods specifies the direction of the next navigation performed by the Angular router.
|
||||
*
|
||||
* `setDirection()` does not trigger any transition, it just sets a set of flags to be consumed by `ion-router-outlet`.
|
||||
* `setDirection()` does not trigger any transition, it just sets some flags to be consumed by `ion-router-outlet`.
|
||||
*
|
||||
* It's recommended to use `navigateForward()`, `navigateBack()` and `navigateBack()` instead of `setDirection()`.
|
||||
* It's recommended to use `navigateForward()`, `navigateBack()` and `navigateRoot()` instead of `setDirection()`.
|
||||
*/
|
||||
setDirection(direction: RouterDirection, animated?: boolean, animationDirection?: 'forward' | 'back') {
|
||||
this.direction = direction;
|
||||
@@ -184,12 +186,34 @@ export class NavController {
|
||||
if (Array.isArray(url)) {
|
||||
return this.router!.navigate(url, options);
|
||||
} else {
|
||||
return this.router!.navigateByUrl(url, options);
|
||||
|
||||
/**
|
||||
* navigateByUrl ignores any properties that
|
||||
* would change the url, so things like queryParams
|
||||
* would be ignored unless we create a url tree
|
||||
* More Info: https://github.com/angular/angular/issues/18798
|
||||
*/
|
||||
const urlTree = this.serializer.parse(url.toString());
|
||||
|
||||
if (options.queryParams !== undefined) {
|
||||
urlTree.queryParams = { ...options.queryParams };
|
||||
}
|
||||
|
||||
if (options.fragment !== undefined) {
|
||||
urlTree.fragment = options.fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* `navigateByUrl` will still apply `NavigationExtras` properties
|
||||
* that do not modify the url, such as `replaceUrl` which is why
|
||||
* `options` is passed in here.
|
||||
*/
|
||||
return this.router!.navigateByUrl(urlTree, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getAnimation(direction: RouterDirection, animated: boolean | undefined, animationDirection: 'forward' | 'back' | undefined): NavDirection | undefined {
|
||||
const getAnimation = (direction: RouterDirection, animated: boolean | undefined, animationDirection: 'forward' | 'back' | undefined): NavDirection | undefined => {
|
||||
if (animated === false) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -202,7 +226,7 @@ function getAnimation(direction: RouterDirection, animated: boolean | undefined,
|
||||
return 'forward';
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const DEFAULT_DIRECTION = 'auto';
|
||||
const DEFAULT_ANIMATED = undefined;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { PickerOptions } from '@ionic/core';
|
||||
import { PickerOptions, pickerController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@@ -8,6 +8,6 @@ import { OverlayBaseController } from '../util/overlay';
|
||||
})
|
||||
export class PickerController extends OverlayBaseController<PickerOptions, HTMLIonPickerElement> {
|
||||
constructor() {
|
||||
super('ion-picker-controller');
|
||||
super(pickerController);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { Inject, Injectable, NgZone } from '@angular/core';
|
||||
import { BackButtonEventDetail, Platforms, getPlatforms, isPlatform } from '@ionic/core';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
|
||||
@@ -12,6 +13,7 @@ export interface BackButtonEmitter extends Subject<BackButtonEventDetail> {
|
||||
export class Platform {
|
||||
|
||||
private _readyPromise: Promise<string>;
|
||||
private win: any;
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
@@ -40,27 +42,30 @@ export class Platform {
|
||||
*/
|
||||
resize = new Subject<void>();
|
||||
|
||||
constructor() {
|
||||
this.backButton.subscribeWithPriority = function(priority, callback) {
|
||||
return this.subscribe(ev => {
|
||||
ev.register(priority, callback);
|
||||
});
|
||||
};
|
||||
constructor(@Inject(DOCUMENT) private doc: any, zone: NgZone) {
|
||||
zone.run(() => {
|
||||
this.win = doc.defaultView;
|
||||
this.backButton.subscribeWithPriority = function(priority, callback) {
|
||||
return this.subscribe(ev => (
|
||||
ev.register(priority, () => zone.run(callback))
|
||||
));
|
||||
};
|
||||
|
||||
proxyEvent(this.pause, document, 'pause');
|
||||
proxyEvent(this.resume, document, 'resume');
|
||||
proxyEvent(this.backButton, document, 'ionBackButton');
|
||||
proxyEvent(this.resize, window, 'resize');
|
||||
proxyEvent(this.pause, doc, 'pause');
|
||||
proxyEvent(this.resume, doc, 'resume');
|
||||
proxyEvent(this.backButton, doc, 'ionBackButton');
|
||||
proxyEvent(this.resize, this.win, 'resize');
|
||||
|
||||
let readyResolve: (value: string) => void;
|
||||
this._readyPromise = new Promise(res => { readyResolve = res; });
|
||||
if ((window as any)['cordova']) {
|
||||
document.addEventListener('deviceready', () => {
|
||||
readyResolve('cordova');
|
||||
}, { once: true });
|
||||
} else {
|
||||
readyResolve!('dom');
|
||||
}
|
||||
let readyResolve: (value: string) => void;
|
||||
this._readyPromise = new Promise(res => { readyResolve = res; });
|
||||
if (this.win && this.win['cordova']) {
|
||||
doc.addEventListener('deviceready', () => {
|
||||
readyResolve('cordova');
|
||||
}, { once: true });
|
||||
} else {
|
||||
readyResolve!('dom');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +111,7 @@ export class Platform {
|
||||
*
|
||||
*/
|
||||
is(platformName: Platforms): boolean {
|
||||
return isPlatform(window, platformName);
|
||||
return isPlatform(this.win, platformName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +134,7 @@ export class Platform {
|
||||
* ```
|
||||
*/
|
||||
platforms(): string[] {
|
||||
return getPlatforms(window);
|
||||
return getPlatforms(this.win);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,14 +177,14 @@ export class Platform {
|
||||
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
|
||||
*/
|
||||
get isRTL(): boolean {
|
||||
return document.dir === 'rtl';
|
||||
return this.doc.dir === 'rtl';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query string parameter
|
||||
*/
|
||||
getQueryParam(key: string): string | null {
|
||||
return readQueryParam(window.location.href, key);
|
||||
return readQueryParam(this.win.location.href, key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,45 +198,48 @@ export class Platform {
|
||||
* Returns `true` if the app is in portait mode.
|
||||
*/
|
||||
isPortrait(): boolean {
|
||||
return window.matchMedia('(orientation: portrait)').matches;
|
||||
return this.win.matchMedia && this.win.matchMedia('(orientation: portrait)').matches;
|
||||
}
|
||||
|
||||
testUserAgent(expression: string): boolean {
|
||||
return navigator.userAgent.indexOf(expression) >= 0;
|
||||
const nav = this.win.navigator;
|
||||
return !!(nav && nav.userAgent && nav.userAgent.indexOf(expression) >= 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current url.
|
||||
*/
|
||||
url() {
|
||||
return window.location.href;
|
||||
return this.win.location.href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width of the platform's viewport using `window.innerWidth`.
|
||||
*/
|
||||
width() {
|
||||
return window.innerWidth;
|
||||
return this.win.innerWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the height of the platform's viewport using `window.innerHeight`.
|
||||
*/
|
||||
height(): number {
|
||||
return window.innerHeight;
|
||||
return this.win.innerHeight;
|
||||
}
|
||||
}
|
||||
|
||||
function readQueryParam(url: string, key: string) {
|
||||
const readQueryParam = (url: string, key: string) => {
|
||||
key = key.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
|
||||
const regex = new RegExp('[\\?&]' + key + '=([^&#]*)');
|
||||
const results = regex.exec(url);
|
||||
return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null;
|
||||
}
|
||||
};
|
||||
|
||||
function proxyEvent<T>(emitter: Subject<T>, el: EventTarget, eventName: string) {
|
||||
el.addEventListener(eventName, (ev: Event | undefined | null) => {
|
||||
// ?? cordova might emit "null" events
|
||||
emitter.next(ev != null ? (ev as any).detail as T : undefined);
|
||||
});
|
||||
}
|
||||
const proxyEvent = <T>(emitter: Subject<T>, el: EventTarget, eventName: string) => {
|
||||
if ((el as any)) {
|
||||
el.addEventListener(eventName, (ev: Event | undefined | null) => {
|
||||
// ?? cordova might emit "null" events
|
||||
emitter.next(ev != null ? (ev as any).detail as T : undefined);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
|
||||
import { PopoverOptions } from '@ionic/core';
|
||||
import { PopoverOptions, popoverController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@@ -13,7 +13,7 @@ export class PopoverController extends OverlayBaseController<PopoverOptions, HTM
|
||||
private resolver: ComponentFactoryResolver,
|
||||
private injector: Injector,
|
||||
) {
|
||||
super('ion-popover-controller');
|
||||
super(popoverController);
|
||||
}
|
||||
|
||||
create(opts: PopoverOptions): Promise<HTMLIonPopoverElement> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ToastOptions } from '@ionic/core';
|
||||
import { ToastOptions, toastController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
|
||||
@@ -8,6 +8,6 @@ import { OverlayBaseController } from '../util/overlay';
|
||||
})
|
||||
export class ToastController extends OverlayBaseController<ToastOptions, HTMLIonToastElement> {
|
||||
constructor() {
|
||||
super('ion-toast-controller');
|
||||
super(toastController);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* Ionic Variables and Theming. */
|
||||
/* This is just a placeholder file For more info, please see: */
|
||||
/* https://beta.ionicframework.com/docs/theming/basics */
|
||||
/* https://ionicframework.com/docs/theming/basics */
|
||||
|
||||
/* To quickly generate your own theme, check out the color generator */
|
||||
/* https://beta.ionicframework.com/docs/theming/color-generator */
|
||||
/* https://ionicframework.com/docs/theming/color-generator */
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
import {
|
||||
Rule,
|
||||
SchematicContext,
|
||||
SchematicsException,
|
||||
Tree,
|
||||
apply,
|
||||
chain,
|
||||
mergeWith,
|
||||
move,
|
||||
template,
|
||||
url
|
||||
} from '@angular-devkit/schematics';
|
||||
import { Path, join } from '@angular-devkit/core';
|
||||
import { join, Path } from '@angular-devkit/core';
|
||||
import { apply, chain, mergeWith, move, Rule, SchematicContext, SchematicsException, template, Tree, url } from '@angular-devkit/schematics';
|
||||
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
|
||||
import { addPackageToPackageJson } from './../utils/package';
|
||||
import { addModuleImportToRootModule } from './../utils/ast';
|
||||
import { addStyle, getWorkspace, addArchitectBuilder } from './../utils/config';
|
||||
import { addArchitectBuilder, addStyle, getWorkspace, addAsset } from './../utils/config';
|
||||
import { addPackageToPackageJson } from './../utils/package';
|
||||
import { Schema as IonAddOptions } from './schema';
|
||||
|
||||
function addIonicAngularToPackageJson(): Rule {
|
||||
@@ -36,10 +25,11 @@ function addIonicAngularToolkitToPackageJson(): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
function addIonicAngularModuleToAppModule(): Rule {
|
||||
function addIonicAngularModuleToAppModule(projectSourceRoot): Rule {
|
||||
return (host: Tree) => {
|
||||
addModuleImportToRootModule(
|
||||
host,
|
||||
projectSourceRoot,
|
||||
'IonicModule.forRoot()',
|
||||
'@ionic/angular'
|
||||
);
|
||||
@@ -67,6 +57,18 @@ function addIonicStyles(): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
function addIonicons(): Rule {
|
||||
return (host: Tree) => {
|
||||
const ioniconsGlob = {
|
||||
glob: '**/*.svg',
|
||||
input: 'node_modules/ionicons/dist/ionicons/svg',
|
||||
output: './svg'
|
||||
};
|
||||
addAsset(host, ioniconsGlob);
|
||||
return host;
|
||||
};
|
||||
}
|
||||
|
||||
function addIonicBuilder(): Rule {
|
||||
return (host: Tree) => {
|
||||
addArchitectBuilder(host, 'ionic-cordova-serve', {
|
||||
@@ -125,9 +127,10 @@ export default function ngAdd(options: IonAddOptions): Rule {
|
||||
// @ionic/angular
|
||||
addIonicAngularToPackageJson(),
|
||||
addIonicAngularToolkitToPackageJson(),
|
||||
addIonicAngularModuleToAppModule(),
|
||||
addIonicAngularModuleToAppModule(sourcePath),
|
||||
addIonicBuilder(),
|
||||
addIonicStyles(),
|
||||
addIonicons(),
|
||||
mergeWith(rootTemplateSource),
|
||||
// install freshly added dependencies
|
||||
installNodeDeps()
|
||||
|
||||
@@ -27,12 +27,13 @@ export function getSourceFile(host: Tree, path: string): ts.SourceFile {
|
||||
*/
|
||||
export function addModuleImportToRootModule(
|
||||
host: Tree,
|
||||
projectSourceRoot,
|
||||
moduleName: string,
|
||||
importSrc: string
|
||||
) {
|
||||
addModuleImportToModule(
|
||||
host,
|
||||
normalize(`src/app/app.module.ts`),
|
||||
normalize(`${projectSourceRoot}/app/app.module.ts`),
|
||||
moduleName,
|
||||
importSrc
|
||||
);
|
||||
|
||||
@@ -64,6 +64,18 @@ export function addStyle(host: Tree, stylePath: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function addAsset(host: Tree, asset: string | {glob: string; input: string; output: string}) {
|
||||
const config = readConfig(host);
|
||||
const appConfig = getAngularAppConfig(config);
|
||||
|
||||
if (appConfig) {
|
||||
appConfig.architect.build.options.assets.push(asset);
|
||||
writeConfig(host, config);
|
||||
} else {
|
||||
throw new SchematicsException(`Cannot find valid app`);
|
||||
}
|
||||
}
|
||||
|
||||
export function addArchitectBuilder(host: Tree, builderName: string, builderOpts: any){
|
||||
const config = readConfig(host);
|
||||
const appConfig = getAngularAppConfig(config);
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
|
||||
export interface IonicGlobal {
|
||||
config?: any;
|
||||
ael?: (elm: any, eventName: string, cb: (ev: Event) => void, opts: any) => void;
|
||||
raf?: (ts: number) => void;
|
||||
rel?: (elm: any, eventName: string, cb: (ev: Event) => void, opts: any) => void;
|
||||
asyncQueue?: boolean;
|
||||
}
|
||||
|
||||
export interface IonicWindow extends Window {
|
||||
Ionic: IonicGlobal;
|
||||
__zone_symbol__requestAnimationFrame: (ts: number) => void;
|
||||
__zone_symbol__requestAnimationFrame?: (ts: FrameRequestCallback) => number;
|
||||
}
|
||||
|
||||
export interface HTMLStencilElement extends HTMLElement {
|
||||
componentOnReady(): Promise<this>;
|
||||
forceUpdate(): void;
|
||||
}
|
||||
|
||||
@@ -25,9 +25,6 @@ export class IonicRouteStrategy implements RouteReuseStrategy {
|
||||
if (future.routeConfig !== curr.routeConfig) {
|
||||
return false;
|
||||
}
|
||||
if (future.component !== curr.component) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// checking router params
|
||||
const futureParams = future.params;
|
||||
|
||||
@@ -1,26 +1,32 @@
|
||||
import { proxyMethod } from './util';
|
||||
|
||||
export class OverlayBaseController<Opts, Overlay> {
|
||||
constructor(private ctrl: string) {}
|
||||
interface ControllerShape<Opts, HTMLElm> {
|
||||
create(options: Opts): Promise<HTMLElm>;
|
||||
dismiss(data?: any, role?: string, id?: string): Promise<boolean>;
|
||||
getTop(): Promise<HTMLElm | undefined>;
|
||||
}
|
||||
|
||||
export class OverlayBaseController<Opts, Overlay> implements ControllerShape<Opts, Overlay> {
|
||||
constructor(private ctrl: ControllerShape<Opts, Overlay>) {}
|
||||
|
||||
/**
|
||||
* Creates a new overlay
|
||||
*/
|
||||
create(opts?: Opts): Promise<Overlay> {
|
||||
return proxyMethod(this.ctrl, 'create', opts);
|
||||
create(opts?: Opts) {
|
||||
// TODO: next major release opts is not optional
|
||||
return this.ctrl.create((opts || {}) as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* When `id` is not provided, it dismisses the top overlay.
|
||||
*/
|
||||
dismiss(data?: any, role?: string, id?: string): Promise<void> {
|
||||
return proxyMethod(this.ctrl, 'dismiss', data, role, id);
|
||||
dismiss(data?: any, role?: string, id?: string) {
|
||||
return this.ctrl.dismiss(data, role, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top overlay.
|
||||
*/
|
||||
getTop(): Promise<Overlay> {
|
||||
return proxyMethod(this.ctrl, 'getTop');
|
||||
getTop() {
|
||||
return this.ctrl.getTop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
import { HTMLStencilElement } from '../types/interfaces';
|
||||
|
||||
export function proxyMethod(ctrlName: string, methodName: string, ...args: any[]) {
|
||||
const controller = ensureElementInBody(ctrlName);
|
||||
declare const __zone_symbol__requestAnimationFrame: any;
|
||||
declare const requestAnimationFrame: any;
|
||||
|
||||
export const raf = (h: any) => {
|
||||
if (typeof __zone_symbol__requestAnimationFrame === 'function') {
|
||||
return __zone_symbol__requestAnimationFrame(h);
|
||||
}
|
||||
if (typeof requestAnimationFrame === 'function') {
|
||||
return requestAnimationFrame(h);
|
||||
}
|
||||
return setTimeout(h);
|
||||
};
|
||||
|
||||
export const proxyMethod = (ctrlName: string, doc: Document, methodName: string, ...args: any[]) => {
|
||||
const controller = ensureElementInBody(ctrlName, doc);
|
||||
return controller.componentOnReady()
|
||||
.then(() => (controller as any)[methodName].apply(controller, args));
|
||||
}
|
||||
};
|
||||
|
||||
export function ensureElementInBody(elementName: string) {
|
||||
let element = document.querySelector(elementName);
|
||||
export const ensureElementInBody = (elementName: string, doc: Document) => {
|
||||
let element = doc.querySelector(elementName);
|
||||
if (!element) {
|
||||
element = document.createElement(elementName);
|
||||
document.body.appendChild(element);
|
||||
element = doc.createElement(elementName);
|
||||
doc.body.appendChild(element);
|
||||
}
|
||||
return element as HTMLStencilElement;
|
||||
}
|
||||
};
|
||||
|
||||
1
angular/test/test-app/.npmrc
Normal file
1
angular/test/test-app/.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
package-lock=false
|
||||
@@ -13,18 +13,25 @@
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/test-app",
|
||||
"outputPath": "dist/browser",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
{
|
||||
"glob": "**/*",
|
||||
"input": "src/assets",
|
||||
"output": "assets"
|
||||
},
|
||||
{
|
||||
"glob": "**/*.svg",
|
||||
"input": "node_modules/ionicons/dist/ionicons/svg",
|
||||
"output": "./svg"
|
||||
}
|
||||
],
|
||||
"styles": ["src/styles.css"],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
@@ -77,37 +84,13 @@
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "src/tsconfig.spec.json",
|
||||
"karmaConfig": "src/karma.conf.js",
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"styles": ["src/styles.css"],
|
||||
"scripts": [],
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
]
|
||||
"assets": ["src/favicon.ico", "src/assets"]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"src/tsconfig.app.json",
|
||||
"src/tsconfig.spec.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"test-app-e2e": {
|
||||
"root": "e2e/",
|
||||
"projectType": "application",
|
||||
"prefix": "",
|
||||
"architect": {
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
@@ -117,20 +100,39 @@
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "test-app:serve:production"
|
||||
},
|
||||
"ci": {
|
||||
"devServerTarget": "test-app:serve:ci"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": "e2e/tsconfig.e2e.json",
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
"tsConfig": ["tsconfig.app.json", "tsconfig.spec.json"],
|
||||
"exclude": ["**/node_modules/**"]
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"builder": "@angular-devkit/build-angular:server",
|
||||
"options": {
|
||||
"outputPath": "dist/server",
|
||||
"main": "src/main.server.ts",
|
||||
"tsConfig": "tsconfig.server.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"src": "src/environments/environment.ts",
|
||||
"replaceWith": "src/environments/environment.prod.ts"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "test-app"
|
||||
}
|
||||
}
|
||||
|
||||
12
angular/test/test-app/browserslist
Normal file
12
angular/test/test-app/browserslist
Normal file
@@ -0,0 +1,12 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
> 0.5%
|
||||
last 2 versions
|
||||
Firefox ESR
|
||||
not dead
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||
@@ -1,19 +1,22 @@
|
||||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
* @type { import("protractor").Config }
|
||||
*/
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./**/*.e2e-spec.ts'
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
|
||||
chromeOptions: {
|
||||
args: [ "--headless", "--disable-gpu", "--window-size=400,1000", "--start-maximized" ]
|
||||
}
|
||||
'browserName': 'chrome'
|
||||
},
|
||||
chromeOptions: {
|
||||
args: [ "--headless", "--disable-gpu", "--window-size=400,1000", "--start-maximized" ]
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
@@ -25,8 +28,8 @@ exports.config = {
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.e2e.json')
|
||||
project: require('path').join(__dirname, './tsconfig.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,12 +4,13 @@ import { handleErrorMessages, setProperty, getText, waitTime } from './utils';
|
||||
describe('form', () => {
|
||||
|
||||
afterEach(() => {
|
||||
handleErrorMessages();
|
||||
return handleErrorMessages();
|
||||
});
|
||||
|
||||
describe('change', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.get('/form');
|
||||
await waitTime(30);
|
||||
});
|
||||
|
||||
it('should have default values', async () => {
|
||||
@@ -81,6 +82,7 @@ describe('form', () => {
|
||||
describe('blur', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.get('/form#blur');
|
||||
await waitTime(30);
|
||||
});
|
||||
|
||||
it('ion-toggle should change only after blur', async () => {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
import { getProperty, setProperty, handleErrorMessages } from './utils';
|
||||
import { getProperty, setProperty, handleErrorMessages, waitTime } from './utils';
|
||||
|
||||
describe('inputs', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await browser.get('/inputs');
|
||||
await waitTime(30);
|
||||
});
|
||||
afterEach(() => {
|
||||
handleErrorMessages();
|
||||
return handleErrorMessages();
|
||||
});
|
||||
|
||||
it('should have default value', async () => {
|
||||
@@ -59,4 +60,10 @@ describe('inputs', () => {
|
||||
expect(await element(by.css('#select-note')).getText()).toEqual('playstation');
|
||||
expect(await element(by.css('#range-note')).getText()).toEqual('20');
|
||||
});
|
||||
|
||||
it('nested components should not interfere with NgModel', async () => {
|
||||
expect(await element(by.css('#range-note')).getText()).toEqual('10');
|
||||
await element(by.css('#nested-toggle')).click();
|
||||
expect(await element(by.css('#range-note')).getText()).toEqual('10');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,9 +5,10 @@ describe('modals', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await browser.get('/modals');
|
||||
await waitTime(30);
|
||||
});
|
||||
afterEach(() => {
|
||||
handleErrorMessages();
|
||||
return handleErrorMessages();
|
||||
});
|
||||
|
||||
it('should open standalone modal and close', async () => {
|
||||
|
||||
@@ -4,9 +4,44 @@ import { handleErrorMessages, waitTime, testStack } from './utils';
|
||||
describe('navigation', () => {
|
||||
|
||||
afterEach(() => {
|
||||
handleErrorMessages();
|
||||
return handleErrorMessages();
|
||||
});
|
||||
|
||||
// TODO: Fix flaky tests
|
||||
xit ('should swipe and abort', async () => {
|
||||
await browser.get('/router-link?ionic:mode=ios');
|
||||
await waitTime(500);
|
||||
await element(by.css('#routerLink')).click();
|
||||
await waitTime(500);
|
||||
await swipeLeft(5);
|
||||
await waitTime(500);
|
||||
|
||||
const pageHidden = element(by.css('app-router-link'));
|
||||
expect(await pageHidden.getAttribute('aria-hidden')).toEqual('true');
|
||||
expect(await pageHidden.getAttribute('class')).toEqual('ion-page ion-page-hidden');
|
||||
|
||||
const pageVisible = element(by.css('app-router-link-page'));
|
||||
expect(await pageVisible.getAttribute('aria-hidden')).toEqual(null);
|
||||
expect(await pageVisible.getAttribute('class')).toEqual('ion-page can-go-back');
|
||||
});
|
||||
|
||||
xit ('should swipe and go back', async () => {
|
||||
await browser.get('/router-link?ionic:mode=ios');
|
||||
await waitTime(500);
|
||||
await element(by.css('#routerLink')).click();
|
||||
await waitTime(500);
|
||||
await testStack('ion-router-outlet', ['app-router-link', 'app-router-link-page']);
|
||||
|
||||
await swipeLeft(300);
|
||||
|
||||
await waitTime(1000);
|
||||
await testStack('ion-router-outlet', ['app-router-link']);
|
||||
|
||||
const page = element(by.css('app-router-link'));
|
||||
expect(await page.getAttribute('aria-hidden')).toEqual(null);
|
||||
expect(await page.getAttribute('class')).toEqual('ion-page');
|
||||
})
|
||||
|
||||
it('should navigate correctly', async () => {
|
||||
await browser.get('/navigation/page1');
|
||||
await waitTime(2000);
|
||||
@@ -22,3 +57,17 @@ describe('navigation', () => {
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function swipeLeft(end: number) {
|
||||
return browser.driver.touchActions()
|
||||
.tapAndHold({x: 5, y: 1})
|
||||
.move({x: 6, y: 1})
|
||||
.move({x: 7, y: 1})
|
||||
.move({x: 8, y: 1})
|
||||
.move({x: 30, y: 1})
|
||||
.move({x: 300, y: 1})
|
||||
.move({x: end, y: 1})
|
||||
.move({x: end, y: 1})
|
||||
.release({x: end, y: 1})
|
||||
.perform();
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { waitTime, handleErrorMessages, goBack } from './utils';
|
||||
describe('nested-outlet', () => {
|
||||
|
||||
afterEach(() => {
|
||||
handleErrorMessages();
|
||||
return handleErrorMessages();
|
||||
});
|
||||
|
||||
it('should navigate correctly', async () => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { handleErrorMessages, waitTime } from './utils';
|
||||
describe('providers', () => {
|
||||
|
||||
afterEach(() => {
|
||||
handleErrorMessages();
|
||||
return handleErrorMessages();
|
||||
});
|
||||
|
||||
it('should load all providers', async () => {
|
||||
@@ -12,6 +12,9 @@ describe('providers', () => {
|
||||
|
||||
expect(await element(by.css('#is-loaded')).getText()).toEqual('true');
|
||||
expect(await element(by.css('#is-ready')).getText()).toEqual('true');
|
||||
expect(await element(by.css('#is-paused')).getText()).toEqual('true');
|
||||
expect(await element(by.css('#is-resumed')).getText()).toEqual('true');
|
||||
expect(await element(by.css('#is-resized')).getText()).toEqual('true');
|
||||
expect(await element(by.css('#is-testing')).getText()).toEqual('false');
|
||||
expect(await element(by.css('#is-desktop')).getText()).toEqual('true');
|
||||
expect(await element(by.css('#is-mobile')).getText()).toEqual('false');
|
||||
|
||||
@@ -1,13 +1,61 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
import { waitTime, testStack, testLifeCycle, handleErrorMessages } from './utils';
|
||||
import { browser, element, by, protractor } from 'protractor';
|
||||
import { waitTime, testStack, testLifeCycle, handleErrorMessages, getText } from './utils';
|
||||
|
||||
const EC = protractor.ExpectedConditions;
|
||||
|
||||
describe('router-link params and fragments', () => {
|
||||
const queryParam = 'A&=#Y';
|
||||
const fragment = 'myDiv1';
|
||||
const id = 'MyPageID==';
|
||||
|
||||
afterEach(() => {
|
||||
return handleErrorMessages();
|
||||
});
|
||||
|
||||
it('should go to a page with properly encoded values', async () => {
|
||||
await browser.get('/router-link?ionic:_testing=true');
|
||||
await element(by.css('#queryParamsFragment')).click();
|
||||
|
||||
const expectedRoute = `${encodeURIComponent(id)}?token=${encodeURIComponent(queryParam)}#${encodeURIComponent(fragment)}`;
|
||||
|
||||
browser.wait(EC.urlContains(expectedRoute), 5000);
|
||||
});
|
||||
|
||||
it('should return to a page with preserved query param and fragment', async () => {
|
||||
await browser.get('/router-link?ionic:_testing=true');
|
||||
await waitTime(30);
|
||||
await element(by.css('#queryParamsFragment')).click();
|
||||
await waitTime(400);
|
||||
await element(by.css('#goToPage3')).click();
|
||||
|
||||
browser.wait(EC.urlContains('router-link-page3'), 5000);
|
||||
await waitTime(400);
|
||||
|
||||
await element(by.css('#goBackFromPage3')).click();
|
||||
|
||||
const expectedRoute = `${encodeURIComponent(id)}?token=${encodeURIComponent(queryParam)}#${encodeURIComponent(fragment)}`;
|
||||
browser.wait(EC.urlContains(expectedRoute), 5000);
|
||||
});
|
||||
|
||||
it('should preserve query param and fragment with defaultHref string', async () => {
|
||||
await browser.get('/router-link-page3?ionic:_testing=true');
|
||||
await waitTime(30);
|
||||
|
||||
await element(by.css('#goBackFromPage3')).click();
|
||||
|
||||
const expectedRoute = '?token=ABC#fragment';
|
||||
browser.wait(EC.urlContains(expectedRoute), 5000);
|
||||
});
|
||||
});
|
||||
|
||||
describe('router-link', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await browser.get('/router-link');
|
||||
await waitTime(30);
|
||||
});
|
||||
afterEach(() => {
|
||||
handleErrorMessages();
|
||||
return handleErrorMessages();
|
||||
});
|
||||
|
||||
|
||||
@@ -25,18 +73,6 @@ describe('router-link', () => {
|
||||
it('should go forward with ion-button[routerLink]', async () => {
|
||||
await element(by.css('#routerLink')).click();
|
||||
await testForward();
|
||||
|
||||
// test go back
|
||||
await element(by.css('ion-back-button')).click();
|
||||
await waitTime(500);
|
||||
|
||||
await testStack('ion-router-outlet', ['app-router-link']);
|
||||
await testLifeCycle('app-router-link', {
|
||||
ionViewWillEnter: 2,
|
||||
ionViewDidEnter: 2,
|
||||
ionViewWillLeave: 1,
|
||||
ionViewDidLeave: 1,
|
||||
});
|
||||
});
|
||||
|
||||
it('should go forward with a[routerLink]', async () => {
|
||||
@@ -77,7 +113,6 @@ describe('router-link', () => {
|
||||
|
||||
it('should go back with ion-button[routerLink][routerDirection=back]', async () => {
|
||||
await element(by.css('#routerLink-back')).click();
|
||||
await testBack();
|
||||
});
|
||||
|
||||
it('should go back with a[routerLink][routerDirection=back]', async () => {
|
||||
@@ -93,21 +128,25 @@ describe('router-link', () => {
|
||||
});
|
||||
|
||||
async function testForward() {
|
||||
await waitTime(500);
|
||||
await waitTime(2500);
|
||||
await testStack('ion-router-outlet', ['app-router-link', 'app-router-link-page']);
|
||||
await testLifeCycle('app-router-link', {
|
||||
ionViewWillEnter: 1,
|
||||
ionViewDidEnter: 1,
|
||||
ionViewWillLeave: 1,
|
||||
ionViewDidLeave: 1,
|
||||
});
|
||||
await testLifeCycle('app-router-link-page', {
|
||||
ionViewWillEnter: 1,
|
||||
ionViewDidEnter: 1,
|
||||
ionViewWillLeave: 0,
|
||||
ionViewDidLeave: 0,
|
||||
});
|
||||
expect(await getText(`app-router-link-page #canGoBack`)).toEqual('true');
|
||||
|
||||
await browser.navigate().back();
|
||||
await waitTime(100);
|
||||
await testStack('ion-router-outlet', ['app-router-link']);
|
||||
await testLifeCycle('app-router-link', {
|
||||
ionViewWillEnter: 2,
|
||||
ionViewDidEnter: 2,
|
||||
ionViewWillLeave: 1,
|
||||
ionViewDidLeave: 1,
|
||||
});
|
||||
}
|
||||
|
||||
async function testRoot() {
|
||||
@@ -119,6 +158,17 @@ async function testRoot() {
|
||||
ionViewWillLeave: 0,
|
||||
ionViewDidLeave: 0,
|
||||
});
|
||||
expect(await getText(`app-router-link-page #canGoBack`)).toEqual('false');
|
||||
|
||||
await browser.navigate().back();
|
||||
await waitTime(100);
|
||||
await testStack('ion-router-outlet', ['app-router-link']);
|
||||
await testLifeCycle('app-router-link', {
|
||||
ionViewWillEnter: 1,
|
||||
ionViewDidEnter: 1,
|
||||
ionViewWillLeave: 0,
|
||||
ionViewDidLeave: 0,
|
||||
});
|
||||
}
|
||||
|
||||
async function testBack() {
|
||||
@@ -130,4 +180,15 @@ async function testBack() {
|
||||
ionViewWillLeave: 0,
|
||||
ionViewDidLeave: 0,
|
||||
});
|
||||
expect(await getText(`app-router-link-page #canGoBack`)).toEqual('false');
|
||||
|
||||
await browser.navigate().back();
|
||||
await waitTime(100);
|
||||
await testStack('ion-router-outlet', ['app-router-link']);
|
||||
await testLifeCycle('app-router-link', {
|
||||
ionViewWillEnter: 1,
|
||||
ionViewDidEnter: 1,
|
||||
ionViewWillLeave: 0,
|
||||
ionViewDidLeave: 0,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@ describe('slides', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await browser.get('/slides');
|
||||
await waitTime(30);
|
||||
});
|
||||
afterEach(() => {
|
||||
handleErrorMessages();
|
||||
return handleErrorMessages();
|
||||
});
|
||||
|
||||
it('should change index on slide change', async () => {
|
||||
|
||||
@@ -3,11 +3,12 @@ import { waitTime, testStack, handleErrorMessages } from './utils';
|
||||
|
||||
describe('tabs', () => {
|
||||
afterEach(() => {
|
||||
handleErrorMessages();
|
||||
return handleErrorMessages();
|
||||
});
|
||||
describe('entry url - /tabs', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.get('/tabs');
|
||||
await waitTime(30);
|
||||
});
|
||||
|
||||
it('should redirect and load tab-account', async () => {
|
||||
@@ -19,7 +20,7 @@ describe('tabs', () => {
|
||||
it('should simulate stack + double tab click', async () => {
|
||||
let tab = await getSelectedTab() as ElementFinder;
|
||||
await tab.$('#goto-tab1-page2').click();
|
||||
await testTabTitle('Tab 1 - Page 2');
|
||||
await testTabTitle('Tab 1 - Page 2 (1)');
|
||||
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab1-nested']);
|
||||
await testState(1, 'account');
|
||||
expect(await tab.$('ion-back-button').isDisplayed()).toBe(true);
|
||||
@@ -30,7 +31,7 @@ describe('tabs', () => {
|
||||
await testState(2, 'contact');
|
||||
|
||||
await element(by.css('#tab-button-account')).click();
|
||||
tab = await testTabTitle('Tab 1 - Page 2');
|
||||
tab = await testTabTitle('Tab 1 - Page 2 (1)');
|
||||
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1', 'app-tabs-tab1-nested', 'app-tabs-tab2']);
|
||||
await testState(3, 'account');
|
||||
expect(await tab.$('ion-back-button').isDisplayed()).toBe(true);
|
||||
@@ -44,7 +45,7 @@ describe('tabs', () => {
|
||||
it('should simulate stack + back button click', async () => {
|
||||
const tab = await getSelectedTab();
|
||||
await tab.$('#goto-tab1-page2').click();
|
||||
await testTabTitle('Tab 1 - Page 2');
|
||||
await testTabTitle('Tab 1 - Page 2 (1)');
|
||||
await testState(1, 'account');
|
||||
|
||||
await element(by.css('#tab-button-contact')).click();
|
||||
@@ -52,7 +53,7 @@ describe('tabs', () => {
|
||||
await testState(2, 'contact');
|
||||
|
||||
await element(by.css('#tab-button-account')).click();
|
||||
await testTabTitle('Tab 1 - Page 2');
|
||||
await testTabTitle('Tab 1 - Page 2 (1)');
|
||||
await testState(3, 'account');
|
||||
|
||||
await element(by.css('ion-back-button')).click();
|
||||
@@ -61,6 +62,33 @@ describe('tabs', () => {
|
||||
await testState(3, 'account');
|
||||
});
|
||||
|
||||
it('should navigate deep then go home', async () => {
|
||||
let tab = await getSelectedTab();
|
||||
await tab.$('#goto-tab1-page2').click();
|
||||
tab = await testTabTitle('Tab 1 - Page 2 (1)');
|
||||
|
||||
await tab.$('#goto-next').click();
|
||||
tab = await testTabTitle('Tab 1 - Page 2 (2)');
|
||||
|
||||
await element(by.css('#tab-button-contact')).click();
|
||||
tab = await testTabTitle('Tab 2 - Page 1');
|
||||
|
||||
await element(by.css('#tab-button-account')).click();
|
||||
await testTabTitle('Tab 1 - Page 2 (2)');
|
||||
await testStack('ion-tabs ion-router-outlet', [
|
||||
'app-tabs-tab1',
|
||||
'app-tabs-tab1-nested',
|
||||
'app-tabs-tab1-nested',
|
||||
'app-tabs-tab2'
|
||||
]);
|
||||
await element(by.css('#tab-button-account')).click();
|
||||
await testTabTitle('Tab 1 - Page 1');
|
||||
await testStack('ion-tabs ion-router-outlet', [
|
||||
'app-tabs-tab1',
|
||||
'app-tabs-tab2'
|
||||
]);
|
||||
});
|
||||
|
||||
it('should switch tabs and go back', async () => {
|
||||
await element(by.css('#tab-button-contact')).click();
|
||||
const tab = await testTabTitle('Tab 2 - Page 1');
|
||||
@@ -75,7 +103,7 @@ describe('tabs', () => {
|
||||
const tab = await testTabTitle('Tab 2 - Page 1');
|
||||
|
||||
await tab.$('#goto-tab1-page2').click();
|
||||
await testTabTitle('Tab 1 - Page 2');
|
||||
await testTabTitle('Tab 1 - Page 2 (1)');
|
||||
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab2', 'app-tabs-tab1-nested']);
|
||||
});
|
||||
|
||||
@@ -96,13 +124,14 @@ describe('tabs', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('entry url - /tabs/account/nested/12', () => {
|
||||
describe('entry url - /tabs/account/nested/1', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.get('/tabs/account/nested/12');
|
||||
await browser.get('/tabs/account/nested/1');
|
||||
await waitTime(30);
|
||||
});
|
||||
|
||||
it('should only display the back-button when there is a page in the stack', async () => {
|
||||
let tab = await testTabTitle('Tab 1 - Page 2') as ElementFinder;
|
||||
let tab = await testTabTitle('Tab 1 - Page 2 (1)') as ElementFinder;
|
||||
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1-nested']);
|
||||
expect(await tab.$('ion-back-button').isDisplayed()).toBe(false);
|
||||
|
||||
@@ -110,14 +139,38 @@ describe('tabs', () => {
|
||||
tab = await testTabTitle('Tab 1 - Page 1');
|
||||
|
||||
await tab.$('#goto-tab1-page2').click();
|
||||
tab = await testTabTitle('Tab 1 - Page 2');
|
||||
tab = await testTabTitle('Tab 1 - Page 2 (1)');
|
||||
expect(await tab.$('ion-back-button').isDisplayed()).toBe(true);
|
||||
});
|
||||
|
||||
it('should not reuse the same page', async () => {
|
||||
let tab = await testTabTitle('Tab 1 - Page 2 (1)') as ElementFinder;
|
||||
await tab.$('#goto-next').click();
|
||||
tab = await testTabTitle('Tab 1 - Page 2 (2)');
|
||||
await tab.$('#goto-next').click();
|
||||
tab = await testTabTitle('Tab 1 - Page 2 (3)');
|
||||
|
||||
await testStack('ion-tabs ion-router-outlet',[
|
||||
'app-tabs-tab1-nested',
|
||||
'app-tabs-tab1-nested',
|
||||
'app-tabs-tab1-nested'
|
||||
]);
|
||||
|
||||
await tab.$('ion-back-button').click();
|
||||
tab = await testTabTitle('Tab 1 - Page 2 (2)');
|
||||
await tab.$('ion-back-button').click();
|
||||
tab = await testTabTitle('Tab 1 - Page 2 (1)');
|
||||
|
||||
expect(await tab.$('ion-back-button').isDisplayed()).toBe(false);
|
||||
|
||||
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab1-nested']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('entry url - /tabs/lazy', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.get('/tabs/lazy');
|
||||
await waitTime(30);
|
||||
});
|
||||
|
||||
it('should not display the back-button if coming from a different stack', async () => {
|
||||
@@ -125,11 +178,32 @@ describe('tabs', () => {
|
||||
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab3']);
|
||||
|
||||
await tab.$('#goto-tab1-page2').click();
|
||||
tab = await testTabTitle('Tab 1 - Page 2');
|
||||
tab = await testTabTitle('Tab 1 - Page 2 (1)');
|
||||
await testStack('ion-tabs ion-router-outlet', ['app-tabs-tab3', 'app-tabs-tab1-nested']);
|
||||
expect(await tab.$('ion-back-button').isDisplayed()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('enter url - /tabs/contact/one', () => {
|
||||
beforeEach(async () => {
|
||||
await browser.get('/tabs/contact/one');
|
||||
await waitTime(30);
|
||||
});
|
||||
|
||||
it('should return to correct tab after going to page in different outlet', async () => {
|
||||
const tab = await getSelectedTab();
|
||||
await tab.$('#goto-nested-page1').click();
|
||||
|
||||
await waitTime(600);
|
||||
await testStack('app-nested-outlet ion-router-outlet', ['app-nested-outlet-page']);
|
||||
|
||||
const nestedOutlet = await element(by.css('app-nested-outlet'));
|
||||
const backButton = await nestedOutlet.$('ion-back-button');
|
||||
await backButton.click();
|
||||
|
||||
await testTabTitle('Tab 2 - Page 1');
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
async function testState(count: number, tab: string) {
|
||||
@@ -137,7 +211,7 @@ async function testState(count: number, tab: string) {
|
||||
}
|
||||
|
||||
async function testTabTitle(title: string) {
|
||||
await waitTime(600);
|
||||
await waitTime(1000);
|
||||
const tab = await getSelectedTab();
|
||||
expect(await tab.$('ion-title').getText()).toEqual(title);
|
||||
return tab;
|
||||
|
||||
@@ -37,29 +37,30 @@ export interface LifeCycleCount {
|
||||
}
|
||||
|
||||
export function handleErrorMessages() {
|
||||
browser.manage().logs().get('browser').then(function(browserLog) {
|
||||
let severWarnings = false;
|
||||
|
||||
for (let i; i <= browserLog.length - 1; i++) {
|
||||
if (browserLog[i].level.name === 'SEVERE') {
|
||||
console.log('\n' + browserLog[i].level.name);
|
||||
console.log('(Possibly exception) \n' + browserLog[i].message);
|
||||
|
||||
severWarnings = true;
|
||||
}
|
||||
return browser.manage().logs().get('browser').then(function (browserLog) {
|
||||
for (let i = 0; i <= browserLog.length - 1; i++) {
|
||||
if (browserLog[i].level.name_ === 'SEVERE') {
|
||||
fail(browserLog[i].message);
|
||||
}
|
||||
}
|
||||
|
||||
expect(severWarnings).toBe(false);
|
||||
});
|
||||
}
|
||||
|
||||
export async function testLifeCycle(selector: string, expected: LifeCycleCount) {
|
||||
await waitTime(50);
|
||||
expect(await getText(`${selector} #ngOnInit`)).toEqual('1');
|
||||
expect(await getText(`${selector} #ionViewWillEnter`)).toEqual(expected.ionViewWillEnter.toString());
|
||||
expect(await getText(`${selector} #ionViewDidEnter`)).toEqual(expected.ionViewDidEnter.toString());
|
||||
expect(await getText(`${selector} #ionViewWillLeave`)).toEqual(expected.ionViewWillLeave.toString());
|
||||
expect(await getText(`${selector} #ionViewDidLeave`)).toEqual(expected.ionViewDidLeave.toString());
|
||||
const results = await Promise.all([
|
||||
getText(`${selector} #ngOnInit`),
|
||||
getText(`${selector} #ionViewWillEnter`),
|
||||
getText(`${selector} #ionViewDidEnter`),
|
||||
getText(`${selector} #ionViewWillLeave`),
|
||||
getText(`${selector} #ionViewDidLeave`),
|
||||
]);
|
||||
|
||||
expect(results[0]).toEqual('1');
|
||||
expect(results[1]).toEqual(expected.ionViewWillEnter.toString());
|
||||
expect(results[2]).toEqual(expected.ionViewDidEnter.toString());
|
||||
expect(results[3]).toEqual(expected.ionViewWillLeave.toString());
|
||||
expect(results[4]).toEqual(expected.ionViewDidLeave.toString());
|
||||
}
|
||||
|
||||
export async function testStack(selector: string, expected: string[]) {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
import { handleErrorMessages } from './utils';
|
||||
import { handleErrorMessages, waitTime } from './utils';
|
||||
|
||||
describe('view-child', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await browser.get('/view-child');
|
||||
await waitTime(30);
|
||||
});
|
||||
afterEach(() => {
|
||||
handleErrorMessages();
|
||||
return handleErrorMessages();
|
||||
});
|
||||
|
||||
it('should get a reference to all children', async () => {
|
||||
|
||||
18
angular/test/test-app/e2e/src/virtual-scroll.e2e-spec.ts
Normal file
18
angular/test/test-app/e2e/src/virtual-scroll.e2e-spec.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
import { waitTime, handleErrorMessages } from './utils';
|
||||
|
||||
describe('virtual-scroll', () => {
|
||||
afterEach(() => {
|
||||
return handleErrorMessages();
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await browser.get('/virtual-scroll');
|
||||
await waitTime(30);
|
||||
});
|
||||
|
||||
it('should open virtual-scroll', () => {
|
||||
const virtualElements = element.all(by.css('ion-virtual-scroll > *'));
|
||||
expect(virtualElements.count()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
});
|
||||
11108
angular/test/test-app/package-lock.json
generated
11108
angular/test/test-app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,51 +1,63 @@
|
||||
{
|
||||
"name": "test-app",
|
||||
"name": "ionic-angular-test-app",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "npm run sync && ng serve",
|
||||
"sync:build": "sh scripts/build-ionic.sh",
|
||||
"sync": "sh scripts/sync.sh",
|
||||
"build": "ng build --prod --no-progress",
|
||||
"build": "npm run sync && ng build --prod --no-progress",
|
||||
"test": "ng e2e --prod",
|
||||
"test.dev": "npm run sync && ng e2e",
|
||||
"lint": "ng lint",
|
||||
"postinstall": "npm run sync"
|
||||
"postinstall": "npm run sync",
|
||||
"compile:server": "webpack --config webpack.server.config.js --progress --colors",
|
||||
"serve:ssr": "node dist/server",
|
||||
"build:ssr": "npm run build:client-and-server-bundles && npm run compile:server",
|
||||
"build:client-and-server-bundles": "npm run build && ng run test-app:server:production"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~7.2.1",
|
||||
"@angular/common": "~7.2.1",
|
||||
"@angular/compiler": "~7.2.1",
|
||||
"@angular/core": "~7.2.1",
|
||||
"@angular/forms": "~7.2.1",
|
||||
"@angular/platform-browser": "~7.2.1",
|
||||
"@angular/platform-browser-dynamic": "~7.2.1",
|
||||
"@angular/router": "~7.2.1",
|
||||
"@ionic/angular": "^4.0.0-rc.1",
|
||||
"@angular/animations": "~8.2.0",
|
||||
"@angular/common": "~8.2.0",
|
||||
"@angular/compiler": "~8.2.0",
|
||||
"@angular/core": "~8.2.0",
|
||||
"@angular/forms": "~8.2.0",
|
||||
"@angular/platform-browser": "~8.2.0",
|
||||
"@angular/platform-browser-dynamic": "~8.2.0",
|
||||
"@angular/platform-server": "~8.2.0",
|
||||
"@angular/router": "~8.2.0",
|
||||
"@ionic/angular": "^4.7.0",
|
||||
"@ionic/angular-server": "^0.0.2",
|
||||
"@nguniversal/express-engine": "~8.1.1",
|
||||
"@nguniversal/module-map-ngfactory-loader": "~8.1.1",
|
||||
"core-js": "^2.6.2",
|
||||
"rxjs": "~6.3.3",
|
||||
"express": "^4.15.2",
|
||||
"rxjs": "~6.5.2",
|
||||
"tslib": "^1.9.0",
|
||||
"zone.js": "~0.8.26"
|
||||
"zone.js": "~0.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.12.2",
|
||||
"@angular/cli": "~7.2.1",
|
||||
"@angular/compiler-cli": "~7.2.1",
|
||||
"@angular/language-service": "~7.2.1",
|
||||
"@types/node": "~8.9.4",
|
||||
"@types/jasmine": "~2.8.8",
|
||||
"@angular-devkit/build-angular": "~0.802.0",
|
||||
"@angular/cli": "~8.2.0",
|
||||
"@angular/compiler-cli": "~8.2.0",
|
||||
"@angular/language-service": "~8.2.0",
|
||||
"@types/jasmine": "~3.3.16",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"codelyzer": "~4.5.0",
|
||||
"@types/node": "~12.6.8",
|
||||
"codelyzer": "^5.0.1",
|
||||
"jasmine-core": "~2.99.1",
|
||||
"jasmine-spec-reporter": "~4.2.1",
|
||||
"karma": "~3.1.4",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||
"karma-jasmine": "~1.1.2",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"karma": "~4.2.0",
|
||||
"karma-chrome-launcher": "~3.0.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.1.0",
|
||||
"karma-jasmine": "~2.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"protractor": "~5.4.2",
|
||||
"ts-node": "~7.0.0",
|
||||
"tslint": "~5.12.1",
|
||||
"typescript": "~3.2.4"
|
||||
"ts-loader": "^5.2.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~5.18.0",
|
||||
"typescript": "~3.5.3",
|
||||
"webpack-cli": "^3.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,14 @@ popd
|
||||
pushd angular
|
||||
npm link @ionic/core
|
||||
npm run build
|
||||
npm link
|
||||
popd
|
||||
|
||||
# Build angular-server
|
||||
pushd packages/angular-server
|
||||
npm link @ionic/core
|
||||
npm link @ionic/angular
|
||||
npm run build
|
||||
popd
|
||||
|
||||
popd
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
# Copy angular dist
|
||||
rm -rf node_modules/@ionic/angular/dist
|
||||
rm -rf node_modules/@ionic/angular
|
||||
mkdir node_modules/@ionic/angular
|
||||
cp -a ../../dist node_modules/@ionic/angular/dist
|
||||
cp -a ../../css node_modules/@ionic/angular/css
|
||||
cp -a ../../package.json node_modules/@ionic/angular/package.json
|
||||
|
||||
# Copy core dist
|
||||
rm -rf node_modules/@ionic/core/dist
|
||||
# Copy angular server
|
||||
rm -rf node_modules/@ionic/angular-server
|
||||
mkdir node_modules/@ionic/angular-server
|
||||
cp -a ../../../packages/angular-server/dist node_modules/@ionic/angular-server/dist
|
||||
cp -a ../../../packages/angular-server/package.json node_modules/@ionic/angular-server/package.json
|
||||
|
||||
# # Copy core dist
|
||||
rm -rf node_modules/@ionic/core
|
||||
mkdir node_modules/@ionic/core
|
||||
cp -a ../../../core/css node_modules/@ionic/core/css
|
||||
cp -a ../../../core/dist node_modules/@ionic/core/dist
|
||||
cp -a ../../../core/hydrate node_modules/@ionic/core/hydrate
|
||||
cp -a ../../../core/loader node_modules/@ionic/core/loader
|
||||
cp -a ../../../core/package.json node_modules/@ionic/core/package.json
|
||||
|
||||
# Copy ionicons
|
||||
# # Copy ionicons
|
||||
rm -rf node_modules/ionicons
|
||||
cp -a ../../../core/node_modules/ionicons node_modules/ionicons
|
||||
|
||||
58
angular/test/test-app/server.ts
Normal file
58
angular/test/test-app/server.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* *** NOTE ON IMPORTING FROM ANGULAR AND NGUNIVERSAL IN THIS FILE ***
|
||||
*
|
||||
* If your application uses third-party dependencies, you'll need to
|
||||
* either use Webpack or the Angular CLI's `bundleDependencies` feature
|
||||
* in order to adequately package them for use on the server without a
|
||||
* node_modules directory.
|
||||
*
|
||||
* However, due to the nature of the CLI's `bundleDependencies`, importing
|
||||
* Angular in this file will create a different instance of Angular than
|
||||
* the version in the compiled application code. This leads to unavoidable
|
||||
* conflicts. Therefore, please do not explicitly import from @angular or
|
||||
* @nguniversal in this file. You can export any needed resources
|
||||
* from your application's main.server.ts file, as seen below with the
|
||||
* import for `ngExpressEngine`.
|
||||
*/
|
||||
|
||||
import 'zone.js/dist/zone-node';
|
||||
|
||||
import * as express from 'express';
|
||||
import {join} from 'path';
|
||||
|
||||
// Express server
|
||||
const app = express();
|
||||
|
||||
const PORT = process.env.PORT || 4000;
|
||||
const DIST_FOLDER = join(process.cwd(), 'dist/browser');
|
||||
|
||||
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
|
||||
const {AppServerModuleNgFactory, LAZY_MODULE_MAP, ngExpressEngine, provideModuleMap} = require('./dist/server/main');
|
||||
|
||||
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
|
||||
app.engine('html', ngExpressEngine({
|
||||
bootstrap: AppServerModuleNgFactory,
|
||||
providers: [
|
||||
provideModuleMap(LAZY_MODULE_MAP)
|
||||
]
|
||||
}));
|
||||
|
||||
app.set('view engine', 'html');
|
||||
app.set('views', DIST_FOLDER);
|
||||
|
||||
// Example Express Rest API endpoints
|
||||
// app.get('/api/**', (req, res) => { });
|
||||
// Serve static files from /browser
|
||||
app.get('*.*', express.static(DIST_FOLDER, {
|
||||
maxAge: '1y'
|
||||
}));
|
||||
|
||||
// All regular routes use the Universal engine
|
||||
app.get('*', (req, res) => {
|
||||
res.render('index', { req });
|
||||
});
|
||||
|
||||
// Start up the Node server
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Node Express server listening on http://localhost:${PORT}`);
|
||||
});
|
||||
10
angular/test/test-app/src/app/alert/alert.component.html
Normal file
10
angular/test/test-app/src/app/alert/alert.component.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>
|
||||
Alert test
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content padding>
|
||||
<p>Change Detections: <span id="counter">{{counter()}}</span></p>
|
||||
</ion-content>
|
||||
39
angular/test/test-app/src/app/alert/alert.component.ts
Normal file
39
angular/test/test-app/src/app/alert/alert.component.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Component, NgZone } from '@angular/core';
|
||||
import { AlertController } from '@ionic/angular';
|
||||
import { NavComponent } from '../nav/nav.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-alert',
|
||||
templateUrl: './alert.component.html',
|
||||
})
|
||||
export class AlertComponent {
|
||||
|
||||
changes = 0;
|
||||
|
||||
constructor(
|
||||
private alertCtrl: AlertController
|
||||
) { }
|
||||
|
||||
counter() {
|
||||
this.changes++;
|
||||
return Math.floor(this.changes / 2);
|
||||
}
|
||||
|
||||
async openAlert() {
|
||||
const alert = await this.alertCtrl.create({
|
||||
header: 'Hello',
|
||||
message: 'Some text',
|
||||
buttons: [
|
||||
{
|
||||
role: 'cancel',
|
||||
text: 'Cancel',
|
||||
handler: () => {
|
||||
console.log(NgZone.isInAngularZone());
|
||||
NgZone.assertInAngularZone();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
await alert.present();
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,9 @@ import { InputsComponent } from './inputs/inputs.component';
|
||||
import { ModalComponent } from './modal/modal.component';
|
||||
import { RouterLinkComponent } from './router-link/router-link.component';
|
||||
import { RouterLinkPageComponent } from './router-link-page/router-link-page.component';
|
||||
import { RouterLinkPage2Component } from './router-link-page2/router-link-page2.component';
|
||||
import { RouterLinkPage3Component } from './router-link-page3/router-link-page3.component';
|
||||
import { HomePageComponent } from './home-page/home-page.component';
|
||||
import { TabsComponent } from './tabs/tabs.component';
|
||||
import { TabsTab1Component } from './tabs-tab1/tabs-tab1.component';
|
||||
import { TabsTab1NestedComponent } from './tabs-tab1-nested/tabs-tab1-nested.component';
|
||||
import { TabsTab2Component } from './tabs-tab2/tabs-tab2.component';
|
||||
import { VirtualScrollComponent } from './virtual-scroll/virtual-scroll.component';
|
||||
import { VirtualScrollDetailComponent } from './virtual-scroll-detail/virtual-scroll-detail.component';
|
||||
import { NestedOutletComponent } from './nested-outlet/nested-outlet.component';
|
||||
@@ -21,9 +19,11 @@ import { FormComponent } from './form/form.component';
|
||||
import { NavigationPage1Component } from './navigation-page1/navigation-page1.component';
|
||||
import { NavigationPage2Component } from './navigation-page2/navigation-page2.component';
|
||||
import { NavigationPage3Component } from './navigation-page3/navigation-page3.component';
|
||||
import { AlertComponent } from './alert/alert.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: HomePageComponent },
|
||||
{ path: 'alerts', component: AlertComponent },
|
||||
{ path: 'inputs', component: InputsComponent },
|
||||
{ path: 'form', component: FormComponent },
|
||||
{ path: 'modals', component: ModalComponent },
|
||||
@@ -31,6 +31,8 @@ const routes: Routes = [
|
||||
{ path: 'providers', component: ProvidersComponent },
|
||||
{ path: 'router-link', component: RouterLinkComponent },
|
||||
{ path: 'router-link-page', component: RouterLinkPageComponent },
|
||||
{ path: 'router-link-page2/:id', component: RouterLinkPage2Component },
|
||||
{ path: 'router-link-page3', component: RouterLinkPage3Component },
|
||||
{ path: 'slides', component: SlidesComponent },
|
||||
{ path: 'virtual-scroll', component: VirtualScrollComponent },
|
||||
{ path: 'virtual-scroll-detail/:itemId', component: VirtualScrollDetailComponent },
|
||||
@@ -45,40 +47,7 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'tabs',
|
||||
component: TabsComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'account',
|
||||
children: [
|
||||
{
|
||||
path: 'nested/:id',
|
||||
component: TabsTab1NestedComponent
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
component: TabsTab1Component
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'contact',
|
||||
children: [
|
||||
{
|
||||
path: 'one',
|
||||
component: TabsTab2Component
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'one',
|
||||
pathMatch: 'full'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'lazy',
|
||||
loadChildren: './tabs-lazy/tabs-lazy.module#TabsLazyModule'
|
||||
}
|
||||
]
|
||||
loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
|
||||
},
|
||||
{
|
||||
path: 'nested-outlet',
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouteReuseStrategy } from '@angular/router';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { InputsComponent } from './inputs/inputs.component';
|
||||
import { ModalComponent } from './modal/modal.component';
|
||||
import { ModalExampleComponent } from './modal-example/modal-example.component';
|
||||
import { RouterLinkComponent } from './router-link/router-link.component';
|
||||
import { RouterLinkPageComponent } from './router-link-page/router-link-page.component';
|
||||
import { RouterLinkPage2Component } from './router-link-page2/router-link-page2.component';
|
||||
import { RouterLinkPage3Component } from './router-link-page3/router-link-page3.component';
|
||||
import { HomePageComponent } from './home-page/home-page.component';
|
||||
import { TabsComponent } from './tabs/tabs.component';
|
||||
import { TabsTab1Component } from './tabs-tab1/tabs-tab1.component';
|
||||
import { TabsTab2Component } from './tabs-tab2/tabs-tab2.component';
|
||||
import { TabsTab1NestedComponent } from './tabs-tab1-nested/tabs-tab1-nested.component';
|
||||
import { VirtualScrollComponent } from './virtual-scroll/virtual-scroll.component';
|
||||
import { VirtualScrollDetailComponent } from './virtual-scroll-detail/virtual-scroll-detail.component';
|
||||
import { VirtualScrollInnerComponent } from './virtual-scroll-inner/virtual-scroll-inner.component';
|
||||
@@ -30,6 +29,7 @@ import { FormComponent } from './form/form.component';
|
||||
import { NavigationPage1Component } from './navigation-page1/navigation-page1.component';
|
||||
import { NavigationPage2Component } from './navigation-page2/navigation-page2.component';
|
||||
import { NavigationPage3Component } from './navigation-page3/navigation-page3.component';
|
||||
import { AlertComponent } from './alert/alert.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -39,11 +39,9 @@ import { NavigationPage3Component } from './navigation-page3/navigation-page3.co
|
||||
ModalExampleComponent,
|
||||
RouterLinkComponent,
|
||||
RouterLinkPageComponent,
|
||||
RouterLinkPage2Component,
|
||||
RouterLinkPage3Component,
|
||||
HomePageComponent,
|
||||
TabsComponent,
|
||||
TabsTab1Component,
|
||||
TabsTab2Component,
|
||||
TabsTab1NestedComponent,
|
||||
VirtualScrollComponent,
|
||||
VirtualScrollDetailComponent,
|
||||
VirtualScrollInnerComponent,
|
||||
@@ -57,10 +55,11 @@ import { NavigationPage3Component } from './navigation-page3/navigation-page3.co
|
||||
FormComponent,
|
||||
NavigationPage1Component,
|
||||
NavigationPage2Component,
|
||||
NavigationPage3Component
|
||||
NavigationPage3Component,
|
||||
AlertComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
BrowserModule.withServerTransition({ appId: 'serverApp' }),
|
||||
AppRoutingModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
@@ -70,7 +69,9 @@ import { NavigationPage3Component } from './navigation-page3/navigation-page3.co
|
||||
ModalExampleComponent,
|
||||
NavComponent
|
||||
],
|
||||
providers: [],
|
||||
providers: [
|
||||
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
||||
19
angular/test/test-app/src/app/app.server.module.ts
Normal file
19
angular/test/test-app/src/app/app.server.module.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ServerModule } from '@angular/platform-server';
|
||||
|
||||
import { IonicServerModule } from '@ionic/angular-server';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
AppModule,
|
||||
ServerModule,
|
||||
ModuleMapLoaderModule,
|
||||
IonicServerModule
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppServerModule {}
|
||||
@@ -20,7 +20,9 @@ export class FormComponent {
|
||||
input2: ['Default Value'],
|
||||
checkbox: [false],
|
||||
range: [5, Validators.min(10)],
|
||||
}, {updateOn: window.location.hash === '#blur' ? 'blur' : 'change'});
|
||||
}, {
|
||||
updateOn: typeof (window as any) !== 'undefined' && window.location.hash === '#blur' ? 'blur' : 'change'
|
||||
});
|
||||
}
|
||||
|
||||
onSubmit(_ev) {
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item routerLink="/alerts">
|
||||
<ion-label>
|
||||
Alerts test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/inputs">
|
||||
<ion-label>
|
||||
Inputs test
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<p>Change Detections: <span id="counter">{{counter()}}</span></p>
|
||||
<ion-list>
|
||||
|
||||
<ion-item>
|
||||
@@ -89,10 +90,12 @@
|
||||
<ion-range [(ngModel)]="range"></ion-range>
|
||||
<ion-note slot="end" id="range-note">{{range}}</ion-note>
|
||||
</ion-item>
|
||||
|
||||
|
||||
<ion-item color="dark">
|
||||
<ion-label>Range Mirror</ion-label>
|
||||
<ion-range [(ngModel)]="range"></ion-range>
|
||||
<ion-range [(ngModel)]="range">
|
||||
<ion-toggle slot="start" id="nested-toggle" [(ngModel)]="toggle"></ion-toggle>
|
||||
</ion-range>
|
||||
<ion-note slot="end">{{range}}</ion-note>
|
||||
</ion-item>
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ export class InputsComponent {
|
||||
toggle = true;
|
||||
select = 'nes';
|
||||
range = 10;
|
||||
changes = 0;
|
||||
|
||||
setValues() {
|
||||
console.log('set values');
|
||||
@@ -32,4 +33,8 @@ export class InputsComponent {
|
||||
this.select = undefined;
|
||||
this.range = undefined;
|
||||
}
|
||||
counter() {
|
||||
this.changes++;
|
||||
return Math.floor(this.changes / 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>
|
||||
NESTED OUTLET
|
||||
</ion-title>
|
||||
|
||||
@@ -12,6 +12,15 @@
|
||||
<p>
|
||||
isReady: <span id="is-ready">{{isReady}}</span>
|
||||
</p>
|
||||
<p>
|
||||
isResumed: <span id="is-resumed">{{isResumed}}</span>
|
||||
</p>
|
||||
<p>
|
||||
isPaused: <span id="is-paused">{{isPaused}}</span>
|
||||
</p>
|
||||
<p>
|
||||
isResized: <span id="is-resized">{{isResized}}</span>
|
||||
</p>
|
||||
<p>
|
||||
isTesting: <span id="is-testing">{{isTesting}}</span>
|
||||
</p>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, NgZone } from '@angular/core';
|
||||
import {
|
||||
Platform, Config, ModalController, AlertController, ActionSheetController,
|
||||
PopoverController, ToastController, Events, PickerController, MenuController,
|
||||
LoadingController, NavController, DomController
|
||||
Platform, ModalController, AlertController, ActionSheetController,
|
||||
PopoverController, ToastController, Events, PickerController, MenuController,
|
||||
LoadingController, NavController, DomController, Config
|
||||
} from '@ionic/angular';
|
||||
|
||||
@Component({
|
||||
@@ -14,6 +14,9 @@ export class ProvidersComponent {
|
||||
isLoaded = false;
|
||||
isReady = false;
|
||||
isEvent = false;
|
||||
isResumed = false;
|
||||
isPaused = false;
|
||||
isResized = false;
|
||||
isTesting: boolean = undefined;
|
||||
isDesktop: boolean = undefined;
|
||||
isMobile: boolean = undefined;
|
||||
@@ -32,7 +35,8 @@ export class ProvidersComponent {
|
||||
toastCtrl: ToastController,
|
||||
navCtrl: NavController,
|
||||
domCtrl: DomController,
|
||||
config: Config
|
||||
config: Config,
|
||||
zone: NgZone
|
||||
) {
|
||||
// test all providers load
|
||||
if (
|
||||
@@ -44,15 +48,31 @@ export class ProvidersComponent {
|
||||
|
||||
// test platform ready()
|
||||
platform.ready().then(() => {
|
||||
NgZone.assertInAngularZone();
|
||||
this.isReady = true;
|
||||
});
|
||||
|
||||
platform.resume.subscribe(() => {
|
||||
console.log('platform:resume');
|
||||
NgZone.assertInAngularZone();
|
||||
this.isResumed = true;
|
||||
});
|
||||
platform.pause.subscribe(() => {
|
||||
console.log('platform:pause');
|
||||
NgZone.assertInAngularZone();
|
||||
this.isPaused = true;
|
||||
});
|
||||
platform.resize.subscribe(() => {
|
||||
console.log('platform:resize');
|
||||
NgZone.assertInAngularZone();
|
||||
this.isResized = true;
|
||||
});
|
||||
this.isDesktop = platform.is('desktop');
|
||||
this.isMobile = platform.is('mobile');
|
||||
|
||||
// test events
|
||||
events.subscribe('topic', () => {
|
||||
this.isEvent = true;
|
||||
NgZone.assertInAngularZone();
|
||||
});
|
||||
events.publish('topic');
|
||||
|
||||
@@ -60,5 +80,11 @@ export class ProvidersComponent {
|
||||
this.isTesting = config.getBoolean('_testing');
|
||||
config.set('keyboardHeight', 12345);
|
||||
this.keyboardHeight = config.getNumber('keyboardHeight');
|
||||
|
||||
zone.runOutsideAngular(() => {
|
||||
document.dispatchEvent(new CustomEvent('pause'));
|
||||
document.dispatchEvent(new CustomEvent('resume'));
|
||||
window.dispatchEvent(new CustomEvent('resize'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
<ion-content padding>
|
||||
<p>ngOnInit: <span id="ngOnInit">{{onInit}}</span></p>
|
||||
<p>canGoBack: <span id="canGoBack">{{canGoBack}}</span></p>
|
||||
<p>ionViewWillEnter: <span id="ionViewWillEnter">{{willEnter}}</span></p>
|
||||
<p>ionViewDidEnter: <span id="ionViewDidEnter">{{didEnter}}</span></p>
|
||||
<p>ionViewWillLeave: <span id="ionViewWillLeave">{{willLeave}}</span></p>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, OnInit, NgZone } from '@angular/core';
|
||||
import { IonRouterOutlet } from '@ionic/angular';
|
||||
|
||||
@Component({
|
||||
selector: 'app-router-link-page',
|
||||
@@ -11,9 +12,15 @@ export class RouterLinkPageComponent implements OnInit {
|
||||
didEnter = 0;
|
||||
willLeave = 0;
|
||||
didLeave = 0;
|
||||
canGoBack: boolean = null;
|
||||
|
||||
constructor(
|
||||
private ionRouterOutlet: IonRouterOutlet
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
NgZone.assertInAngularZone();
|
||||
this.canGoBack = this.ionRouterOutlet.canGoBack();
|
||||
this.onInit++;
|
||||
}
|
||||
|
||||
@@ -21,10 +28,16 @@ export class RouterLinkPageComponent implements OnInit {
|
||||
if (this.onInit !== 1) {
|
||||
throw new Error('ngOnInit was not called');
|
||||
}
|
||||
if (this.canGoBack !== this.ionRouterOutlet.canGoBack()) {
|
||||
throw new Error('canGoBack() changed');
|
||||
}
|
||||
NgZone.assertInAngularZone();
|
||||
this.willEnter++;
|
||||
}
|
||||
ionViewDidEnter() {
|
||||
if (this.canGoBack !== this.ionRouterOutlet.canGoBack()) {
|
||||
throw new Error('canGoBack() changed');
|
||||
}
|
||||
NgZone.assertInAngularZone();
|
||||
this.didEnter++;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Router Page 2</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content padding>
|
||||
<ion-button routerLink="/router-link-page3" id="goToPage3">Go to Page 3</ion-button>
|
||||
</ion-content>
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-router-link-page2',
|
||||
templateUrl: './router-link-page2.component.html'
|
||||
})
|
||||
export class RouterLinkPage2Component implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<ion-header>
|
||||
<ion-toolbar color="dark">
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button defaultHref="/?token=ABC#fragment" id="goBackFromPage3"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Router Page 3</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content padding>
|
||||
Page 3
|
||||
</ion-content>
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-router-link-page3',
|
||||
templateUrl: './router-link-page3.component.html'
|
||||
})
|
||||
export class RouterLinkPage3Component implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
<p>ionViewDidEnter: <span id="ionViewDidEnter">{{didEnter}}</span></p>
|
||||
<p>ionViewWillLeave: <span id="ionViewWillLeave">{{willLeave}}</span></p>
|
||||
<p>ionViewDidLeave: <span id="ionViewDidLeave">{{didLeave}}</span></p>
|
||||
<p>Change Detections: <span id="counter">{{counter()}}</span></p>
|
||||
|
||||
<p>
|
||||
<ion-button routerLink="/router-link-page" expand="block" color="dark" id="routerLink">ion-button[routerLink]</ion-button>
|
||||
@@ -27,4 +28,6 @@
|
||||
<p><button (click)="navigateRoot()" id="button-root">navigateForward</button></p>
|
||||
<p><button (click)="navigateBack()" id="button-back">navigateBack</button></p>
|
||||
|
||||
<p><button id="queryParamsFragment" routerLink="/router-link-page2/MyPageID==" [queryParams]="{ token: 'A&=#Y' }" fragment="myDiv1">Query Params and Fragment</button></p>
|
||||
|
||||
</ion-content>
|
||||
|
||||
@@ -13,6 +13,7 @@ export class RouterLinkComponent implements OnInit {
|
||||
didEnter = 0;
|
||||
willLeave = 0;
|
||||
didLeave = 0;
|
||||
changes = 0;
|
||||
|
||||
constructor(
|
||||
private navCtrl: NavController,
|
||||
@@ -35,6 +36,11 @@ export class RouterLinkComponent implements OnInit {
|
||||
this.navCtrl.navigateRoot('/router-link-page');
|
||||
}
|
||||
|
||||
counter() {
|
||||
this.changes++;
|
||||
return Math.floor(this.changes / 2);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
NgZone.assertInAngularZone();
|
||||
this.onInit++;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
|
||||
import { AfterViewInit, Component, ViewChild } from '@angular/core';
|
||||
import { IonSlides } from '@ionic/angular';
|
||||
|
||||
@Component({
|
||||
@@ -6,7 +6,7 @@ import { IonSlides } from '@ionic/angular';
|
||||
templateUrl: './slides.component.html',
|
||||
})
|
||||
export class SlidesComponent implements AfterViewInit {
|
||||
@ViewChild(IonSlides) slides: IonSlides;
|
||||
@ViewChild(IonSlides, { static: true }) slides: IonSlides;
|
||||
|
||||
slideIndex = 0;
|
||||
slideIndex2 = 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user