Compare commits

...

10 Commits

Author SHA1 Message Date
Sean Perkins
e917e9fdf2 refactor: rename and comments 2024-07-22 11:07:41 -04:00
Sean Perkins
d12f35705f fix: mounting of pages
Notes: Push and Pop animations are not working. Swipe to go back isn't working. It isn't keeping the leaving view item in the DOM after pushing a new entering view item.
2024-07-19 14:53:31 -04:00
Sean Perkins
cb8e54e9ac fix: mounting of top-level outlet pages 2024-07-18 12:36:36 -04:00
Sean Perkins
8bc66d1db3 chore(deps): bump react router types to v6 2024-07-18 12:31:50 -04:00
Sean Perkins
f4561d57c6 feat(IonReactHashRouter): migrate to rr6 hash router 2024-07-17 17:10:46 -04:00
Sean Perkins
0a2a8b0f09 feat(IonReactMemoryRouter): migrate to rr6 memory router 2024-07-17 17:10:31 -04:00
Sean Perkins
0ba5219c09 feat(IonReactRouter): migrate to a functional component and react router 6 2024-07-17 17:08:45 -04:00
Sean Perkins
4ecd533aef feat(IonRouteInner): migrate to functional component and react router 6 2024-07-17 17:01:48 -04:00
Sean Perkins
b6d47ff035 feat(IonRouter): migrate to functional component with react router 6 2024-07-17 16:59:23 -04:00
Sean Perkins
35e2de2ce2 chore(deps): bump to react-router@6 and react-router-dom@6 2024-07-17 16:58:54 -04:00
11 changed files with 368 additions and 589 deletions

View File

@@ -19,7 +19,7 @@
"@types/node": "^14.0.14",
"@types/react": "^17.0.79",
"@types/react-dom": "^17.0.25",
"@types/react-router": "^5.0.3",
"@types/react-router": "^6.0.0",
"@types/react-router-dom": "^5.1.5",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
@@ -27,8 +27,8 @@
"prettier": "^2.8.3",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1",
"react-router": "^6.0.0",
"react-router-dom": "^6.0.0",
"rimraf": "^3.0.2",
"rollup": "^4.2.0",
"typescript": "^4.0.5"
@@ -36,8 +36,8 @@
"peerDependencies": {
"react": ">=16.8.6",
"react-dom": ">=16.8.6",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1"
"react-router": "^6.0.0",
"react-router-dom": "^6.0.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -152,18 +152,6 @@
"node": ">=4"
}
},
"node_modules/@babel/runtime": {
"version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz",
"integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -462,6 +450,15 @@
"node": ">= 8"
}
},
"node_modules/@remix-run/router": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.18.0.tgz",
"integrity": "sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw==",
"dev": true,
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rollup/plugin-typescript": {
"version": "11.1.5",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.5.tgz",
@@ -2337,29 +2334,6 @@
"node": ">= 0.4"
}
},
"node_modules/history": {
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.1.2",
"loose-envify": "^1.2.0",
"resolve-pathname": "^3.0.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0",
"value-equal": "^1.0.1"
}
},
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"dev": true,
"dependencies": {
"react-is": "^16.7.0"
}
},
"node_modules/ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@@ -2664,12 +2638,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
"dev": true
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -2994,15 +2962,6 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"node_modules/path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
"dev": true,
"dependencies": {
"isarray": "0.0.1"
}
},
"node_modules/path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@@ -3057,17 +3016,6 @@
"node": ">=0.4.0"
}
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dev": true,
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -3122,56 +3070,38 @@
"react": "17.0.2"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
},
"node_modules/react-router": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz",
"integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==",
"version": "6.25.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.25.1.tgz",
"integrity": "sha512-u8ELFr5Z6g02nUtpPAggP73Jigj1mRePSwhS/2nkTrlPU5yEkH1vYzWNyvSnSzeeE2DNqWdH+P8OhIh9wuXhTw==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.12.13",
"history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.2",
"react-is": "^16.6.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
"@remix-run/router": "1.18.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=15"
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz",
"integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==",
"version": "6.25.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.25.1.tgz",
"integrity": "sha512-0tUDpbFvk35iv+N89dWNrJp+afLgd+y4VtorJZuOCXK0kkCWjEvb3vTJM++SYvMEpbVwXKf3FjeVveVEb6JpDQ==",
"dev": true,
"dependencies": {
"@babel/runtime": "^7.12.13",
"history": "^4.9.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-router": "5.3.4",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
"@remix-run/router": "1.18.0",
"react-router": "6.25.1"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=15"
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/regenerator-runtime": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
"integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==",
"dev": true
},
"node_modules/regexp.prototype.flags": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz",
@@ -3236,12 +3166,6 @@
"node": ">=4"
}
},
"node_modules/resolve-pathname": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==",
"dev": true
},
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -3636,18 +3560,6 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
"node_modules/tiny-invariant": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==",
"dev": true
},
"node_modules/tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"dev": true
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -3830,12 +3742,6 @@
"integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==",
"dev": true
},
"node_modules/value-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==",
"dev": true
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -3990,15 +3896,6 @@
}
}
},
"@babel/runtime": {
"version": "7.23.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz",
"integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.14.0"
}
},
"@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -4198,6 +4095,12 @@
"fastq": "^1.6.0"
}
},
"@remix-run/router": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.18.0.tgz",
"integrity": "sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw==",
"dev": true
},
"@rollup/plugin-typescript": {
"version": "11.1.5",
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.5.tgz",
@@ -5511,29 +5414,6 @@
"function-bind": "^1.1.2"
}
},
"history": {
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
"integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
"dev": true,
"requires": {
"@babel/runtime": "^7.1.2",
"loose-envify": "^1.2.0",
"resolve-pathname": "^3.0.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0",
"value-equal": "^1.0.1"
}
},
"hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
"dev": true,
"requires": {
"react-is": "^16.7.0"
}
},
"ignore": {
"version": "5.2.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
@@ -5742,12 +5622,6 @@
"call-bind": "^1.0.2"
}
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
"dev": true
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -6006,15 +5880,6 @@
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"path-to-regexp": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz",
"integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==",
"dev": true,
"requires": {
"isarray": "0.0.1"
}
},
"path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@@ -6045,17 +5910,6 @@
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
"prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dev": true,
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -6087,50 +5941,25 @@
"scheduler": "^0.20.2"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true
},
"react-router": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz",
"integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==",
"version": "6.25.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.25.1.tgz",
"integrity": "sha512-u8ELFr5Z6g02nUtpPAggP73Jigj1mRePSwhS/2nkTrlPU5yEkH1vYzWNyvSnSzeeE2DNqWdH+P8OhIh9wuXhTw==",
"dev": true,
"requires": {
"@babel/runtime": "^7.12.13",
"history": "^4.9.0",
"hoist-non-react-statics": "^3.1.0",
"loose-envify": "^1.3.1",
"path-to-regexp": "^1.7.0",
"prop-types": "^15.6.2",
"react-is": "^16.6.0",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
"@remix-run/router": "1.18.0"
}
},
"react-router-dom": {
"version": "5.3.4",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz",
"integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==",
"version": "6.25.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.25.1.tgz",
"integrity": "sha512-0tUDpbFvk35iv+N89dWNrJp+afLgd+y4VtorJZuOCXK0kkCWjEvb3vTJM++SYvMEpbVwXKf3FjeVveVEb6JpDQ==",
"dev": true,
"requires": {
"@babel/runtime": "^7.12.13",
"history": "^4.9.0",
"loose-envify": "^1.3.1",
"prop-types": "^15.6.2",
"react-router": "5.3.4",
"tiny-invariant": "^1.0.2",
"tiny-warning": "^1.0.0"
"@remix-run/router": "1.18.0",
"react-router": "6.25.1"
}
},
"regenerator-runtime": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
"integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==",
"dev": true
},
"regexp.prototype.flags": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz",
@@ -6171,12 +6000,6 @@
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
},
"resolve-pathname": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==",
"dev": true
},
"reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -6462,18 +6285,6 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
"tiny-invariant": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz",
"integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==",
"dev": true
},
"tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"dev": true
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -6612,12 +6423,6 @@
"integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==",
"dev": true
},
"value-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==",
"dev": true
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View File

@@ -42,8 +42,8 @@
"peerDependencies": {
"react": ">=16.8.6",
"react-dom": ">=16.8.6",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1"
"react-router": "^6.0.0",
"react-router-dom": "^6.0.0"
},
"devDependencies": {
"@ionic/eslint-config": "^0.3.0",
@@ -52,7 +52,7 @@
"@types/node": "^14.0.14",
"@types/react": "^17.0.79",
"@types/react-dom": "^17.0.25",
"@types/react-router": "^5.0.3",
"@types/react-router": "^6.0.0",
"@types/react-router-dom": "^5.1.5",
"@typescript-eslint/eslint-plugin": "^5.48.2",
"@typescript-eslint/parser": "^5.48.2",
@@ -60,8 +60,8 @@
"prettier": "^2.8.3",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router": "^5.0.1",
"react-router-dom": "^5.0.1",
"react-router": "^6.0.0",
"react-router-dom": "^6.0.0",
"rimraf": "^3.0.2",
"rollup": "^4.2.0",
"typescript": "^4.0.5"

View File

@@ -2,7 +2,7 @@ import type { Action as HistoryAction, History, Location as HistoryLocation } fr
import { createHashHistory as createHistory } from 'history';
import React from 'react';
import type { BrowserRouterProps } from 'react-router-dom';
import { Router } from 'react-router-dom';
import { HashRouter } from 'react-router-dom';
import { IonRouter } from './IonRouter';
@@ -45,9 +45,9 @@ export class IonReactHashRouter extends React.Component<IonReactHashRouterProps>
render() {
const { children, ...props } = this.props;
return (
<Router history={this.history} {...props}>
<HashRouter history={this.history} {...props}>
<IonRouter registerHistoryListener={this.registerHistoryListener}>{children}</IonRouter>
</Router>
</HashRouter>
);
}
}

View File

@@ -1,7 +1,7 @@
import type { Action as HistoryAction, Location as HistoryLocation, MemoryHistory } from 'history';
import React from 'react';
import type { MemoryRouterProps } from 'react-router';
import { Router } from 'react-router';
import { MemoryRouter } from 'react-router';
import { IonRouter } from './IonRouter';
@@ -43,9 +43,9 @@ export class IonReactMemoryRouter extends React.Component<IonReactMemoryRouterPr
render() {
const { children, ...props } = this.props;
return (
<Router {...props}>
<MemoryRouter {...props}>
<IonRouter registerHistoryListener={this.registerHistoryListener}>{children}</IonRouter>
</Router>
</MemoryRouter>
);
}
}

View File

@@ -1,8 +1,8 @@
import type { Action as HistoryAction, History, Location as HistoryLocation } from 'history';
import { createBrowserHistory as createHistory } from 'history';
import React from 'react';
import type { PropsWithChildren } from 'react';
import React, { useEffect, useRef } from 'react';
import type { BrowserRouterProps } from 'react-router-dom';
import { Router } from 'react-router-dom';
import { BrowserRouter, useLocation, useNavigationType } from 'react-router-dom';
import { IonRouter } from './IonRouter';
@@ -10,44 +10,41 @@ interface IonReactRouterProps extends BrowserRouterProps {
history?: History;
}
export class IonReactRouter extends React.Component<IonReactRouterProps> {
historyListenHandler?: (location: HistoryLocation, action: HistoryAction) => void;
history: History;
/**
* `IonReactRouter` is Ionic's routing container component. It is a wrapper around `BrowserRouter` from `react-router-dom`.
*/
export const IonReactRouter = ({ children, ...rest }: PropsWithChildren<IonReactRouterProps>) => {
return (
<BrowserRouter>
<IonInnerReactRouter {...rest}>{children}</IonInnerReactRouter>
</BrowserRouter>
);
};
constructor(props: IonReactRouterProps) {
super(props);
const { history, ...rest } = props;
this.history = history || createHistory(rest);
this.history.listen(this.handleHistoryChange.bind(this));
this.registerHistoryListener = this.registerHistoryListener.bind(this);
}
/**
* Browser router provides the context APIs for the hooks to work.
*/
const IonInnerReactRouter = ({ children }: PropsWithChildren<IonReactRouterProps>) => {
const location = useLocation();
const navigationType = useNavigationType();
/**
* history@4.x passes separate location and action
* params. history@5.x passes location and action
* together as a single object.
* TODO: If support for React Router <=5 is dropped
* this logic is no longer needed. We can just assume
* a single object with both location and action.
*/
handleHistoryChange(location: HistoryLocation, action: HistoryAction) {
const historyListenHandler = useRef<(location: HistoryLocation, action: HistoryAction) => void>();
const registerHistoryListener = (cb: (location: HistoryLocation, action: HistoryAction) => void) => {
historyListenHandler.current = cb;
};
const handleHistoryChange = (location: HistoryLocation, action: HistoryAction) => {
const locationValue = (location as any).location || location;
const actionValue = (location as any).action || action;
if (this.historyListenHandler) {
this.historyListenHandler(locationValue, actionValue);
if (historyListenHandler.current) {
historyListenHandler.current(locationValue, actionValue);
}
}
};
registerHistoryListener(cb: (location: HistoryLocation, action: HistoryAction) => void) {
this.historyListenHandler = cb;
}
useEffect(() => {
handleHistoryChange(location, navigationType);
}, [location, navigationType]);
render() {
const { children, ...props } = this.props;
return (
<Router history={this.history} {...props}>
<IonRouter registerHistoryListener={this.registerHistoryListener}>{children}</IonRouter>
</Router>
);
}
}
return <IonRouter registerHistoryListener={registerHistoryListener}>{children}</IonRouter>;
};

View File

@@ -2,29 +2,8 @@ import type { IonRouteProps } from '@ionic/react';
import React from 'react';
import { Route } from 'react-router';
export class IonRouteInner extends React.PureComponent<IonRouteProps> {
render() {
return (
<Route
path={this.props.path}
exact={this.props.exact}
render={this.props.render}
{
/**
* `computedMatch` is a private API in react-router v5 that
* has been removed in v6.
*
* This needs to be removed when we support v6.
*
* TODO: FW-647
*/
...((this.props as any).computedMatch !== undefined
? {
computedMatch: (this.props as any).computedMatch,
}
: {})
}
/>
);
}
}
export const IonRouteInner = ({ path, render }: IonRouteProps) => {
return (
<Route path={path} children={render} />
)
}

View File

@@ -1,16 +1,9 @@
import type {
AnimationBuilder,
RouteAction,
RouteInfo,
RouteManagerContextState,
RouterDirection,
ViewItem,
} from '@ionic/react';
import type { AnimationBuilder, RouteAction, RouteInfo, RouteManagerContextState, RouterDirection } from '@ionic/react';
import { LocationHistory, NavManager, RouteManagerContext, generateId, getConfig } from '@ionic/react';
import type { Action as HistoryAction, Location as HistoryLocation } from 'history';
import React from 'react';
import type { RouteComponentProps } from 'react-router-dom';
import { withRouter } from 'react-router-dom';
import type { PropsWithChildren } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { IonRouteInner } from './IonRouteInner';
import { ReactRouterViewStack } from './ReactRouterViewStack';
@@ -21,157 +14,110 @@ export interface LocationState {
routerOptions?: { as?: string; unmount?: boolean };
}
interface IonRouteProps extends RouteComponentProps<{}, {}, LocationState> {
interface IonRouterProps {
registerHistoryListener: (cb: (location: HistoryLocation<any>, action: HistoryAction) => void) => void;
}
interface IonRouteState {
routeInfo: RouteInfo;
}
export const IonRouter = ({ children, registerHistoryListener }: PropsWithChildren<IonRouterProps>) => {
const location = useLocation();
const params = useParams();
const navigate = useNavigate();
class IonRouterInner extends React.PureComponent<IonRouteProps, IonRouteState> {
currentTab?: string;
exitViewFromOtherOutletHandlers: ((pathname: string) => ViewItem | undefined)[] = [];
incomingRouteParams?: Partial<RouteInfo>;
locationHistory = new LocationHistory();
viewStack = new ReactRouterViewStack();
routeMangerContextState: RouteManagerContextState = {
canGoBack: () => this.locationHistory.canGoBack(),
clearOutlet: this.viewStack.clear,
findViewItemByPathname: this.viewStack.findViewItemByPathname,
getChildrenToRender: this.viewStack.getChildrenToRender,
goBack: () => this.handleNavigateBack(),
createViewItem: this.viewStack.createViewItem,
findViewItemByRouteInfo: this.viewStack.findViewItemByRouteInfo,
findLeavingViewItemByRouteInfo: this.viewStack.findLeavingViewItemByRouteInfo,
addViewItem: this.viewStack.add,
unMountViewItem: this.viewStack.remove,
};
const didMountRef = useRef(false);
const locationHistory = useRef(new LocationHistory());
const currentTab = useRef<string | undefined>(undefined);
const viewStack = useRef(new ReactRouterViewStack());
const incomingRouteParams = useRef<Partial<RouteInfo> | null>(null);
constructor(props: IonRouteProps) {
super(props);
const [routeInfo, setRouteInfo] = useState({
id: generateId('routeInfo'),
pathname: location.pathname,
search: location.search,
});
const routeInfo = {
id: generateId('routeInfo'),
pathname: this.props.location.pathname,
search: this.props.location.search,
};
useEffect(() => {
didMountRef.current = true;
}, []);
this.locationHistory.add(routeInfo);
this.handleChangeTab = this.handleChangeTab.bind(this);
this.handleResetTab = this.handleResetTab.bind(this);
this.handleNativeBack = this.handleNativeBack.bind(this);
this.handleNavigate = this.handleNavigate.bind(this);
this.handleNavigateBack = this.handleNavigateBack.bind(this);
this.props.registerHistoryListener(this.handleHistoryChange.bind(this));
this.handleSetCurrentTab = this.handleSetCurrentTab.bind(this);
this.state = {
routeInfo,
};
}
handleChangeTab(tab: string, path?: string, routeOptions?: any) {
if (!path) {
return;
}
const routeInfo = this.locationHistory.getCurrentRouteInfoForTab(tab);
const [pathname, search] = path.split('?');
if (routeInfo) {
this.incomingRouteParams = { ...routeInfo, routeAction: 'push', routeDirection: 'none' };
if (routeInfo.pathname === pathname) {
this.incomingRouteParams.routeOptions = routeOptions;
this.props.history.push(routeInfo.pathname + (routeInfo.search || ''));
} else {
this.incomingRouteParams.pathname = pathname;
this.incomingRouteParams.search = search ? '?' + search : undefined;
this.incomingRouteParams.routeOptions = routeOptions;
this.props.history.push(pathname + (search ? '?' + search : ''));
}
} else {
this.handleNavigate(pathname, 'push', 'none', undefined, routeOptions, tab);
}
}
handleHistoryChange(location: HistoryLocation<LocationState>, action: HistoryAction) {
const handleHistoryChange = (location: HistoryLocation<LocationState>, action: HistoryAction) => {
let leavingLocationInfo: RouteInfo;
if (this.incomingRouteParams) {
if (this.incomingRouteParams.routeAction === 'replace') {
leavingLocationInfo = this.locationHistory.previous();
if (incomingRouteParams) {
if (incomingRouteParams.current?.routeAction === 'replace') {
leavingLocationInfo = locationHistory.current.previous();
} else {
leavingLocationInfo = this.locationHistory.current();
leavingLocationInfo = locationHistory.current.current();
}
} else {
leavingLocationInfo = this.locationHistory.current();
leavingLocationInfo = locationHistory.current.current();
}
const leavingUrl = leavingLocationInfo.pathname + leavingLocationInfo.search;
if (leavingUrl !== location.pathname) {
if (!this.incomingRouteParams) {
if (!incomingRouteParams.current) {
if (action === 'REPLACE') {
this.incomingRouteParams = {
incomingRouteParams.current = {
routeAction: 'replace',
routeDirection: 'none',
tab: this.currentTab,
tab: currentTab.current,
};
}
if (action === 'POP') {
const currentRoute = this.locationHistory.current();
const currentRoute = locationHistory.current.current();
if (currentRoute && currentRoute.pushedByRoute) {
const prevInfo = this.locationHistory.findLastLocation(currentRoute);
this.incomingRouteParams = { ...prevInfo, routeAction: 'pop', routeDirection: 'back' };
const prevInfo = locationHistory.current.findLastLocation(currentRoute);
incomingRouteParams.current = { ...prevInfo, routeAction: 'pop', routeDirection: 'back' };
} else {
this.incomingRouteParams = {
incomingRouteParams.current = {
routeAction: 'pop',
routeDirection: 'none',
tab: this.currentTab,
tab: currentTab.current,
};
}
}
if (!this.incomingRouteParams) {
this.incomingRouteParams = {
if (!incomingRouteParams.current) {
incomingRouteParams.current = {
routeAction: 'push',
routeDirection: location.state?.direction || 'forward',
routeOptions: location.state?.routerOptions,
tab: this.currentTab,
tab: currentTab.current,
};
}
}
let routeInfo: RouteInfo;
if (this.incomingRouteParams?.id) {
if (incomingRouteParams.current?.id) {
routeInfo = {
...(this.incomingRouteParams as RouteInfo),
...(incomingRouteParams.current as RouteInfo),
lastPathname: leavingLocationInfo.pathname,
};
this.locationHistory.add(routeInfo);
locationHistory.current.add(routeInfo);
} else {
const isPushed =
this.incomingRouteParams.routeAction === 'push' && this.incomingRouteParams.routeDirection === 'forward';
incomingRouteParams.current?.routeAction === 'push' &&
incomingRouteParams.current.routeDirection === 'forward';
routeInfo = {
id: generateId('routeInfo'),
...this.incomingRouteParams,
...incomingRouteParams,
lastPathname: leavingLocationInfo.pathname,
pathname: location.pathname,
search: location.search,
params: this.props.match.params,
params: params as any, // TODO @sean fix type of route info for params
prevRouteLastPathname: leavingLocationInfo.lastPathname,
};
if (isPushed) {
routeInfo.tab = leavingLocationInfo.tab;
routeInfo.pushedByRoute = leavingLocationInfo.pathname;
} else if (routeInfo.routeAction === 'pop') {
const r = this.locationHistory.findLastLocation(routeInfo);
const r = locationHistory.current.findLastLocation(routeInfo);
routeInfo.pushedByRoute = r?.pushedByRoute;
} else if (routeInfo.routeAction === 'push' && routeInfo.tab !== leavingLocationInfo.tab) {
// If we are switching tabs grab the last route info for the tab and use its pushedByRoute
const lastRoute = this.locationHistory.getCurrentRouteInfoForTab(routeInfo.tab);
const lastRoute = locationHistory.current.getCurrentRouteInfoForTab(routeInfo.tab);
routeInfo.pushedByRoute = lastRoute?.pushedByRoute;
} else if (routeInfo.routeAction === 'replace') {
// Make sure to set the lastPathname, etc.. to the current route so the page transitions out
const currentRouteInfo = this.locationHistory.current();
const currentRouteInfo = locationHistory.current.current();
/**
* If going from /home to /child, then replacing from
@@ -198,58 +144,79 @@ class IonRouterInner extends React.PureComponent<IonRouteProps, IonRouteState> {
routeInfo.routeAnimation = routeInfo.routeAnimation || currentRouteInfo?.routeAnimation;
}
this.locationHistory.add(routeInfo);
locationHistory.current.add(routeInfo);
}
this.setState({
routeInfo,
});
setRouteInfo(routeInfo);
}
this.incomingRouteParams = undefined;
}
incomingRouteParams.current = null;
};
/**
* history@4.x uses goBack(), history@5.x uses back()
* TODO: If support for React Router <=5 is dropped
* this logic is no longer needed. We can just
* assume back() is available.
*/
handleNativeBack() {
const history = this.props.history as any;
const goBack = history.goBack || history.back;
goBack();
}
const handleResetTab = (tab: string, originalHref: string, originalRouteOptions: any) => {
const routeInfo = locationHistory.current.getFirstRouteInfoForTab(tab);
if (routeInfo) {
const newRouteInfo = { ...routeInfo };
newRouteInfo.pathname = originalHref;
newRouteInfo.routeOptions = originalRouteOptions;
incomingRouteParams.current = { ...newRouteInfo, routeAction: 'pop', routeDirection: 'back' };
navigate(newRouteInfo.pathname + (newRouteInfo.search || ''));
}
};
handleNavigate(
path: string,
routeAction: RouteAction,
routeDirection?: RouterDirection,
routeAnimation?: AnimationBuilder,
routeOptions?: any,
tab?: string
) {
this.incomingRouteParams = Object.assign(this.incomingRouteParams || {}, {
routeAction,
routeDirection,
routeOptions,
routeAnimation,
tab,
});
const handleChangeTab = (tab: string, path?: string, routeOptions?: any) => {
if (!path) {
return;
}
if (routeAction === 'push') {
this.props.history.push(path);
const routeInfo = locationHistory.current.getCurrentRouteInfoForTab(tab);
const [pathname, search] = path.split('?');
if (routeInfo) {
const routeParams = {
...routeInfo,
routeAction: 'push' as RouteAction,
routeDirection: 'none' as RouterDirection,
};
if (routeInfo.pathname === pathname) {
incomingRouteParams.current = {
...routeParams,
routeOptions,
};
navigate(routeInfo.pathname + (routeInfo.search || ''));
} else {
incomingRouteParams.current = {
...routeParams,
pathname,
search: search ? '?' + search : undefined,
routeOptions,
};
navigate(pathname + (search ? '?' + search : ''));
}
} else {
this.props.history.replace(path);
handleNavigate(pathname, 'push', 'none', undefined, routeOptions, tab);
}
}
};
handleNavigateBack(defaultHref: string | RouteInfo = '/', routeAnimation?: AnimationBuilder) {
const handleSetCurrentTab = (tab: string) => {
currentTab.current = tab;
const ri = { ...locationHistory.current.current() };
if (ri.tab !== tab) {
ri.tab = tab;
locationHistory.current.update(ri);
}
};
const handleNativeBack = () => {
navigate(-1);
};
const handleNavigateBack = (defaultHref: string | RouteInfo = '/', routeAnimation?: AnimationBuilder) => {
const config = getConfig();
defaultHref = defaultHref ? defaultHref : config && config.get('backButtonDefaultHref' as any);
const routeInfo = this.locationHistory.current();
const routeInfo = locationHistory.current.current();
if (routeInfo && routeInfo.pushedByRoute) {
const prevInfo = this.locationHistory.findLastLocation(routeInfo);
const prevInfo = locationHistory.current.findLastLocation(routeInfo);
if (prevInfo) {
/**
* This needs to be passed to handleNavigate
@@ -257,7 +224,7 @@ class IonRouterInner extends React.PureComponent<IonRouteProps, IonRouteState> {
* will be overridden.
*/
const incomingAnimation = routeAnimation || routeInfo.routeAnimation;
this.incomingRouteParams = {
incomingRouteParams.current = {
...prevInfo,
routeAction: 'pop',
routeDirection: 'back',
@@ -273,68 +240,75 @@ class IonRouterInner extends React.PureComponent<IonRouteProps, IonRouteState> {
*/
(prevInfo.pathname === routeInfo.pushedByRoute && routeInfo.tab === '' && prevInfo.tab === '')
) {
/**
* history@4.x uses goBack(), history@5.x uses back()
* TODO: If support for React Router <=5 is dropped
* this logic is no longer needed. We can just
* assume back() is available.
*/
const history = this.props.history as any;
const goBack = history.goBack || history.back;
goBack();
navigate(-1);
} else {
this.handleNavigate(prevInfo.pathname + (prevInfo.search || ''), 'pop', 'back', incomingAnimation);
handleNavigate(prevInfo.pathname + (prevInfo.search || ''), 'pop', 'back', incomingAnimation);
}
} else {
this.handleNavigate(defaultHref as string, 'pop', 'back', routeAnimation);
handleNavigate(defaultHref as string, 'pop', 'back', routeAnimation);
}
} else {
this.handleNavigate(defaultHref as string, 'pop', 'back', routeAnimation);
handleNavigate(defaultHref as string, 'pop', 'back', routeAnimation);
}
};
const handleNavigate = (
path: string,
routeAction: RouteAction,
routeDirection?: RouterDirection,
routeAnimation?: AnimationBuilder,
routeOptions?: any,
tab?: string
) => {
incomingRouteParams.current = Object.assign(incomingRouteParams.current || {}, {
routeAction,
routeDirection,
routeOptions,
routeAnimation,
tab,
});
navigate(path, { replace: routeAction !== 'push' });
};
if (!didMountRef.current) {
locationHistory.current.add(routeInfo);
registerHistoryListener(handleHistoryChange);
}
handleResetTab(tab: string, originalHref: string, originalRouteOptions: any) {
const routeInfo = this.locationHistory.getFirstRouteInfoForTab(tab);
if (routeInfo) {
const newRouteInfo = { ...routeInfo };
newRouteInfo.pathname = originalHref;
newRouteInfo.routeOptions = originalRouteOptions;
this.incomingRouteParams = { ...newRouteInfo, routeAction: 'pop', routeDirection: 'back' };
this.props.history.push(newRouteInfo.pathname + (newRouteInfo.search || ''));
}
}
const routeMangerContextValue: RouteManagerContextState = {
canGoBack: () => locationHistory.current.canGoBack(),
clearOutlet: viewStack.current.clear,
findViewItemByPathname: viewStack.current.findViewItemByPathname,
getChildrenToRender: viewStack.current.getChildrenToRender,
goBack: () => handleNavigateBack(),
createViewItem: viewStack.current.createViewItem,
findViewItemByRouteInfo: viewStack.current.findViewItemByRouteInfo,
findLeavingViewItemByRouteInfo: viewStack.current.findLeavingViewItemByRouteInfo,
addViewItem: viewStack.current.add,
unMountViewItem: viewStack.current.remove,
};
handleSetCurrentTab(tab: string) {
this.currentTab = tab;
const ri = { ...this.locationHistory.current() };
if (ri.tab !== tab) {
ri.tab = tab;
this.locationHistory.update(ri);
}
}
return (
<RouteManagerContext.Provider value={routeMangerContextValue}>
<NavManager
ionRoute={IonRouteInner}
ionRedirect={{}}
stackManager={StackManager}
routeInfo={routeInfo}
onNativeBack={handleNativeBack}
onNavigateBack={handleNavigateBack}
onNavigate={handleNavigate}
onSetCurrentTab={handleSetCurrentTab}
onChangeTab={handleChangeTab}
onResetTab={handleResetTab}
locationHistory={locationHistory.current}
>
{children}
</NavManager>
</RouteManagerContext.Provider>
);
};
render() {
return (
<RouteManagerContext.Provider value={this.routeMangerContextState}>
<NavManager
ionRoute={IonRouteInner}
ionRedirect={{}}
stackManager={StackManager}
routeInfo={this.state.routeInfo!}
onNativeBack={this.handleNativeBack}
onNavigateBack={this.handleNavigateBack}
onNavigate={this.handleNavigate}
onSetCurrentTab={this.handleSetCurrentTab}
onChangeTab={this.handleChangeTab}
onResetTab={this.handleResetTab}
locationHistory={this.locationHistory}
>
{this.props.children}
</NavManager>
</RouteManagerContext.Provider>
);
}
}
export const IonRouter = withRouter(IonRouterInner);
IonRouter.displayName = 'IonRouter';

View File

@@ -1,7 +1,9 @@
import type { RouteInfo, ViewItem } from '@ionic/react';
import { IonRoute, ViewLifeCycleManager, ViewStacks, generateId } from '@ionic/react';
import React from 'react';
import { Route } from 'react-router';
import { findRoutesNode } from './utils/findRoutesNode';
import { matchPath } from './utils/matchPath';
export class ReactRouterViewStack extends ViewStacks {
@@ -44,42 +46,32 @@ export class ReactRouterViewStack extends ViewStacks {
const viewItems = this.getViewItemsForOutlet(outletId);
// Sync latest routes with viewItems
React.Children.forEach(ionRouterOutlet.props.children, (child: React.ReactElement) => {
const viewItem = viewItems.find((v) => {
return matchComponent(child, v.routeData.childProps.path || v.routeData.childProps.from);
});
if (viewItem) {
viewItem.reactElement = child;
React.Children.forEach(findRoutesNode(ionRouterOutlet.props.children), (child: React.ReactElement) => {
if (child.type === Route) {
const viewItem = viewItems.find((v) => {
return matchComponent(child, v.routeData.childProps.path || v.routeData.childProps.from);
});
if (viewItem) {
viewItem.reactElement = child;
}
}
});
const children = viewItems.map((viewItem) => {
let clonedChild;
if (viewItem.ionRoute && !viewItem.disableIonPageManagement) {
clonedChild = (
<ViewLifeCycleManager
key={`view-${viewItem.id}`}
mount={viewItem.mount}
removeView={() => this.remove(viewItem)}
>
{React.cloneElement(viewItem.reactElement, {
computedMatch: viewItem.routeData.match,
})}
</ViewLifeCycleManager>
);
} else {
const clonedChild = (
<ViewLifeCycleManager
key={`view-${viewItem.id}`}
mount={viewItem.mount}
removeView={() => this.remove(viewItem)}
>
{React.cloneElement(viewItem.reactElement, {
computedMatch: viewItem.routeData.match,
})}
</ViewLifeCycleManager>
);
if (!viewItem.ionRoute || viewItem.disableIonPageManagement) {
const match = matchComponent(viewItem.reactElement, routeInfo.pathname);
clonedChild = (
<ViewLifeCycleManager
key={`view-${viewItem.id}`}
mount={viewItem.mount}
removeView={() => this.remove(viewItem)}
>
{React.cloneElement(viewItem.reactElement, {
computedMatch: viewItem.routeData.match,
})}
</ViewLifeCycleManager>
);
if (!match && viewItem.routeData.match) {
viewItem.routeData.match = undefined;
@@ -152,8 +144,9 @@ export class ReactRouterViewStack extends ViewStacks {
*
* To validate this, we need to check if the path and url match the view item's route data.
*/
const hasParameter = match.path.includes(':');
if (!hasParameter || (hasParameter && match.url === v.routeData?.match?.url)) {
// changed from match.path
const hasParameter = match.params && Object.keys(match.params).length > 0;
if (!hasParameter || (hasParameter && match.pathname === v.routeData?.match?.url)) {
viewItem = v;
return true;
}
@@ -165,11 +158,11 @@ export class ReactRouterViewStack extends ViewStacks {
// try to find a route that doesn't have a path or from prop, that will be our default route
if (!v.routeData.childProps.path && !v.routeData.childProps.from) {
match = {
path: pathname,
url: pathname,
isExact: true,
pathname: pathname,
// url: pathname,
// isExact: true,
params: {},
};
} as any;
viewItem = v;
return true;
}

View File

@@ -1,8 +1,10 @@
import type { RouteInfo, StackContextState, ViewItem } from '@ionic/react';
import { RouteManagerContext, StackContext, generateId, getConfig } from '@ionic/react';
import React from 'react';
import { Route } from 'react-router';
import { clonePageElement } from './clonePageElement';
import { findRoutesNode } from './utils/findRoutesNode';
import { matchPath } from './utils/matchPath';
// TODO(FW-2959): types
@@ -110,12 +112,17 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
}
}
const enteringRoute = matchRoute(this.ionRouterOutlet?.props.children, routeInfo) as React.ReactElement;
const enteringRoute = findRouteByRouteInfo(this.ionRouterOutlet?.props.children, routeInfo) as React.ReactElement;
if (enteringRoute === undefined) {
console.warn('Could not match a route for', routeInfo, this.ionRouterOutlet?.props.children);
return;
}
if (enteringViewItem) {
enteringViewItem.reactElement = enteringRoute;
enteringViewItem.reactElement = enteringRoute.props.element;
} else if (enteringRoute) {
enteringViewItem = this.context.createViewItem(this.id, enteringRoute, routeInfo);
enteringViewItem = this.context.createViewItem(this.id, enteringRoute.props.element, routeInfo);
this.context.addViewItem(enteringViewItem);
}
@@ -133,7 +140,7 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
* If the route data does not match the current path, the parent router outlet
* is attempting to transition and we cancel the operation.
*/
if (enteringViewItem.routeData.match.url !== routeInfo.pathname) {
if (enteringViewItem.routeData.match.pathname !== routeInfo.pathname) {
return;
}
}
@@ -430,30 +437,38 @@ export class StackManager extends React.PureComponent<StackManagerProps, StackMa
export default StackManager;
function matchRoute(node: React.ReactNode, routeInfo: RouteInfo) {
/**
* Finds the <Route /> node matching the current route info.
* If no <Route /> can be matched, a fallback node is returned.
*/
function findRouteByRouteInfo(node: React.ReactNode, routeInfo: RouteInfo) {
let matchedNode: React.ReactNode;
React.Children.forEach(node as React.ReactElement, (child: React.ReactElement) => {
const match = matchPath({
pathname: routeInfo.pathname,
componentProps: child.props,
});
if (match) {
matchedNode = child;
let fallbackNode: React.ReactNode;
// <Route /> nodes are rendered inside of a <Routes /> node
const routesNode = findRoutesNode(node) ?? node;
React.Children.forEach(routesNode, (child: React.ReactElement) => {
// Ignore any non-<Route /> nodes
if (child.type === Route) {
const match = matchPath({
pathname: routeInfo.pathname,
componentProps: child.props,
});
if (match) {
matchedNode = child;
}
if (!(child.props.path || child.props.from)) {
// If we haven't found a node
// try to find one that doesn't have a path or from prop, that will be our not found route
fallbackNode = child;
}
}
});
if (matchedNode) {
return matchedNode;
}
// If we haven't found a node
// try to find one that doesn't have a path or from prop, that will be our not found route
React.Children.forEach(node as React.ReactElement, (child: React.ReactElement) => {
if (!(child.props.path || child.props.from)) {
matchedNode = child;
}
});
return matchedNode;
return matchedNode ?? fallbackNode;
}
function matchComponent(node: React.ReactElement, pathname: string, forceExact?: boolean) {

View File

@@ -0,0 +1,16 @@
import React from "react";
import { Routes } from "react-router";
export const findRoutesNode = (node: React.ReactNode) => {
// Finds the <Routes /> component node
let routesNode: React.ReactNode;
React.Children.forEach(node as React.ReactElement, (child: React.ReactElement) => {
if (child.type === Routes) {
routesNode = child;
}
});
if (routesNode) {
return (routesNode as React.ReactElement).props.children;
}
return undefined;
};

View File

@@ -21,23 +21,23 @@ interface MatchPathOptions {
*/
export const matchPath = ({
pathname,
componentProps,
componentProps
}: MatchPathOptions): false | ReturnType<typeof reactRouterMatchPath> => {
const { exact, component } = componentProps;
const { exact: caseSensitive, path, from } = componentProps;
const path = componentProps.path || componentProps.from;
/***
* The props to match against, they are identical
* to the matching props `Route` accepts. It could also be a string
* or an array of strings as shortcut for `{ path }`.
*/
const matchProps = {
exact,
path,
component,
};
if (!pathname) {
console.warn('Attempt to match path on an undefined pathname', {
path: componentProps.path,
from: componentProps.from,
});
// TODO @sean this is new behavior, need to debug where it is coming from
return false;
}
const match = reactRouterMatchPath(pathname, matchProps);
const match = reactRouterMatchPath({
path: path ?? from ?? '',
caseSensitive
}, pathname);
if (!match) {
return false;